def graph_num_clients(step_size, output_name, title, data): """ Create a stacked graph per client pool. Arguments: output_name(Path): where to write the graph title(str): title for the graph step_size(int): number of milliseconds between samples data(DataFrame): data to graph """ max_time = data['end'].max() clients = data['client'].unique() xs = list() ydata = dict() # client -> data time_step = 0 while time_step < max_time: xs.append(map_utils.timestamp_to_minutes(time_step)) for client in sorted(clients): ys = ydata.get(client, list()) y_value = data.loc[(data['start'] <= time_step) & (time_step < data['end']) & (data['client'] == client)]['num clients'].sum() ys.append(y_value) ydata[client] = ys time_step = time_step + step_size fig, ax = map_utils.subplots() ax.set_title(title) ax.set_xlabel("Time (minutes)") ax.set_ylabel("Number of clients") ax.set_xlim(left=0, right=map_utils.timestamp_to_minutes(max_time)) stack_labels, stack_ys = zip(*(sorted(ydata.items()))) ax.stackplot(xs, stack_ys, labels=stack_labels) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd, ), bbox_inches='tight') plt.close(fig)
def output_graph(output, data, min_time): fig, ax = map_utils.subplots() ax.set_title("Resource report generation lag") ax.set_xlabel("Time (minutes)") ax.set_ylabel("Difference (seconds)") max_minutes = 0 for node_name, plot_data in data.items(): (xs, ys) = plot_data minutes = [map_utils.timestamp_to_minutes(x - min_time) for x in xs] max_minutes = max(max_minutes, max(minutes)) ax.scatter(minutes, ys, label=node_name, s=1) ax.set_xlim(left=0, right=max_minutes) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / 'resource-report-time-lag.png' plt.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd, ), bbox_inches='tight') plt.close(fig)
def relative_timestamps_service(data, min_timestamp): """ Arguments: data (dict): service -> timestamp -> ServiceCounts min_timestamp (int): first timestamp as milliseconds since the Epoch Returns: dict: service -> relative minutes -> ServiceCounts """ relative_data = dict() for service, service_data in data.items(): relative_service_data = dict() for timestamp, counts in sorted(service_data.items()): minutes = map_utils.timestamp_to_minutes(timestamp - min_timestamp) relative_service_data[minutes] = counts relative_data[service] = relative_service_data return relative_data
def output_graph(output, ncp, xs, ys, first_timestamp_ms): """ Arguments: output(Path): output directory ncp(str): name of the NCP the graph is for xs: list of x-values ys(dict): neighbor to y-values first_timestamp_ms(int): initial timestamp to subtract from values """ fig, ax = map_utils.subplots() ax.set_title(f"RLG Resource report lag for node {ncp}") ax.set_xlabel("Time (minutes)") ax.set_ylabel("Difference (seconds)") minutes = [ map_utils.timestamp_to_minutes(x - first_timestamp_ms) for x in xs ] max_minutes = max(minutes) for node_name, plot_data in ys.items(): get_logger().debug( "Graphing NCP %s with neighbor %s. x.len: %d y.len: %d", ncp, node_name, len(minutes), len(plot_data)) ax.scatter(minutes, plot_data, label=node_name, s=1) ax.set_xlim(left=0, right=max_minutes) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f'rlg-node-resource-report-time-lag_{ncp}.png' plt.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd, ), bbox_inches='tight') plt.close(fig)
def output_region_capacity_graph_for_app(first_timestamp, output, capacity, app): """ Create a graph per node attribute for the specified service with a series for each region. Creates files with names capacity-<attribute>-<app>.png Args: first_timestamp(datetime.datetime): when the simulation started output (Path): output directory capacity (boolean): if true output a graph of capacity only app (str): name of the service to generate the graph for """ first_timestamp_ms = first_timestamp.timestamp() * 1000 max_minutes = 0 try: label='capacity' all_regions = set(capacity.keys()) all_attrs = set() for region, region_data in capacity.items(): all_attrs.update(region_data.keys()) for attr in all_attrs: if 'QueueLength' == attr: # graphing queue length capacity doesn't work well because we've hardcoded it to be a big number continue frames = list() fig, ax = map_utils.subplots() ax.set_title(f"{attr} {label} for {app}") ax.set_xlabel("Time (minutes)") stack_xs = None stack_ys = list() stack_labels = list() for region in sorted(all_regions): region_data = capacity.get(region, dict()) attr_data = region_data.get(attr, dict()) app_data = attr_data.get(app, dict()) if len(app_data) > 0: pairs = sorted(app_data.items()) timestamps, values = zip(*pairs) times = [map_utils.timestamp_to_minutes(float(t) - first_timestamp_ms) for t in timestamps] max_minutes = max(max_minutes, max(times)) series_label=f"{region} capacity" frames.append(pd.DataFrame(list(values), index=list(times), columns=[series_label])) ax.plot(times, values, label=series_label) if stack_xs is None: stack_xs = times stack_ys.append(values) stack_labels.append(series_label) ax.set_xlim(left=0, right=max_minutes) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f"{label}-{attr}-{app}.png" fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd,), bbox_inches='tight') plt.close(fig) # write out CSV of the plot data df = pd.concat(frames, axis=1) df.to_csv(output / f"{label}-{attr}-{app}.csv", index_label="relative minutes") # create stacked plot fig, ax = map_utils.subplots() ax.set_title(f"{attr} {label} for {app}") ax.set_xlabel("Time (minutes)") ax.set_xlim(left=0, right=max_minutes) ax.stackplot(stack_xs, stack_ys, labels=stack_labels) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f"{label}-{attr}-{app}_stacked.png" fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd,), bbox_inches='tight') plt.close(fig) except: get_logger().exception("Unexpected error")
def output_region_graphs_for_app(first_timestamp, output, capacity, data, label, app): """ Create a graph per node attribute for the specified app with a series for each region. Creates files with names <label>-<attribute>-<app>.png Args: first_timestamp(datetime.datetime): when the simulation started output (Path): output directory data (dict): region -> attr -> app -> timestamp -> value label (str): used to label the graph and generate the filenames capacity (dict): region -> attr -> app -> timestamp -> value app (str): service to generate the graph for """ first_timestamp_ms = first_timestamp.timestamp() * 1000 max_minutes = 0 try: all_regions = set(capacity.keys()) all_attrs = set() for region, region_data in data.items(): all_attrs.update(region_data.keys()) for attr in all_attrs: stack_xs = None stack_ys = list() stack_labels = list() frames = list() fig, ax = map_utils.subplots() ax.set_title(f"{attr} {label} for {app}") ax.set_xlabel("Time (minutes)") for region in sorted(all_regions): region_data = data.get(region, dict()) attr_data = region_data.get(attr, dict()) capacity_region = capacity.get(region, dict()) if attr in capacity_region and 'QueueLength' != attr: app_data = capacity_region[attr].get(app, dict()) if len(app_data) > 0: pairs = sorted(app_data.items()) timestamps, values = zip(*pairs) times = [map_utils.timestamp_to_minutes(float(t) - first_timestamp_ms) for t in timestamps] max_minutes = max(max_minutes, max(times)) series_label=f"{region} capacity" frames.append(pd.DataFrame(list(values), index=list(times), columns=[series_label])) ax.plot(times, values, label=series_label) app_data = attr_data.get(app, dict()) if len(app_data) > 0: pairs = sorted(app_data.items()) timestamps, values = zip(*pairs) times = [map_utils.timestamp_to_minutes(float(t) - first_timestamp_ms) for t in timestamps] max_minutes = max(max_minutes, max(times)) frames.append(pd.DataFrame(list(values), index=list(times), columns=[region])) ax.plot(times, values, label=region) if stack_xs is None: stack_xs = times stack_ys.append(values) stack_labels.append(region) ax.set_xlim(left=0, right=max_minutes) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f"{label}-{attr}-{app}.png" fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd,), bbox_inches='tight') plt.close(fig) # write out CSV of the plot data df = pd.concat(frames, axis=1) df.to_csv(output / f"{label}-{attr}-{app}.csv", index_label="relative minutes") # create stacked plot fig, ax = map_utils.subplots() ax.set_title(f"{attr} {label} for {app}") ax.set_xlabel("Time (minutes)") ax.set_xlim(left=0, right=max_minutes) ax.stackplot(stack_xs, stack_ys, labels=stack_labels) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f"{label}-{attr}-{app}_stacked.png" fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd,), bbox_inches='tight') plt.close(fig) except: get_logger().exception("Unexpected error")
def output_graphs_per_region(first_timestamp, output, capacity, data, label): """ Create a graph per node attribute. Creates files with names <label>-<attribute>-<region>.png Args: first_timestamp(datetime.datetime): when the simulation started output (Path): output directory data (dict): region -> attr -> app -> timestamp -> value label (str): used to label the graph and generate the filenames capacity: region -> attr -> app -> timestamp -> value """ first_timestamp_ms = first_timestamp.timestamp() * 1000 max_minutes = 0 try: for region, region_data in data.items(): capacity_region = capacity[region] for attr, attr_data in region_data.items(): frames = list() fig, ax = map_utils.subplots() ax.set_title(f"{attr} {label} in {region}") ax.set_xlabel("Time (minutes)") stack_xs = None stack_ys = list() stack_labels = list() total_capacity = None if attr in capacity_region and 'QueueLength' != attr: for app, app_data in sorted(capacity_region[attr].items()): if len(app_data) > 0: pairs = sorted(app_data.items()) timestamps, values = zip(*pairs) times = [map_utils.timestamp_to_minutes(float(t) - first_timestamp_ms) for t in timestamps] max_minutes = max(max_minutes, max(times)) series_label=f"{app} capacity" frames.append(pd.DataFrame(list(values), index=list(times), columns=[series_label])) ax.plot(times, values, label=series_label) if total_capacity is None: total_capacity = app_data.copy() else: total_capacity = {k: total_capacity.get(k, 0) + app_data.get(k, 0) for k in set(total_capacity) | set(app_data)} app_timestamps = None for app, app_data in sorted(attr_data.items()): if len(app_data) > 0: pairs = sorted(app_data.items()) timestamps, values = zip(*pairs) times = [map_utils.timestamp_to_minutes(float(t) - first_timestamp_ms) for t in timestamps] max_minutes = max(max_minutes, max(times)) if app_timestamps is None: app_timestamps = timestamps frames.append(pd.DataFrame(list(values), index=list(times), columns=[app])) ax.plot(times, values, label=app) if stack_xs is None: stack_xs = times stack_ys.append(values) stack_labels.append(app) ax.set_xlim(left=0, right=max_minutes) handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f"{label}-{attr}-{region}.png" fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd,), bbox_inches='tight') plt.close(fig) # write out CSV of the plot data df = pd.concat(frames, axis=1) df.to_csv(output / f"{label}-{attr}-{region}.csv", index_label="relative minutes") if app_timestamps is not None and total_capacity is not None: # create stacked plot fig, ax = map_utils.subplots() ax.set_title(f"{attr} {label} in {region}") ax.set_xlabel("Time (minutes)") ax.set_xlim(left=0, right=max_minutes) ax.stackplot(stack_xs, stack_ys, labels=stack_labels) total_capacity = map_utils.fill_missing_times(app_timestamps, total_capacity) ax.plot(stack_xs, total_capacity, label="Total allocated capacity") handles, labels = ax.get_legend_handles_labels() lgd = ax.legend(handles, labels, bbox_to_anchor=(1.04, 1), loc="upper left") output_name = output / f"{label}-{attr}-{region}_stacked.png" fig.savefig(output_name.as_posix(), format='png', bbox_extra_artists=(lgd,), bbox_inches='tight') plt.close(fig) elif 'QueueLength' != attr: get_logger().warning("Missing data to draw stackplot for %s %s in %s", label, attr, region) except: get_logger().exception("Unexpected error")
def graph(output, first_timestamp, label, data_type, node_data): """ Graph network data. Arguments: output(Path): output directory first_timestamp(int): first timestamp seen in the data label(str): "Summary" or "Report" label(str): "demand" or "load" node_data(dict): service -> NetworkData """ for node_name, network_data in node_data.items(): rx_frames = list() tx_frames = list() all_frames = list() rx_fig, rx_ax = map_utils.subplots() rx_ax.set_title(f"{node_name} {label} {data_type} RX data") rx_ax.set_xlabel("Time (minutes)") tx_fig, tx_ax = map_utils.subplots() tx_ax.set_title(f"{node_name} {label} {data_type} TX data") tx_ax.set_xlabel("Time (minutes)") all_fig, all_ax = map_utils.subplots() all_ax.set_title(f"{node_name} {label} {data_type} ALL data") all_ax.set_xlabel("Time (minutes)") max_minutes = 0 rx_empty_plot = True tx_empty_plot = True all_empty_plot = True for service, service_data in network_data.service.items(): if len(service_data.rx) > 0: rx_empty_plot = False rx_pairs = sorted(service_data.rx.items()) rx_timestamps, rx_values = zip(*rx_pairs) rx_times = [ map_utils.timestamp_to_minutes(float(t) - first_timestamp) for t in rx_timestamps ] rx_series_label = f"{service} RX" rx_ax.plot(rx_times, rx_values, label=rx_series_label) rx_frames.append( pd.DataFrame(list(rx_values), index=rx_times, columns=[rx_series_label])) max_minutes = max(max_minutes, max(rx_times)) if len(service_data.tx) > 0: tx_empty_plot = False tx_pairs = sorted(service_data.tx.items()) tx_timestamps, tx_values = zip(*tx_pairs) tx_times = [ map_utils.timestamp_to_minutes(float(t) - first_timestamp) for t in tx_timestamps ] tx_series_label = f"{service} TX" tx_ax.plot(tx_times, tx_values, label=tx_series_label) tx_frames.append( pd.DataFrame(list(tx_values), index=tx_times, columns=[tx_series_label])) max_minutes = max(max_minutes, max(tx_times)) if len(service_data.all_traffic) > 0: all_empty_plot = False all_pairs = sorted(service_data.all_traffic.items()) all_timestamps, all_values = zip(*all_pairs) all_times = [ map_utils.timestamp_to_minutes(float(t) - first_timestamp) for t in all_timestamps ] all_series_label = f"{service} ALL" all_ax.plot(all_times, all_values, label=all_series_label) all_frames.append( pd.DataFrame(list(all_values), index=all_times, columns=[all_series_label])) max_minutes = max(max_minutes, max(all_times)) if not rx_empty_plot: rx_ax.set_xlim(left=0, right=max_minutes) rx_handles, rx_labels = rx_ax.get_legend_handles_labels() rx_lgd = rx_ax.legend(rx_handles, rx_labels, bbox_to_anchor=(1.04, 1), loc="upper left") rx_output_name = output / f"network-{node_name}-{label}-RX-{data_type}.png" rx_fig.savefig(rx_output_name.as_posix(), format='png', bbox_extra_artists=(rx_lgd, ), bbox_inches='tight') rx_df = pd.concat(rx_frames, axis=1) rx_df.to_csv(output / f"network-{node_name}-{label}-RX-{data_type}.csv", index_label="relative minutes") if not tx_empty_plot: tx_ax.set_xlim(left=0, right=max_minutes) tx_handles, tx_labels = tx_ax.get_legend_handles_labels() tx_lgd = tx_ax.legend(tx_handles, tx_labels, bbox_to_anchor=(1.04, 1), loc="upper left") tx_output_name = output / f"network-{node_name}-{label}-TX-{data_type}.png" tx_fig.savefig(tx_output_name.as_posix(), format='png', bbox_extra_artists=(tx_lgd, ), bbox_inches='tight') tx_df = pd.concat(tx_frames, axis=1) tx_df.to_csv(output / f"network-{node_name}-{label}-TX-{data_type}.csv", index_label="relative minutes") if not all_empty_plot: all_ax.set_xlim(left=0, right=max_minutes) all_handles, all_labels = all_ax.get_legend_handles_labels() all_lgd = all_ax.legend(all_handles, all_labels, bbox_to_anchor=(1.04, 1), loc="upper left") all_output_name = output / f"network-{node_name}-{label}-ALL-{data_type}.png" all_fig.savefig(all_output_name.as_posix(), format='png', bbox_extra_artists=(all_lgd, ), bbox_inches='tight') all_df = pd.concat(all_frames, axis=1) all_df.to_csv(output / f"network-{node_name}-{label}-ALL-{data_type}.csv", index_label="relative minutes") plt.close(rx_fig) plt.close(tx_fig) plt.close(all_fig)
def process_node(output, first_timestamp_ms, all_services, node_dir, show_load_demand_attr): minutes = list() planned_total = list() # number of planned containers total planned_services = dict() # service -> list(value) actual_total_allocation = list() # number of containers actual_services_allocation = dict() # service -> list(allocation value) actual_total_load = list() # load actual_services_load = dict() # service -> list(load value) actual_total_demand = list() # demand actual_services_demand = dict() # service -> list(demand value) ncp = node_dir.stem get_logger().info("Processing directory for node %s: %s", ncp, node_dir) for time_dir in sorted(node_dir.iterdir()): if not time_dir.is_dir(): continue get_logger().debug("Working on %s", time_dir) directory_time = int(time_dir.stem) directory_time_min = map_utils.timestamp_to_minutes(directory_time - first_timestamp_ms) rlg_plan_file = time_dir / 'loadBalancerPlan.json' if not rlg_plan_file.exists(): continue resource_reports_file = time_dir / 'regionResourceReports-SHORT.json' if not resource_reports_file.exists(): continue try: with open(rlg_plan_file, 'r') as f: rlg_plan = json.load(f) except json.decoder.JSONDecodeError: get_logger().warning("Problem reading %s, skipping", rlg_plan_file) continue try: with open(resource_reports_file, 'r') as f: resource_reports = json.load(f) except json.decoder.JSONDecodeError: get_logger().warning("Problem reading %s, skipping", resource_reports_file) continue minutes.append(directory_time_min) (p_total, p_per_service) = process_rlg_plan(rlg_plan) planned_total.append(p_total) for service in all_services: service_list = planned_services.get(service, list()) if service in p_per_service: service_list.append(p_per_service[service]) else: service_list.append(0) planned_services[service] = service_list (a_total_allocation, a_per_service_allocation, a_total_load, a_per_service_load, a_total_demand, a_per_service_demand) = process_reports(resource_reports, show_load_demand_attr) actual_total_allocation.append(a_total_allocation) actual_total_load.append(a_total_load) actual_total_demand.append(a_total_demand) for service in all_services: service_list_allocation = actual_services_allocation.get(service, list()) if service in a_per_service_allocation: service_list_allocation.append(a_per_service_allocation[service]) else: service_list_allocation.append(0) actual_services_allocation[service] = service_list_allocation service_list_load = actual_services_load.get(service, list()) if service in a_per_service_load: service_list_load.append(a_per_service_load[service]) else: service_list_load.append(0) actual_services_load[service] = service_list_load service_list_demand = actual_services_demand.get(service, list()) if service in a_per_service_demand: service_list_demand.append(a_per_service_demand[service]) else: service_list_demand.append(0) actual_services_demand[service] = service_list_demand graph(output, ncp, 'total', minutes, planned_total, actual_total_allocation, actual_total_load, actual_total_demand, show_load_demand_attr) for service in all_services: graph(output, ncp, service, minutes, planned_services[service], actual_services_allocation[service], actual_services_load[service], actual_services_demand[service], show_load_demand_attr)