def generate_traffic(self, activity, legend_keys, report_type): """ Generate traffic data during the time the user was logged-in. """ cache = {} combined_activity = [] for event in activity: host = event[0] timefilter = TimeFilter(string_to_datetime(event[1]), string_to_datetime(event[2])) if self.options.usecache and report_type == 'timeseries': # check cache - only consider a hit when whole time period is covered minutes = timefilter.profiler_minutes(astimestamp=True) if host in cache and all(t in cache[host] for t in minutes): data = [cache[host][t] for t in minutes] else: legend, data = self.traffic_report(host, timefilter, report_type) # store results in cache by host->times->data cache.setdefault(host, {}).update((int(x[0]), x) for x in data) else: legend, data = self.traffic_report(host, timefilter, report_type) if data: if self.options.aggregate and report_type == 'timeseries': # generate running averages over data samples received # first convert empty strings to zeros, then run averages columns = map(lambda c: [0 if x == '' else x for x in c], itertools.izip(*data)) aggmap = [x[1] for x in TCOLUMNS] aggregates = [aggmap[i](x) for i, x in enumerate(columns)] combined_activity.append(list(event) + aggregates) elif report_type == 'timeseries' or report_type == 'summary': # create entry for each element in report for row in data: r = ['--' if x == '' else x for x in row] combined_activity.append(list(event) + r) else: raise RuntimeError('unknown report type: %s' % report_type) else: # populate result with blanks combined_activity.append(list(event) + ['--'] * len(legend)) traffic_legend = [c.key for c in legend] legend = legend_keys + traffic_legend return legend, combined_activity
def test_resolution(self): groupby = self.profiler.groupbys.host columns = [self.profiler.columns.key.host_ip, self.profiler.columns.value.avg_bytes, self.profiler.columns.value.avg_pkts] sort_col = self.profiler.columns.value.avg_bytes trafficexpr = TrafficFilter("host 10/8") resolutions = [["1min", "last 5 min"], ["15min", "last 1 hour"], ["hour", "last 4 hours"], ["6hour", "last 1 day"], ["day", "last 1 week"], ["3600", "last 4 hours"], # hour resolution ["60", "last 5 min"], # minute resolution #["week", "last 4 weeks"], #["month", "last 12 months"], #Commented values blow up with a #E RvbdHTTPException: 400 Unknown time resolution. ] for (resolution, duration) in resolutions: timerange = TimeFilter.parse_range(duration) with TrafficSummaryReport(self.profiler) as rep: rep.run(groupby, columns, sort_col, timerange, trafficexpr, resolution=resolution)
def main(self): # groupby validation should be part of validate_args, but there # is no Profiler initialized at that part of the initialization try: self.groupby = self.profiler.groupbys[self.options.groupby] except KeyError: if self.options.groupby not in self.profiler.groupbys.values(): self.optparse.error('Invalid groupby chosen.') else: self.groupby = self.options.groupby self.timefilter = TimeFilter.parse_range(self.options.timefilter) if self.options.trafficexpr: self.trafficexpr = TrafficFilter(self.options.trafficexpr) else: self.trafficexpr = None with TrafficSummaryReport(self.profiler) as report: report.run(columns=self.options.columns.split(','), groupby=self.groupby, sort_col=self.options.sortby, timefilter=self.timefilter, trafficexpr=self.trafficexpr, centricity=self.centricity, resolution='auto') data = report.get_data() legend = [c.label for c in report.get_legend()] self.print_data(data, legend)
def main(self): """ Setup query and run report with default column set """ if self.options.timerange: timefilter = TimeFilter.parse_range(self.options.timerange) else: timefilter = TimeFilter(self.options.time0, self.options.time1) trafficexpr = TrafficFilter(self.options.trafficexpr) columns = [self.profiler.columns.key.srv_host_ip, self.profiler.columns.key.app_info, self.profiler.columns.key.start_time, self.profiler.columns.key.end_time, self.profiler.columns.value.s2c_total_bytes, self.profiler.columns.value.s2c_total_pkts, self.profiler.columns.value.response_time, self.profiler.columns.value.server_delay] report = TrafficFlowListReport(self.profiler) report.run(columns, timefilter=timefilter, trafficexpr=trafficexpr) data = report.get_data() report.delete() headers = [c.key for c in columns] Formatter.print_table(data, headers)
def main(self): """ Setup query and run report with default column set """ if self.options.timerange: timefilter = TimeFilter.parse_range(self.options.timerange) else: timefilter = TimeFilter(self.options.time0, self.options.time1) if self.options.trafficexpr: trafficexpr = TrafficFilter(self.options.trafficexpr) else: trafficexpr = None legend_columns, all_data = self.identity_report(timefilter=timefilter, trafficexpr=trafficexpr, testfile=self.options.testfile) legend, activity = self.analyze_login_data(all_data, legend_columns) if self.options.timeseries_report: headers, tbl_data = self.generate_traffic(activity, legend, 'timeseries') elif self.options.summary_report: headers, tbl_data = self.generate_traffic(activity, legend, 'summary') else: headers = ('Host IP', 'Login Time', 'Logout Time', 'Duration') tbl_data = [(x[0], format_time(x[1]), format_time(x[2]), x[3]) for x in activity] if self.options.csv: Formatter.print_csv(tbl_data, headers) elif self.options.tsv: Formatter.print_csv(tbl_data, headers, delim='\t') else: Formatter.print_table(tbl_data, headers)
def test_traffic_summary_report(self): groupby = self.profiler.groupbys.host columns = [self.profiler.columns.key.host_ip, self.profiler.columns.value.avg_bytes, self.profiler.columns.value.avg_pkts] sort_col = self.profiler.columns.value.avg_bytes timerange = TimeFilter.parse_range("last 1 h") trafficexpr = TrafficFilter("host 10/8") with TrafficSummaryReport(self.profiler) as rep: rep.run(groupby, columns, sort_col, timerange, trafficexpr) legend = rep.get_legend() self.assertEqual(len(legend), 3) legend = rep.get_legend(columns=[self.profiler.columns.key.host_ip, self.profiler.columns.value.avg_bytes]) self.assertEqual(len(legend), 2) self.assertEqual(legend[0].key, 'host_ip') self.assertEqual(legend[1].key, 'avg_bytes') data = rep.get_data() if data: self.assertEqual(len(data[0]), 3) #check that data is refetched from cache data = rep.get_data() data = rep.get_data(columns=[self.profiler.columns.key.host_ip, self.profiler.columns.value.avg_bytes]) if data: self.assertEqual(len(data[0]), 2)
def test_timefilter(self): tfilter = TimeFilter.parse_range('9:01:36 to 10:04:39') testtime = tfilter.start.replace(minute=33, second=59) self.assertTrue(tfilter.compare_time(testtime)) testtime = tfilter.end.replace(minute=44) self.assertFalse(tfilter.compare_time(testtime)) minutes = tfilter.profiler_minutes() self.assertEqual(len(minutes), 64) minutes = tfilter.profiler_minutes(astimestamp=True) self.assertEqual(len(minutes), 64) minutes = tfilter.profiler_minutes(aslocal=True) self.assertEqual(len(minutes), 64) tfilter = TimeFilter.parse_range('9:01:36 to 9:02:33') minutes = tfilter.profiler_minutes() self.assertEqual(len(minutes), 1)
def test_identity_report(self): timerange = TimeFilter.parse_range('last 30 m') with IdentityReport(self.profiler) as report: report.run(timefilter=timerange) legend = report.get_legend() data = report.get_data() keys = [c.key for c in legend] self.assertTrue('time' in keys) self.assertTrue('username' in keys) if data: self.assertEqual(len(data[0]), 9)
def test_report_with_area(self): groupby = self.profiler.groupbys.host columns = [self.profiler.columns.key.host_ip, self.profiler.columns.value.avg_bytes, self.profiler.columns.value.avg_pkts] sort_col = self.profiler.columns.value.avg_bytes timerange = TimeFilter.parse_range("last 1 h") trafficexpr = TrafficFilter("host 10/8") area = self.profiler.areas.vxlan_tenant with TrafficSummaryReport(self.profiler) as rep: rep.run(groupby, columns, sort_col, timerange, trafficexpr, area=area)
def mapdata(request): report = p.create_traffic_summary_report( groupby=p.groupbys.host_pair, columns=[p.columns.key.cli_host_ip, p.columns.key.srv_host_ip, p.columns.value.avg_bytes], sort_col=p.columns.value.avg_bytes, timefilter=TimeFilter.parse_range("last 5 m"), trafficexpr=TrafficFilter("host 10/8"), ) rdata = report.get_data() print "rdata: %s" % rdata data = [] for row in rdata[:-10]: data.append({"x0": row[0], "x1": row[1], "x2": str(row[2])}) return HttpResponse(json.dumps(data))
def main(self): self.timefilter = TimeFilter.parse_range(self.options.timefilter) if self.options.trafficexpr: self.trafficexpr = TrafficFilter(self.options.trafficexpr) else: self.trafficexpr = None with TrafficFlowListReport(self.profiler) as report: report.run(columns=self.options.columns.split(','), sort_col=self.options.sortby, timefilter=self.timefilter, trafficexpr=self.trafficexpr) data = report.get_data() legend = [c.label for c in report.get_legend()] self.print_data(data, legend)
def main(self): self.timefilter = TimeFilter.parse_range(self.options.timefilter) if self.options.trafficexpr: self.trafficexpr = TrafficFilter(self.options.trafficexpr) else: self.trafficexpr = None with TrafficOverallTimeSeriesReport(self.profiler) as report: report.run(columns=self.options.columns.split(','), timefilter=self.timefilter, trafficexpr=self.trafficexpr, centricity=self.centricity) data = report.get_data() legend = [c.label for c in report.get_legend()] self.print_data(data, legend)
def test_unsupported_column(self): groupby = self.profiler.groupbys.port # host_ip shouldn't be included as part of 'port' groupby columns = [self.profiler.columns.key.host_ip, self.profiler.columns.value.avg_bytes, self.profiler.columns.value.avg_pkts] sort_col = self.profiler.columns.value.avg_bytes timerange = TimeFilter.parse_range("last 1 h") trafficexpr = TrafficFilter("host 10/8") report = TrafficSummaryReport(self.profiler) kwds = dict(groupby=groupby, columns=columns, sort_col=sort_col, timefilter=timerange, trafficexpr=trafficexpr) self.assertRaises(RvbdException, report.run, None, kwds)
def main(app): # Create and run a traffic summary report of all server ports in use # by hosts in 10/8 report = TrafficOverallTimeSeriesReport(app.profiler) # Run the report report.run( columns=[ app.profiler.columns.key.time, app.profiler.columns.value.avg_bytes, app.profiler.columns.value.network_rtt, ], timefilter=TimeFilter.parse_range("last 15 m"), trafficexpr=TrafficFilter("host 10/8"), ) # Retrieve and print data data = report.get_data() printer = pprint.PrettyPrinter(2) printer.pprint(data)
def main(app): # Create and run a traffic summary report of all server ports in use # by hosts in 10/8 report = TrafficSummaryReport(app.profiler) # Run the report report.run( groupby = app.profiler.groupbys.port, columns = [app.profiler.columns.key.protoport, app.profiler.columns.key.protoport_name, app.profiler.columns.value.avg_bytes, app.profiler.columns.value.network_rtt], sort_col = app.profiler.columns.value.avg_bytes, timefilter = TimeFilter.parse_range("last 15 m"), trafficexpr = TrafficFilter("host 10/8") ) # Retrieve and print data data = report.get_data() printer = pprint.PrettyPrinter(2) printer.pprint(data[:20])
def data(request): report = p.create_traffic_overall_time_series_report( columns=[p.columns.key.time, p.columns.value.avg_bytes, p.columns.value.network_rtt], sort_col=p.columns.key.time, timefilter=TimeFilter.parse_range("last 15 m"), trafficexpr=TrafficFilter("host 10/8"), ) rdata = report.get_data() print "rdata: %s" % rdata s1 = [] for row in rdata: s1.append({"x": int(row[0] - rdata[0][0]), "y": int(row[1] / 1000000)}) print "s1: %s" % s1 data = { "xaxis": "Time", "yaxis": "BW", "width": "600px", "height": "300px", "data": [{"values": s1, "key": "Data 1", "color": "#ff7f0e"}], } return HttpResponse(json.dumps(data))
def get_interfaces(self, device_ip): """ Query profiler to attempt to automatically determine LAN and WAN interface ids. """ cols = self.profiler.get_columns(['interface_dns', 'interface']) super(WANReport, self).run(realm='traffic_summary', groupby='ifc', columns=cols, timefilter=TimeFilter.parse_range('last 1 h'), trafficexpr=TrafficFilter('device %s' % device_ip), centricity='int', resolution='auto', sync=True) interfaces = self._get_data() lan = [address for name, address in interfaces if 'lan' in name] wan = [address for name, address in interfaces if 'wan' in name] if not lan or not wan: raise RvbdException('Unable to determine LAN and WAN interfaces for device %s' % device_ip) return lan, wan
def test_traffic_flow_list_report(self): columns = [self.profiler.columns.key.srv_host_ip, self.profiler.columns.key.app_info, self.profiler.columns.key.start_time, self.profiler.columns.key.end_time, self.profiler.columns.value.s2c_total_bytes, self.profiler.columns.value.s2c_total_pkts, self.profiler.columns.value.response_time, self.profiler.columns.value.server_delay] timerange = TimeFilter.parse_range("last 1 h") trafficexpr = TrafficFilter("host 10/8") with TrafficFlowListReport(self.profiler) as report: report.run(columns, timefilter=timerange, trafficexpr=trafficexpr) legend = report.get_legend() keys = [c.key for c in legend] self.assertTrue('app_info' in keys) data = report.get_data() if data: self.assertEqual(len(data[0]), 8)
def test_traffic_overall_time_series_report(self): columns = [self.profiler.columns.key.time, self.profiler.columns.value.avg_bytes, self.profiler.columns.value.avg_pkts] timerange = TimeFilter.parse_range("last 1 h") trafficexpr = TrafficFilter("host 10/8") resolution = "15min" report = TrafficOverallTimeSeriesReport(self.profiler) report.run(columns, timerange, trafficexpr, resolution=resolution) legend = report.get_legend() keys = [c.key for c in legend] data = report.get_data() for item in data: d = dict(zip(keys, item)) # resolution assumes 15-minute responses self.assertTrue(timerange.compare_time(d['time'], resolution=15*60)) report.delete()
def get_interfaces(self, device_ip): """ Query profiler to attempt to automatically determine LAN and WAN interface ids. """ cols = self.profiler.get_columns(["interface_dns", "interface"]) super(WANReport, self).run( realm="traffic_summary", groupby="ifc", columns=cols, timefilter=TimeFilter.parse_range("last 1 h"), trafficexpr=TrafficFilter("device %s" % device_ip), centricity="int", resolution="auto", sync=True, ) interfaces = self._get_data() lan = [address for name, address in interfaces if "lan" in name] wan = [address for name, address in interfaces if "wan" in name] if not lan or not wan: raise RvbdException("Unable to determine LAN and WAN interfaces for device %s" % device_ip) return lan, wan
# connection information username = '******' password = '******' host = '<profiler.ip.address>' if (username == '<username>' or password == '<password>' or host == '<profiler.ip.address>'): print "Update the username, password, and profiler host values before running this script." sys.exit(0) auth = UserAuth(username, password) p = rvbd.profiler.Profiler(host, auth=auth) report = TrafficSummaryReport(p) columns = [p.columns.key.host_ip, p.columns.value.avg_bytes, p.columns.value.network_rtt] sort_column = p.columns.value.avg_bytes timefilter = TimeFilter.parse_range("last 15 m") report.run('hos', columns, timefilter=timefilter, sort_col=sort_column) data = report.get_data() legend = report.get_legend() report.delete() pprint.pprint(data[:10])
listInterfaceGroups = app.options.listInterfaceGroups listHostGroupTypes = app.options.listHostGroupTypes host = app.host trafficExpressionHelp = app.options.trafficExpressionHelp percentileVal = int(app.options.percentile) outputData = app.options.outputData sshPassword = app.options.sshPassword sshUsername = app.options.sshUsername sumTime = app.options.sumTime if not is_int(percentileVal) or percentileVal < 1 or percentileVal > 100: print "Percentile MUST be an integer between 1 and 100" exit(1) # Validate the time filter theTimeFilter=TimeFilter.parse_range(timeFilter) def checkSet(value, label): if value is None: print "'%s' must be passed for this command" % label exit(1) # Process every index on the system if listInterfaceGroups == True or listHostGroupTypes == True or trafficExpressionHelp: # List the interface or host groups if listHostGroupTypes == True: checkSet(sshUsername, "sshUsername") checkSet(sshPassword, "sshPassword") listGroupTypes(host, sshUsername, sshPassword) exit() elif listInterfaceGroups == True:
def run(self): """ Main execution method """ criteria = self.job.criteria if criteria.profiler_device == '': logger.debug('%s: No profiler device selected' % self.table) self.job.mark_error("No Profiler Device Selected") return False #self.fake_run() #return True profiler = DeviceManager.get_device(criteria.profiler_device) report = rvbd.profiler.report.SingleQueryReport(profiler) columns = [col.name for col in self.table.get_columns(synthetic=False)] sortcol = None if self.table.sortcol is not None: sortcol = self.table.sortcol.name tf = TimeFilter(start=criteria.starttime, end=criteria.endtime) logger.info("Running Profiler table %d report for timeframe %s" % (self.table.id, str(tf))) if ('datafilter' in criteria) and (criteria.datafilter is not None): datafilter = criteria.datafilter.split(',') else: datafilter = None trafficexpr = TrafficFilter( self.job.combine_filterexprs(exprs=criteria.profiler_filterexpr) ) # Incoming criteria.resolution is a timedelta logger.debug('Profiler report got criteria resolution %s (%s)' % (criteria.resolution, type(criteria.resolution))) if criteria.resolution != 'auto': rsecs = int(timedelta_total_seconds(criteria.resolution)) resolution = rvbd.profiler.report.Report.RESOLUTION_MAP[rsecs] else: resolution = 'auto' logger.debug('Profiler report using resolution %s (%s)' % (resolution, type(resolution))) with lock: report.run(realm=self.table.options.realm, groupby=profiler.groupbys[self.table.options.groupby], centricity=self.table.options.centricity, columns=columns, timefilter=tf, trafficexpr=trafficexpr, data_filter=datafilter, resolution=resolution, sort_col=sortcol, sync=False ) done = False logger.info("Waiting for report to complete") while not done: time.sleep(0.5) with lock: s = report.status() self.job.safe_update(progress=int(s['percent'])) done = (s['status'] == 'completed') # Retrieve the data with lock: query = report.get_query_by_index(0) self.data = query.get_data() tz = criteria.starttime.tzinfo # Update criteria criteria.starttime = (datetime.datetime .utcfromtimestamp(query.actual_t0) .replace(tzinfo=tz)) criteria.endtime = (datetime.datetime .utcfromtimestamp(query.actual_t1) .replace(tzinfo=tz)) self.job.safe_update(actual_criteria=criteria) if self.table.rows > 0: self.data = self.data[:self.table.rows] logger.info("Report %s returned %s rows" % (self.job, len(self.data))) return True
def run(self): """ Main execution method. """ criteria = self.job.criteria if criteria.profiler_device == '': logger.debug('%s: No profiler device selected' % self.table) self.job.mark_error("No Profiler Device Selected") return False profiler = DeviceManager.get_device(criteria.profiler_device) report = rvbd.profiler.report.MultiQueryReport(profiler) tf = TimeFilter(start=criteria.starttime, end=criteria.endtime) logger.info("Running ProfilerTemplateTable table %d report " "for timeframe %s" % (self.table.id, str(tf))) trafficexpr = TrafficFilter( self.job.combine_filterexprs(exprs=criteria.profiler_filterexpr) ) # Incoming criteria.resolution is a timedelta logger.debug('Profiler report got criteria resolution %s (%s)' % (criteria.resolution, type(criteria.resolution))) if criteria.resolution != 'auto': rsecs = int(timedelta_total_seconds(criteria.resolution)) resolution = rvbd.profiler.report.Report.RESOLUTION_MAP[rsecs] else: resolution = 'auto' logger.debug('Profiler report using resolution %s (%s)' % (resolution, type(resolution))) with lock: res = report.run(template_id=self.table.options.template_id, timefilter=tf, trafficexpr=trafficexpr, resolution=resolution) if res is True: logger.info("Report template complete.") self.job.safe_update(progress=100) # Retrieve the data with lock: query = report.get_query_by_index(0) data = query.get_data() headers = report.get_legend() tz = criteria.starttime.tzinfo # Update criteria criteria.starttime = (datetime.datetime .utcfromtimestamp(query.actual_t0) .replace(tzinfo=tz)) criteria.endtime = (datetime.datetime .utcfromtimestamp(query.actual_t1) .replace(tzinfo=tz)) self.job.safe_update(actual_criteria=criteria) # create dataframe with all of the default headers df = pandas.DataFrame(data, columns=[h.key for h in headers]) # now filter down to the columns requested by the table columns = [col.name for col in self.table.get_columns(synthetic=False)] self.data = df[columns] if self.table.sortcol is not None: self.data = self.data.sort(self.table.sortcol.name) if self.table.rows > 0: self.data = self.data[:self.table.rows] logger.info("Report %s returned %s rows" % (self.job, len(self.data))) return True
def run( self, template_id, timefilter=None, resolution="auto", query=None, trafficexpr=None, data_filter=None, sync=True ): """Create the report on Profiler and begin running the report. If the `sync` option is True, periodically poll until the report is complete, otherwise return immediately. `template_id` is the numeric id of the template to use for the report `timefilter` is the range of time to query, a TimeFilter object `resolution` is the data resolution (1min, 15min, etc.), defaults to 'auto' `query` is the query object containing criteria `trafficexpr` is a TrafficFilter object `data_filter` is a deprecated filter to run against report data `sync` if True, poll for status until the report is complete """ self.template_id = template_id self.custom_columns = False if self.template_id != 184: # the columns in this report won't match, use custom columns instead self.custom_columns = True if timefilter is None: self.timefilter = TimeFilter.parse_range("last 5 min") else: self.timefilter = timefilter self.query = query self.trafficexpr = trafficexpr self.data_filter = data_filter self.id = None self.queries = list() self.last_status = None if resolution not in ["auto", "1min", "15min", "hour", "6hour", "day", "week", "month"]: rd = parse_timedelta(resolution) resolution = self.RESOLUTION_MAP[int(timedelta_total_seconds(rd))] self.resolution = resolution start = datetime_to_seconds(self.timefilter.start) end = datetime_to_seconds(self.timefilter.end) # using a RecursiveUpdateDict criteria = RecursiveUpdateDict(**{"time_frame": {"start": int(start), "end": int(end)}}) if self.query is not None: criteria["query"] = self.query if self.resolution != "auto": criteria["time_frame"]["resolution"] = self.resolution if self.data_filter: criteria["deprecated"] = {self.data_filter[0]: self.data_filter[1]} if self.trafficexpr is not None: criteria["traffic_expression"] = self.trafficexpr.filter to_post = {"template_id": self.template_id, "criteria": criteria} logger.debug("Posting JSON: %s" % to_post) response = self.profiler.api.report.reports(data=to_post) try: self.id = int(response["id"]) except KeyError: raise ValueError("failed to retrieve report id from report creation response: %s" % response) logger.info("Created report %d" % self.id) if sync: self.wait_for_complete()
def main(self): self.ip_address = None self.lan_address = None self.wan_address = None self.timefilter = TimeFilter.parse_range(self.options.timefilter) if self.options.wan_address and self.options.lan_address: self.ip_address = self.options.wan_address.split(':')[0] self.lan_address = [self.options.lan_address] self.wan_address = [self.options.wan_address] elif self.options.device_name: name = self.options.device_name devices = self.profiler.api.devices.get_all() for d in devices: if name.lower() in d['name'].lower(): self.ip_address = d['ipaddr'] break else: print 'Device %s cannot be found in Profiler device list' % name print 'Try specifying the name differently or use an IP address' sys.exit(1) else: self.ip_address = self.options.device_address if self.options.summary: self.columns = ['device', 'avg_bytes', 'total_bytes'] self.groupby = 'dev' ReportClass = WANSummaryReport else: # Time Series report self.columns = ['time', 'avg_bytes', 'total_bytes'] self.groupby = None ReportClass = WANTimeSeriesReport with ReportClass(self.profiler) as report: if not self.lan_address: # query for the interfaces self.lan_address, self.wan_address = report.get_interfaces(self.ip_address) if self.options.out_inbound or self.options.out_combined: # inbound report.run(self.lan_address, self.wan_address, 'inbound', columns=self.columns, groupby=self.groupby, timefilter=self.timefilter, resolution='auto') inbound = report.get_data(as_list=False) if self.options.out_inbound: header = 'Inbound traffic:' self.print_data(inbound, header) if self.options.out_outbound or self.options.out_combined: # outbound report.run(self.lan_address, self.wan_address, 'outbound', columns=self.columns, groupby=self.groupby, timefilter=self.timefilter, resolution='auto') outbound = report.get_data(as_list=False) if self.options.out_outbound: header = 'Outbound traffic:' self.print_data(outbound, header) if self.options.out_combined: header = 'Combined Inbound/Outbound traffic:' total = inbound + outbound self.print_data(total, header)