Grafana dashboard: Pixie Pod Overview
Description: Dashboard shows an overview of the specified pod, including high-level HTTP application metrics, and resource usage. 

To learn how to monitor infrastructure health using Pixie, see the [tutorial](https://docs.px.dev/tutorials/pixie-101/infra-health/).

{
  "__inputs": [
    {
      "name": "DS_PIXIE_GRAFANA DATASOURCE PLUGIN",
      "label": "Pixie Grafana Datasource Plugin",
      "description": "",
      "type": "datasource",
      "pluginId": "pixie-pixie-datasource",
      "pluginName": "Pixie Grafana Datasource Plugin"
    }
  ],
  "__requires": [
    {
      "type": "datasource",
      "id": "pixie-pixie-datasource",
      "name": "Pixie Grafana Datasource Plugin",
      "version": "0.0.9"
    },
    {
      "type": "panel",
      "id": "table",
      "name": "Table",
      "version": ""
    },
    {
      "type": "panel",
      "id": "timeseries",
      "name": "Time series",
      "version": ""
    }
  ],
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": "-- Grafana --",
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "target": {
          "limit": 100,
          "matchAny": false,
          "tags": [],
          "type": "dashboard"
        },
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 0,
  "id": null,
  "iteration": 1657153831003,
  "links": [],
  "liveNow": false,
  "panels": [
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "request throughput",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "/s"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 0,
        "y": 0
      },
      "id": 2,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable changes\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = inbound_request_timeseries_by_container(start_time, pod)\noutput.request_throughput = output.request_throughput * px.pow(10,9)\npx.display(output[['time_', 'request_throughput', 'container']])"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "HTTP Requests",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "error rate",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "/s"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 8,
        "y": 0
      },
      "id": 4,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = inbound_request_timeseries_by_container(start_time, pod)\noutput.errors_per_ns = output.errors_per_ns * px.pow(10,9)\npx.display(output[['time_', 'errors_per_ns', 'container']])"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "HTTP Errors",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisGridShow": true,
            "axisLabel": "milliseconds",
            "axisPlacement": "auto",
            "axisSoftMax": 6,
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "ms"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 16,
        "y": 0
      },
      "id": 10,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = inbound_latency_timeseries(start_time, pod)\noutput.latency_p50 = output.latency_p50 / px.pow(10,6)\noutput.latency_p90 = output.latency_p90 / px.pow(10,6)\noutput.latency_p99 = output.latency_p99 / px.pow(10,6)\n\npx.display(output[['time_', 'latency_p50', 'latency_p90', 'latency_p99']])"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "HTTP Latency",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "CPU Usage",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "percent"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 12,
        "w": 8,
        "x": 0,
        "y": 13
      },
      "id": 6,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = resource_timeseries(start_time, pod)\noutput.cpu_usage_percent = output.cpu_usage * 100\npx.display(output[['time_', 'cpu_usage_percent', 'container']])"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "CPU Usage",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "displayMode": "auto",
            "inspect": false
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          }
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "state"
            },
            "properties": [
              {
                "id": "custom.width",
                "value": 131
              },
              {
                "id": "custom.displayMode",
                "value": "color-text"
              },
              {
                "id": "mappings",
                "value": [
                  {
                    "options": {
                      "Failed": {
                        "color": "red",
                        "index": 5
                      },
                      "Pending": {
                        "color": "yellow",
                        "index": 6
                      },
                      "Running": {
                        "color": "green",
                        "index": 0
                      },
                      "Succeeded": {
                        "color": "green",
                        "index": 4
                      },
                      "Terminated": {
                        "color": "red",
                        "index": 3
                      },
                      "Unknown": {
                        "color": "red",
                        "index": 7
                      },
                      "Waiting": {
                        "color": "yellow",
                        "index": 8
                      },
                      "false": {
                        "color": "red",
                        "index": 2
                      },
                      "true": {
                        "color": "green",
                        "index": 1
                      }
                    },
                    "type": "value"
                  }
                ]
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "ready"
            },
            "properties": [
              {
                "id": "custom.width",
                "value": 97
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "name"
            },
            "properties": [
              {
                "id": "custom.width",
                "value": 102
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "message"
            },
            "properties": [
              {
                "id": "custom.width",
                "value": 84
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 12,
        "w": 8,
        "x": 8,
        "y": 13
      },
      "id": 8,
      "options": {
        "footer": {
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true,
        "sortBy": []
      },
      "pluginVersion": "7.5.1",
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = containers(start_time, pod)\n\noutput.state = px.pluck(output.status, \"state\") \noutput.message = px.pluck(output.status, \"ready\") \n\noutput = output[['name', 'state', 'message', 'id']]\n\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Container List",
      "type": "table"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "displayMode": "auto",
            "inspect": false
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          }
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "pid"
            },
            "properties": [
              {
                "id": "custom.width",
                "value": 118
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "cmd"
            },
            "properties": [
              {
                "id": "custom.width",
                "value": 321
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 12,
        "w": 8,
        "x": 16,
        "y": 13
      },
      "id": 12,
      "options": {
        "footer": {
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true,
        "sortBy": []
      },
      "pluginVersion": "7.5.1",
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = processes(start_time, pod)\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Process List",
      "type": "table"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "Network Throughput",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "KBs"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 0,
        "y": 25
      },
      "id": 15,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = network_timeseries(start_time, pod)\noutput.rx_bytes_per_ns = output.rx_bytes_per_ns * px.pow(10, 9) / px.pow(2,10)\noutput.tx_bytes_per_ns = output.tx_bytes_per_ns * px.pow(10, 9) / px.pow(2,10)\noutput = output[['time_', 'rx_bytes_per_ns', 'tx_bytes_per_ns']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Network Sent And Received",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "Disk Read Throughput",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "KBs"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 8,
        "y": 25
      },
      "id": 16,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": ".05# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = resource_timeseries(start_time, pod)\noutput.total_disk_read_throughput = output.total_disk_read_throughput * px.pow(10,9) / px.pow(2,10)\noutput = output[['time_', 'total_disk_read_throughput', 'container']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Bytes Read",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "Disk Write Throughput",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "binBps"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 16,
        "y": 25
      },
      "id": 17,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = resource_timeseries(start_time, pod)\noutput.total_disk_write_throughput = output.total_disk_write_throughput * px.pow(10,9)\noutput = output[['time_', 'total_disk_write_throughput', 'container']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Bytes Written",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "RSS",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "decmbytes"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 0,
        "y": 38
      },
      "id": 18,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = resource_timeseries(start_time, pod)\noutput.rss = output.rss / px.pow(2,20)\noutput = output[['time_', 'rss', 'container']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Resident Set Size",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisLabel": "vsize",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 0,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "decmbytes"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 13,
        "w": 8,
        "x": 8,
        "y": 38
      },
      "id": 19,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom"
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = resource_timeseries(start_time, pod)\noutput.vsize = output.vsize / px.pow(2,20)\noutput = output[['time_', 'vsize', 'container']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Virtual Memory Size",
      "type": "timeseries"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "displayMode": "auto",
            "inspect": false
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          }
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "latency_p99"
            },
            "properties": [
              {
                "id": "custom.displayMode",
                "value": "color-text"
              },
              {
                "id": "thresholds",
                "value": {
                  "mode": "absolute",
                  "steps": [
                    {
                      "color": "green"
                    },
                    {
                      "color": "light-orange",
                      "value": 150
                    },
                    {
                      "color": "red",
                      "value": 300
                    }
                  ]
                }
              },
              {
                "id": "decimals",
                "value": 1
              },
              {
                "id": "unit",
                "value": "ms"
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "error_rate"
            },
            "properties": [
              {
                "id": "unit",
                "value": "percentunit"
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "request_throughput"
            },
            "properties": [
              {
                "id": "unit",
                "value": "/s"
              },
              {
                "id": "decimals",
                "value": 1
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "requesting_pod"
            },
            "properties": [
              {
                "id": "links",
                "value": [
                  {
                    "title": "",
                    "url": "/d/_t3foxCnz/px-pod?orgId=1&${pixieCluster:queryparam}&var-pixiePod=${__data.fields.requesting_pod}"
                  }
                ]
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "requesting_svc"
            },
            "properties": [
              {
                "id": "links",
                "value": [
                  {
                    "title": "",
                    "url": "/d/qhHtFyCnk/px-service?orgId=1&${pixieCluster:queryparam}&var-pixieService=${__data.fields.requesting_svc}"
                  }
                ]
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 10,
        "w": 24,
        "x": 0,
        "y": 51
      },
      "id": 20,
      "options": {
        "footer": {
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true,
        "sortBy": [
          {
            "desc": true,
            "displayName": "error_rate"
          }
        ]
      },
      "pluginVersion": "7.5.1",
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = inbound_let_summary(start_time, pod)\noutput.latency_p99 = px.pluck_float64(output.latency, \"p99\") / px.pow(10,6)\noutput.request_throughput = output.request_throughput * px.pow(10,9)\noutput = output[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency_p99', 'error_rate', 'request_throughput']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Inbound HTTP Traffic To Pod",
      "type": "table"
    },
    {
      "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
      "description": "",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "displayMode": "auto",
            "inspect": false
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green"
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          }
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "phase"
            },
            "properties": [
              {
                "id": "custom.displayMode",
                "value": "color-text"
              },
              {
                "id": "mappings",
                "value": [
                  {
                    "options": {
                      "Running": {
                        "color": "green",
                        "index": 0
                      }
                    },
                    "type": "value"
                  }
                ]
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "ready"
            },
            "properties": [
              {
                "id": "custom.displayMode",
                "value": "color-text"
              },
              {
                "id": "mappings",
                "value": [
                  {
                    "options": {
                      "false": {
                        "color": "red",
                        "index": 1
                      },
                      "true": {
                        "color": "green",
                        "index": 0
                      }
                    },
                    "type": "value"
                  }
                ]
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "node"
            },
            "properties": [
              {
                "id": "links",
                "value": [
                  {
                    "title": "",
                    "url": "/d/tcpoz_jnz/px-node?orgId=1&${pixieCluster:queryparam}&var-node=${__data.fields.node}&var-groupby=node"
                  }
                ]
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "service"
            },
            "properties": [
              {
                "id": "links",
                "value": [
                  {
                    "title": "",
                    "url": "/d/qhHtFyCnk/px-service?orgId=1&${pixieCluster:queryparam}&var-pixieService=${__data.fields.service}"
                  }
                ]
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 8,
        "w": 24,
        "x": 0,
        "y": 61
      },
      "id": 21,
      "options": {
        "footer": {
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true
      },
      "pluginVersion": "7.5.1",
      "targets": [
        {
          "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
          "queryBody": {
            "pxlScript": "# Copyright 2018- The Pixie Authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n\n'''Pod Overview\n\nOverview of a specific Pod monitored by Pixie with its high level application metrics\n(latency, error-rate & rps) and resource usage (cpu, writes, reads).\n\n'''\nimport px\n\nns_per_ms = 1000 * 1000\nns_per_s = 1000 * ns_per_ms\n# Window size to use on time_ column for bucketing.\nwindow_ns = px.DurationNanos(10 * ns_per_s)\n# Flag to filter out requests that come from an unresolvable IP.\nfilter_unresolved_inbound = True\n# Flag to filter out health checks from the data.\nfilter_health_checks = True\n# Flag to filter out ready checks from the data.\nfilter_ready_checks = True\n\n# $pixieCluster - work around for grafana to update panel on variable change\n\nstart_time = __time_from\npod = \"$pixiePod\"\n\ndef containers(start_time: int, pod: str):\n    ''' A list of containers in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.name = df.ctx['container_name']\n    df.id = df.ctx['container_id']\n    df = df.groupby(['name', 'id']).agg()\n    df.status = px.container_id_to_status(df.id)\n    return df\n\n\ndef node(start_time: int, pod: str):\n    ''' A list containing the node the `pod` is running on.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.node = df.ctx['node_name']\n    df.service = df.ctx['service']\n    df.pod_id = df.ctx['pod_id']\n    df.pod_name = df.ctx['pod']\n    df = df.groupby(['node', 'service', 'pod_id', 'pod_name']).agg()\n    df.pod_start_time = px.pod_name_to_start_time(df.pod_name)\n    df.status = px.pod_name_to_status(df.pod_name)\n    return df.drop('pod_name')\n\n\ndef processes(start_time: int, pod: str):\n    ''' A list of processes in `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.cmd = df.ctx['cmdline']\n    df.pid = df.ctx['pid']\n    df = df.groupby(['pid', 'cmd', 'upid']).agg()\n    return df\n\n\ndef resource_timeseries(start_time: int, pod: str):\n    ''' Compute the resource usage as a timeseries for `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = px.DataFrame(table='process_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.container = df.ctx['container_name']\n\n    # First calculate CPU usage by process (UPID) in each k8s_object\n    # over all windows.\n    df = df.groupby(['upid', 'container', 'timestamp']).agg(\n        rss=('rss_bytes', px.mean),\n        vsize=('vsize_bytes', px.mean),\n        # The fields below are counters, so we take the min and the max to subtract them.\n        cpu_utime_ns_max=('cpu_utime_ns', px.max),\n        cpu_utime_ns_min=('cpu_utime_ns', px.min),\n        cpu_ktime_ns_max=('cpu_ktime_ns', px.max),\n        cpu_ktime_ns_min=('cpu_ktime_ns', px.min),\n        read_bytes_max=('read_bytes', px.max),\n        read_bytes_min=('read_bytes', px.min),\n        write_bytes_max=('write_bytes', px.max),\n        write_bytes_min=('write_bytes', px.min),\n        rchar_bytes_max=('rchar_bytes', px.max),\n        rchar_bytes_min=('rchar_bytes', px.min),\n        wchar_bytes_max=('wchar_bytes', px.max),\n        wchar_bytes_min=('wchar_bytes', px.min),\n    )\n\n    # Next calculate cpu usage and memory stats per window.\n    df.cpu_utime_ns = df.cpu_utime_ns_max - df.cpu_utime_ns_min\n    df.cpu_ktime_ns = df.cpu_ktime_ns_max - df.cpu_ktime_ns_min\n    df.actual_disk_read_throughput = (df.read_bytes_max - df.read_bytes_min) / window_ns\n    df.actual_disk_write_throughput = (df.write_bytes_max - df.write_bytes_min) / window_ns\n    df.total_disk_read_throughput = (df.rchar_bytes_max - df.rchar_bytes_min) / window_ns\n    df.total_disk_write_throughput = (df.wchar_bytes_max - df.wchar_bytes_min) / window_ns\n\n    # Then aggregate process individual process metrics.\n    df = df.groupby(['timestamp', 'container']).agg(\n        cpu_ktime_ns=('cpu_ktime_ns', px.sum),\n        cpu_utime_ns=('cpu_utime_ns', px.sum),\n        actual_disk_read_throughput=('actual_disk_read_throughput', px.sum),\n        actual_disk_write_throughput=('actual_disk_write_throughput', px.sum),\n        total_disk_read_throughput=('total_disk_read_throughput', px.sum),\n        total_disk_write_throughput=('total_disk_write_throughput', px.sum),\n        rss=('rss', px.sum),\n        vsize=('vsize', px.sum),\n    )\n\n    # Finally, calculate total (kernel + user time)  percentage used over window.\n    df.cpu_usage = px.Percent((df.cpu_ktime_ns + df.cpu_utime_ns) / window_ns)\n    df['time_'] = df['timestamp']\n    return df.drop(['cpu_ktime_ns', 'cpu_utime_ns', 'timestamp'])\n\n\ndef network_timeseries(start_time: int, pod: str):\n    ''' Gets the network stats (transmitted/received traffic) for the input node.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @node: The full name of the node to filter on.\n    '''\n    df = px.DataFrame(table='network_stats', start_time=start_time)\n    df = df[df.ctx['pod'] == pod]\n    df.timestamp = px.bin(df.time_, window_ns)\n\n    # First calculate network usage by node over all windows.\n    # Data is sharded by Pod in network_stats.\n    df = df.groupby(['timestamp', 'pod_id']).agg(\n        rx_bytes_end=('rx_bytes', px.max),\n        rx_bytes_start=('rx_bytes', px.min),\n        tx_bytes_end=('tx_bytes', px.max),\n        tx_bytes_start=('tx_bytes', px.min),\n        tx_errors_end=('tx_errors', px.max),\n        tx_errors_start=('tx_errors', px.min),\n        rx_errors_end=('rx_errors', px.max),\n        rx_errors_start=('rx_errors', px.min),\n        tx_drops_end=('tx_drops', px.max),\n        tx_drops_start=('tx_drops', px.min),\n        rx_drops_end=('rx_drops', px.max),\n        rx_drops_start=('rx_drops', px.min),\n    )\n\n    # Calculate the network statistics rate over the window.\n    # We subtract the counter value at the beginning ('_start')\n    # from the value at the end ('_end').\n    df.rx_bytes_per_ns = (df.rx_bytes_end - df.rx_bytes_start) / window_ns\n    df.tx_bytes_per_ns = (df.tx_bytes_end - df.tx_bytes_start) / window_ns\n    df.rx_drops_per_ns = (df.rx_drops_end - df.rx_drops_start) / window_ns\n    df.tx_drops_per_ns = (df.tx_drops_end - df.tx_drops_start) / window_ns\n    df.rx_errors_per_ns = (df.rx_errors_end - df.rx_errors_start) / window_ns\n    df.tx_errors_per_ns = (df.tx_errors_end - df.tx_errors_start) / window_ns\n\n    # Add up the network values per node.\n    df = df.groupby(['timestamp']).agg(\n        rx_bytes_per_ns=('rx_bytes_per_ns', px.sum),\n        tx_bytes_per_ns=('tx_bytes_per_ns', px.sum),\n        rx_drop_per_ns=('rx_drops_per_ns', px.sum),\n        tx_drops_per_ns=('tx_drops_per_ns', px.sum),\n        rx_errors_per_ns=('rx_errors_per_ns', px.sum),\n        tx_errors_per_ns=('tx_errors_per_ns', px.sum),\n    )\n    df['time_'] = df['timestamp']\n    return df\n\n\ndef inbound_latency_timeseries(start_time: int, pod: str):\n    ''' Compute the latency as a timeseries for requests received by `pod`.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    df = df.groupby(['timestamp']).agg(\n        latency_quantiles=('latency', px.quantiles)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.latency_p50 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p50')))\n    df.latency_p90 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p90')))\n    df.latency_p99 = px.DurationNanos(px.floor(px.pluck_float64(df.latency_quantiles, 'p99')))\n    df.time_ = df.timestamp\n    return df[['time_', 'latency_p50', 'latency_p90', 'latency_p99']]\n\n\ndef inbound_request_timeseries_by_container(start_time: int, pod: str):\n    ''' Compute the request statistics as a timeseries for requests received\n        by `pod` by container.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n    @pod: The name of the pod to filter on.\n\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n    df.container = df.ctx['container']\n\n    df = df.groupby(['timestamp', 'container']).agg(\n        error_rate_per_window=('failure', px.mean),\n        throughput_total=('latency', px.count)\n    )\n\n    # Format the result of LET aggregates into proper scalar formats and\n    # time series.\n    df.request_throughput = df.throughput_total / window_ns\n    df.errors_per_ns = df.error_rate_per_window * df.request_throughput / px.DurationNanos(1)\n    df.error_rate = px.Percent(df.error_rate_per_window)\n    df.time_ = df.timestamp\n\n    return df[['time_', 'container', 'request_throughput', 'errors_per_ns', 'error_rate']]\n\n\ndef inbound_let_summary(start_time: int, pod: str):\n    ''' Gets a summary of requests inbound to `pod`.\n\n    Args:\n    @start_time Starting time of the data to examine.\n    @pod: The pod to filter on.\n    '''\n    df = let_helper(start_time)\n    df = df[df.pod == pod]\n\n    quantiles_agg = df.groupby(['pod', 'remote_addr']).agg(\n        latency=('latency', px.quantiles),\n        total_request_count=('latency', px.count)\n    )\n\n    quantiles_table = quantiles_agg[['pod', 'remote_addr', 'latency',\n                                     'total_request_count']]\n\n    range_agg = df.groupby(['pod', 'remote_addr', 'timestamp']).agg(\n        requests_per_window=('time_', px.count),\n        error_rate=('failure', px.mean)\n    )\n\n    rps_table = range_agg.groupby(['pod', 'remote_addr']).agg(\n        requests_per_window=('requests_per_window', px.mean),\n        error_rate=('error_rate', px.mean)\n    )\n\n    joined_table = quantiles_table.merge(rps_table,\n                                         how='inner',\n                                         left_on=['pod', 'remote_addr'],\n                                         right_on=['pod', 'remote_addr'],\n                                         suffixes=['', '_x'])\n\n    joined_table.error_rate = px.Percent(joined_table.error_rate)\n    joined_table.request_throughput = joined_table.requests_per_window / window_ns\n\n    joined_table.responder = df.pod\n    joined_table.requesting_ip = df.remote_addr\n    joined_table.requesting_pod = px.pod_id_to_pod_name(px.ip_to_pod_id(df.remote_addr))\n    joined_table.requesting_svc = px.pod_id_to_service_name(px.ip_to_pod_id(df.remote_addr))\n\n    return joined_table[['requesting_ip', 'requesting_pod', 'requesting_svc', 'latency',\n                         'error_rate', 'request_throughput']]\n\n\ndef let_helper(start_time: int):\n    ''' Compute the initial part of the let for requests.\n        Filtering to inbound/outbound traffic by pod is done by the calling function.\n\n    Args:\n    @start_time: The timestamp of data to start at.\n\n    '''\n    df = px.DataFrame(table='http_events', start_time=start_time)\n    df.pod = df.ctx['pod']\n    df.timestamp = px.bin(df.time_, window_ns)\n    df.failure = df.resp_status >= 400\n\n    # Filter only to inbound pod traffic (server-side).\n    # Don't include traffic initiated by this pod to an external location.\n    df = df[df.trace_role == 2]\n\n    filter_out_conds = ((df.req_path != '/healthz' or not filter_health_checks) and (\n        df.req_path != '/readyz' or not filter_ready_checks)) and (\n        df['remote_addr'] != '-' or not filter_unresolved_inbound)\n    df = df[filter_out_conds]\n\n    return df\n\n\ndef stacktraces(start_time: int, pod: str):\n    df = px.DataFrame(table='stack_traces.beta', start_time=start_time)\n\n    df.namespace = df.ctx['namespace']\n    df.pod = df.ctx['pod']\n    df.container = df.ctx['container']\n    df.cmdline = df.ctx['cmdline']\n\n    # Filter on the pod.\n    df = df[df.pod == pod]\n\n    # Get stack trace totals for the pod.\n    # This must be done before any additional filtering to avoid skewing percentages.\n    grouping_agg = df.groupby([\"pod\"]).agg(\n        count=('count', px.sum)\n    )\n\n    # Combine flamegraphs from different intervals into one larger framegraph.\n    df = df.groupby(['namespace', 'pod', 'container', 'cmdline', 'stack_trace_id']).agg(\n        stack_trace=('stack_trace', px.any),\n        count=('count', px.sum)\n    )\n\n    # Compute percentages.\n    df = df.merge(\n        grouping_agg,\n        how='inner',\n        left_on=\"pod\",\n        right_on=\"pod\",\n        suffixes=['', '_x']\n    )\n    df.percent = 100.0 * df.count / df.count_x\n    df.drop('pod_x')\n\n    return df\n\noutput = node(start_time, pod)\noutput.phase = px.pluck(output.status, \"phase\")\noutput.ready = px.pluck(output.status, \"ready\")\noutput = output[['node', 'phase', 'ready', 'service', 'pod_id', 'pod_start_time']]\npx.display(output)"
          },
          "queryType": "run-script",
          "refId": "A"
        }
      ],
      "title": "Pod Metadata",
      "type": "table"
    }
  ],
  "schemaVersion": 27,
  "style": "dark",
  "tags": [],
  "templating": {
    "list": [
      {
        "current": {},
        "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
        "definition": "Clusters",
        "hide": 0,
        "includeAll": false,
        "multi": false,
        "name": "pixieCluster",
        "options": [],
        "query": {
          "queryType": "get-clusters"
        },
        "refresh": 2,
        "regex": "",
        "skipUrlSync": false,
        "sort": 0,
        "type": "query"
      },
      {
        "current": {},
        "datasource": "${DS_PIXIE_GRAFANA DATASOURCE PLUGIN}",
        "definition": "Pods",
        "hide": 0,
        "includeAll": false,
        "multi": false,
        "name": "pixiePod",
        "options": [],
        "query": {
          "queryBody": {
            "clusterID": "$pixieCluster"
          },
          "queryType": "get-pods"
        },
        "refresh": 2,
        "regex": "",
        "skipUrlSync": false,
        "sort": 0,
        "type": "query"
      }
    ]
  },
  "time": {
    "from": "now-15m",
    "to": "now"
  },
  "timepicker": {},
  "timezone": "",
  "title": "Pixie Pod Overview",
  "description": "Dashboard shows an overview of the specified pod, including high-level HTTP application metrics, and resource usage. ",
  "uid": "_t3foxCnz",
  "version": 1,
  "weekStart": "",
  "gnetId": 17001
}