We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add Network send/recv to web UI plot
It would be nice to see network activity over time.

We already capture this data in the same way we capture the CPU/Memory data that we already plot, so the job of figuring out how to move the data into the `ColumnDataSource` isn't hard.  What _is_ tricky is that we don't have a percentage, but instead have actual MB/s bandwidth counts.  We would have to add a second Y-axis (this is doable) and think a bit about how best to set the axes.

This might be an interesting task for anyone looking to get into the Bokeh Web UI, notably @martindurant 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `distributed/bokeh/worker_monitor.py`
Content:
```
1 from __future__ import print_function, division, absolute_import
2 
3 from collections import defaultdict
4 from itertools import chain
5 
6 from toolz import pluck
7 
8 from ..utils import ignoring
9 
10 with ignoring(ImportError):
11     from bokeh.models import (ColumnDataSource, DataRange1d, Range1d,
12             NumeralTickFormatter)
13     from bokeh.palettes import Spectral9
14     from bokeh.plotting import figure
15 
16 
17 def resource_profile_plot(width=600, height=300):
18     names = ['time', 'cpu', 'memory-percent']
19     source = ColumnDataSource({k: [] for k in names})
20 
21     x_range = DataRange1d(follow='end', follow_interval=30000, range_padding=0)
22     y_range = Range1d(0, 1)
23     p = figure(width=width, height=height, x_axis_type='datetime',
24                responsive=True, tools='xpan,xwheel_zoom,box_zoom,resize,reset',
25                x_range=x_range, y_range=y_range)
26     p.line(x='time', y='memory-percent', line_width=2, line_alpha=0.8,
27            color=Spectral9[7], legend='Avg Memory Usage', source=source)
28     p.line(x='time', y='cpu', line_width=2, line_alpha=0.8,
29            color=Spectral9[0], legend='Avg CPU Usage', source=source)
30     p.legend[0].location = 'top_left'
31     p.yaxis[0].formatter = NumeralTickFormatter(format="0 %")
32     p.min_border_right = 10
33 
34     return source, p
35 
36 
37 def resource_profile_update(source, worker_buffer, times_buffer):
38     data = defaultdict(list)
39 
40     workers = sorted(list(set(chain(*list(w.keys() for w in worker_buffer)))))
41 
42     for name in ['cpu', 'memory-percent']:
43         data[name] = [[msg[w][name] if w in msg and name in msg[w] else 'null'
44                        for msg in worker_buffer]
45                        for w in workers]
46 
47     data['workers'] = workers
48     data['times'] = [[t * 1000 if w in worker_buffer[i] else 'null'
49                       for i, t in enumerate(times_buffer)]
50                       for w in workers]
51 
52     source.data.update(data)
53 
54 
55 def resource_append(lists, msg):
56     L = list(msg.values())
57     if not L:
58         return
59     for k in ['cpu', 'memory-percent']:
60         lists[k].append(mean(pluck(k, L)) / 100)
61 
62     lists['time'].append(mean(pluck('time', L)) * 1000)
63 
64 
65 def mean(seq):
66     seq = list(seq)
67     return sum(seq) / len(seq)
68
```

Path: `distributed/bokeh/status/server_lifecycle.py`
Content:
```
1 #!/usr/bin/env python
2 from __future__ import print_function, division, absolute_import
3 
4 from collections import deque
5 import json
6 import os
7 from time import time
8 
9 from tornado import gen
10 from tornado.httpclient import AsyncHTTPClient
11 from tornado.iostream import StreamClosedError
12 from tornado.ioloop import IOLoop
13 
14 from distributed.core import read
15 from distributed.diagnostics.progress_stream import progress_stream
16 from distributed.bokeh.worker_monitor import resource_append
17 import distributed.bokeh
18 from distributed.utils import log_errors
19 
20 client = AsyncHTTPClient()
21 
22 messages = distributed.bokeh.messages  # monkey-patching
23 
24 if os.path.exists('.dask-web-ui.json'):
25     with open('.dask-web-ui.json', 'r') as f:
26         options = json.load(f)
27 else:
28     options = {'host': '127.0.0.1',
29                'tcp-port': 8786,
30                'http-port': 9786}
31 
32 
33 @gen.coroutine
34 def http_get(route):
35     """ Get data from JSON route, store in messages deques """
36     with log_errors():
37         try:
38             response = yield client.fetch(
39                     'http://%(host)s:%(http-port)d/' % options
40                      + route + '.json')
41         except ConnectionRefusedError:
42             import sys; sys.exit(0)
43         msg = json.loads(response.body.decode())
44         messages[route]['deque'].append(msg)
45         messages[route]['times'].append(time())
46 
47 
48 last_index = [0]
49 @gen.coroutine
50 def workers():
51     """ Get data from JSON route, store in messages deques """
52     with log_errors():
53         response = yield client.fetch(
54                 'http://%(host)s:%(http-port)d/workers.json' % options)
55         msg = json.loads(response.body.decode())
56         if msg:
57             messages['workers']['deque'].append(msg)
58             messages['workers']['times'].append(time())
59             resource_append(messages['workers']['plot-data'], msg)
60             index = messages['workers']['index']
61             index.append(last_index[0] + 1)
62             last_index[0] += 1
63 
64 
65 @gen.coroutine
66 def progress():
67     with log_errors():
68         stream = yield progress_stream('%(host)s:%(tcp-port)d' % options, 0.050)
69         while True:
70             try:
71                 msg = yield read(stream)
72             except StreamClosedError:
73                 break
74             else:
75                 messages['progress'] = msg
76 
77 
78 def on_server_loaded(server_context):
79     n = 60
80     messages['workers'] = {'interval': 500,
81                            'deque': deque(maxlen=n),
82                            'times': deque(maxlen=n),
83                            'index': deque(maxlen=n),
84                            'plot-data': {'time': deque(maxlen=n),
85                                          'cpu': deque(maxlen=n),
86                                          'memory-percent': deque(maxlen=n)}}
87     server_context.add_periodic_callback(workers, 500)
88 
89     messages['tasks'] = {'interval': 100,
90                          'deque': deque(maxlen=100),
91                          'times': deque(maxlen=100)}
92     server_context.add_periodic_callback(lambda: http_get('tasks'), 100)
93 
94     messages['progress'] = {'all': {}, 'in_memory': {},
95                             'erred': {}, 'released': {}}
96 
97     IOLoop.current().add_callback(progress)
98
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/distributed/bokeh/status/server_lifecycle.py b/distributed/bokeh/status/server_lifecycle.py
--- a/distributed/bokeh/status/server_lifecycle.py
+++ b/distributed/bokeh/status/server_lifecycle.py
@@ -83,7 +83,8 @@
                            'index': deque(maxlen=n),
                            'plot-data': {'time': deque(maxlen=n),
                                          'cpu': deque(maxlen=n),
-                                         'memory-percent': deque(maxlen=n)}}
+                                         'memory-percent': deque(maxlen=n),
+                                         'network-send': deque(maxlen=n)}}
     server_context.add_periodic_callback(workers, 500)
 
     messages['tasks'] = {'interval': 100,
diff --git a/distributed/bokeh/worker_monitor.py b/distributed/bokeh/worker_monitor.py
--- a/distributed/bokeh/worker_monitor.py
+++ b/distributed/bokeh/worker_monitor.py
@@ -9,13 +9,13 @@
 
 with ignoring(ImportError):
     from bokeh.models import (ColumnDataSource, DataRange1d, Range1d,
-            NumeralTickFormatter)
+            NumeralTickFormatter, LinearAxis)
     from bokeh.palettes import Spectral9
     from bokeh.plotting import figure
 
 
 def resource_profile_plot(width=600, height=300):
-    names = ['time', 'cpu', 'memory-percent']
+    names = ['time', 'cpu', 'memory-percent', 'network-send']
     source = ColumnDataSource({k: [] for k in names})
 
     x_range = DataRange1d(follow='end', follow_interval=30000, range_padding=0)
@@ -30,6 +30,14 @@
     p.legend[0].location = 'top_left'
     p.yaxis[0].formatter = NumeralTickFormatter(format="0 %")
     p.min_border_right = 10
+    p.extra_y_ranges = {"send": DataRange1d(bounds=(0, None))}
+    p.add_layout(LinearAxis(y_range_name="send", axis_label="Throughput (MB/s)"),
+                 'right')
+    p.yaxis.axis_label_text_font_size = "10pt"
+
+    p.line(x='time', y='network-send', line_width=2, line_alpha=0.8,
+           color=Spectral9[2], legend='Network', source=source,
+           y_range_name="send")
 
     return source, p
 
@@ -39,7 +47,7 @@
 
     workers = sorted(list(set(chain(*list(w.keys() for w in worker_buffer)))))
 
-    for name in ['cpu', 'memory-percent']:
+    for name in ['cpu', 'memory-percent', 'network-send']:
         data[name] = [[msg[w][name] if w in msg and name in msg[w] else 'null'
                        for msg in worker_buffer]
                        for w in workers]
@@ -60,6 +68,13 @@
         lists[k].append(mean(pluck(k, L)) / 100)
 
     lists['time'].append(mean(pluck('time', L)) * 1000)
+    net = mean(pluck('network-send', L, 0))
+    if len(lists['time']) >= 2:
+        t1, t2 = lists['time'][-2], lists['time'][-1]
+        interval = (t2 - t1) / 1000
+    else:
+        interval = 0.5
+    lists['network-send'].append(net / 2**20 / interval)
 
 
 def mean(seq):

{"golden_diff": "diff --git a/distributed/bokeh/status/server_lifecycle.py b/distributed/bokeh/status/server_lifecycle.py\n--- a/distributed/bokeh/status/server_lifecycle.py\n+++ b/distributed/bokeh/status/server_lifecycle.py\n@@ -83,7 +83,8 @@\n                            'index': deque(maxlen=n),\n                            'plot-data': {'time': deque(maxlen=n),\n                                          'cpu': deque(maxlen=n),\n-                                         'memory-percent': deque(maxlen=n)}}\n+                                         'memory-percent': deque(maxlen=n),\n+                                         'network-send': deque(maxlen=n)}}\n     server_context.add_periodic_callback(workers, 500)\n \n     messages['tasks'] = {'interval': 100,\ndiff --git a/distributed/bokeh/worker_monitor.py b/distributed/bokeh/worker_monitor.py\n--- a/distributed/bokeh/worker_monitor.py\n+++ b/distributed/bokeh/worker_monitor.py\n@@ -9,13 +9,13 @@\n \n with ignoring(ImportError):\n     from bokeh.models import (ColumnDataSource, DataRange1d, Range1d,\n-            NumeralTickFormatter)\n+            NumeralTickFormatter, LinearAxis)\n     from bokeh.palettes import Spectral9\n     from bokeh.plotting import figure\n \n \n def resource_profile_plot(width=600, height=300):\n-    names = ['time', 'cpu', 'memory-percent']\n+    names = ['time', 'cpu', 'memory-percent', 'network-send']\n     source = ColumnDataSource({k: [] for k in names})\n \n     x_range = DataRange1d(follow='end', follow_interval=30000, range_padding=0)\n@@ -30,6 +30,14 @@\n     p.legend[0].location = 'top_left'\n     p.yaxis[0].formatter = NumeralTickFormatter(format=\"0 %\")\n     p.min_border_right = 10\n+    p.extra_y_ranges = {\"send\": DataRange1d(bounds=(0, None))}\n+    p.add_layout(LinearAxis(y_range_name=\"send\", axis_label=\"Throughput (MB/s)\"),\n+                 'right')\n+    p.yaxis.axis_label_text_font_size = \"10pt\"\n+\n+    p.line(x='time', y='network-send', line_width=2, line_alpha=0.8,\n+           color=Spectral9[2], legend='Network', source=source,\n+           y_range_name=\"send\")\n \n     return source, p\n \n@@ -39,7 +47,7 @@\n \n     workers = sorted(list(set(chain(*list(w.keys() for w in worker_buffer)))))\n \n-    for name in ['cpu', 'memory-percent']:\n+    for name in ['cpu', 'memory-percent', 'network-send']:\n         data[name] = [[msg[w][name] if w in msg and name in msg[w] else 'null'\n                        for msg in worker_buffer]\n                        for w in workers]\n@@ -60,6 +68,13 @@\n         lists[k].append(mean(pluck(k, L)) / 100)\n \n     lists['time'].append(mean(pluck('time', L)) * 1000)\n+    net = mean(pluck('network-send', L, 0))\n+    if len(lists['time']) >= 2:\n+        t1, t2 = lists['time'][-2], lists['time'][-1]\n+        interval = (t2 - t1) / 1000\n+    else:\n+        interval = 0.5\n+    lists['network-send'].append(net / 2**20 / interval)\n \n \n def mean(seq):\n", "issue": "Add Network send/recv to web UI plot\nIt would be nice to see network activity over time.\n\nWe already capture this data in the same way we capture the CPU/Memory data that we already plot, so the job of figuring out how to move the data into the `ColumnDataSource` isn't hard.  What _is_ tricky is that we don't have a percentage, but instead have actual MB/s bandwidth counts.  We would have to add a second Y-axis (this is doable) and think a bit about how best to set the axes.\n\nThis might be an interesting task for anyone looking to get into the Bokeh Web UI, notably @martindurant \n\n", "before_files": [{"content": "from __future__ import print_function, division, absolute_import\n\nfrom collections import defaultdict\nfrom itertools import chain\n\nfrom toolz import pluck\n\nfrom ..utils import ignoring\n\nwith ignoring(ImportError):\n    from bokeh.models import (ColumnDataSource, DataRange1d, Range1d,\n            NumeralTickFormatter)\n    from bokeh.palettes import Spectral9\n    from bokeh.plotting import figure\n\n\ndef resource_profile_plot(width=600, height=300):\n    names = ['time', 'cpu', 'memory-percent']\n    source = ColumnDataSource({k: [] for k in names})\n\n    x_range = DataRange1d(follow='end', follow_interval=30000, range_padding=0)\n    y_range = Range1d(0, 1)\n    p = figure(width=width, height=height, x_axis_type='datetime',\n               responsive=True, tools='xpan,xwheel_zoom,box_zoom,resize,reset',\n               x_range=x_range, y_range=y_range)\n    p.line(x='time', y='memory-percent', line_width=2, line_alpha=0.8,\n           color=Spectral9[7], legend='Avg Memory Usage', source=source)\n    p.line(x='time', y='cpu', line_width=2, line_alpha=0.8,\n           color=Spectral9[0], legend='Avg CPU Usage', source=source)\n    p.legend[0].location = 'top_left'\n    p.yaxis[0].formatter = NumeralTickFormatter(format=\"0 %\")\n    p.min_border_right = 10\n\n    return source, p\n\n\ndef resource_profile_update(source, worker_buffer, times_buffer):\n    data = defaultdict(list)\n\n    workers = sorted(list(set(chain(*list(w.keys() for w in worker_buffer)))))\n\n    for name in ['cpu', 'memory-percent']:\n        data[name] = [[msg[w][name] if w in msg and name in msg[w] else 'null'\n                       for msg in worker_buffer]\n                       for w in workers]\n\n    data['workers'] = workers\n    data['times'] = [[t * 1000 if w in worker_buffer[i] else 'null'\n                      for i, t in enumerate(times_buffer)]\n                      for w in workers]\n\n    source.data.update(data)\n\n\ndef resource_append(lists, msg):\n    L = list(msg.values())\n    if not L:\n        return\n    for k in ['cpu', 'memory-percent']:\n        lists[k].append(mean(pluck(k, L)) / 100)\n\n    lists['time'].append(mean(pluck('time', L)) * 1000)\n\n\ndef mean(seq):\n    seq = list(seq)\n    return sum(seq) / len(seq)\n", "path": "distributed/bokeh/worker_monitor.py"}, {"content": "#!/usr/bin/env python\nfrom __future__ import print_function, division, absolute_import\n\nfrom collections import deque\nimport json\nimport os\nfrom time import time\n\nfrom tornado import gen\nfrom tornado.httpclient import AsyncHTTPClient\nfrom tornado.iostream import StreamClosedError\nfrom tornado.ioloop import IOLoop\n\nfrom distributed.core import read\nfrom distributed.diagnostics.progress_stream import progress_stream\nfrom distributed.bokeh.worker_monitor import resource_append\nimport distributed.bokeh\nfrom distributed.utils import log_errors\n\nclient = AsyncHTTPClient()\n\nmessages = distributed.bokeh.messages  # monkey-patching\n\nif os.path.exists('.dask-web-ui.json'):\n    with open('.dask-web-ui.json', 'r') as f:\n        options = json.load(f)\nelse:\n    options = {'host': '127.0.0.1',\n               'tcp-port': 8786,\n               'http-port': 9786}\n\n\n@gen.coroutine\ndef http_get(route):\n    \"\"\" Get data from JSON route, store in messages deques \"\"\"\n    with log_errors():\n        try:\n            response = yield client.fetch(\n                    'http://%(host)s:%(http-port)d/' % options\n                     + route + '.json')\n        except ConnectionRefusedError:\n            import sys; sys.exit(0)\n        msg = json.loads(response.body.decode())\n        messages[route]['deque'].append(msg)\n        messages[route]['times'].append(time())\n\n\nlast_index = [0]\n@gen.coroutine\ndef workers():\n    \"\"\" Get data from JSON route, store in messages deques \"\"\"\n    with log_errors():\n        response = yield client.fetch(\n                'http://%(host)s:%(http-port)d/workers.json' % options)\n        msg = json.loads(response.body.decode())\n        if msg:\n            messages['workers']['deque'].append(msg)\n            messages['workers']['times'].append(time())\n            resource_append(messages['workers']['plot-data'], msg)\n            index = messages['workers']['index']\n            index.append(last_index[0] + 1)\n            last_index[0] += 1\n\n\n@gen.coroutine\ndef progress():\n    with log_errors():\n        stream = yield progress_stream('%(host)s:%(tcp-port)d' % options, 0.050)\n        while True:\n            try:\n                msg = yield read(stream)\n            except StreamClosedError:\n                break\n            else:\n                messages['progress'] = msg\n\n\ndef on_server_loaded(server_context):\n    n = 60\n    messages['workers'] = {'interval': 500,\n                           'deque': deque(maxlen=n),\n                           'times': deque(maxlen=n),\n                           'index': deque(maxlen=n),\n                           'plot-data': {'time': deque(maxlen=n),\n                                         'cpu': deque(maxlen=n),\n                                         'memory-percent': deque(maxlen=n)}}\n    server_context.add_periodic_callback(workers, 500)\n\n    messages['tasks'] = {'interval': 100,\n                         'deque': deque(maxlen=100),\n                         'times': deque(maxlen=100)}\n    server_context.add_periodic_callback(lambda: http_get('tasks'), 100)\n\n    messages['progress'] = {'all': {}, 'in_memory': {},\n                            'erred': {}, 'released': {}}\n\n    IOLoop.current().add_callback(progress)\n", "path": "distributed/bokeh/status/server_lifecycle.py"}], "after_files": [{"content": "from __future__ import print_function, division, absolute_import\n\nfrom collections import defaultdict\nfrom itertools import chain\n\nfrom toolz import pluck\n\nfrom ..utils import ignoring\n\nwith ignoring(ImportError):\n    from bokeh.models import (ColumnDataSource, DataRange1d, Range1d,\n            NumeralTickFormatter, LinearAxis)\n    from bokeh.palettes import Spectral9\n    from bokeh.plotting import figure\n\n\ndef resource_profile_plot(width=600, height=300):\n    names = ['time', 'cpu', 'memory-percent', 'network-send']\n    source = ColumnDataSource({k: [] for k in names})\n\n    x_range = DataRange1d(follow='end', follow_interval=30000, range_padding=0)\n    y_range = Range1d(0, 1)\n    p = figure(width=width, height=height, x_axis_type='datetime',\n               responsive=True, tools='xpan,xwheel_zoom,box_zoom,resize,reset',\n               x_range=x_range, y_range=y_range)\n    p.line(x='time', y='memory-percent', line_width=2, line_alpha=0.8,\n           color=Spectral9[7], legend='Avg Memory Usage', source=source)\n    p.line(x='time', y='cpu', line_width=2, line_alpha=0.8,\n           color=Spectral9[0], legend='Avg CPU Usage', source=source)\n    p.legend[0].location = 'top_left'\n    p.yaxis[0].formatter = NumeralTickFormatter(format=\"0 %\")\n    p.min_border_right = 10\n    p.extra_y_ranges = {\"send\": DataRange1d(bounds=(0, None))}\n    p.add_layout(LinearAxis(y_range_name=\"send\", axis_label=\"Throughput (MB/s)\"),\n                 'right')\n    p.yaxis.axis_label_text_font_size = \"10pt\"\n\n    p.line(x='time', y='network-send', line_width=2, line_alpha=0.8,\n           color=Spectral9[2], legend='Network', source=source,\n           y_range_name=\"send\")\n\n    return source, p\n\n\ndef resource_profile_update(source, worker_buffer, times_buffer):\n    data = defaultdict(list)\n\n    workers = sorted(list(set(chain(*list(w.keys() for w in worker_buffer)))))\n\n    for name in ['cpu', 'memory-percent', 'network-send']:\n        data[name] = [[msg[w][name] if w in msg and name in msg[w] else 'null'\n                       for msg in worker_buffer]\n                       for w in workers]\n\n    data['workers'] = workers\n    data['times'] = [[t * 1000 if w in worker_buffer[i] else 'null'\n                      for i, t in enumerate(times_buffer)]\n                      for w in workers]\n\n    source.data.update(data)\n\n\ndef resource_append(lists, msg):\n    L = list(msg.values())\n    if not L:\n        return\n    for k in ['cpu', 'memory-percent']:\n        lists[k].append(mean(pluck(k, L)) / 100)\n\n    lists['time'].append(mean(pluck('time', L)) * 1000)\n    net = mean(pluck('network-send', L, 0))\n    if len(lists['time']) >= 2:\n        t1, t2 = lists['time'][-2], lists['time'][-1]\n        interval = (t2 - t1) / 1000\n    else:\n        interval = 0.5\n    lists['network-send'].append(net / 2**20 / interval)\n\n\ndef mean(seq):\n    seq = list(seq)\n    return sum(seq) / len(seq)\n", "path": "distributed/bokeh/worker_monitor.py"}, {"content": "#!/usr/bin/env python\nfrom __future__ import print_function, division, absolute_import\n\nfrom collections import deque\nimport json\nimport os\nfrom time import time\n\nfrom tornado import gen\nfrom tornado.httpclient import AsyncHTTPClient\nfrom tornado.iostream import StreamClosedError\nfrom tornado.ioloop import IOLoop\n\nfrom distributed.core import read\nfrom distributed.diagnostics.progress_stream import progress_stream\nfrom distributed.bokeh.worker_monitor import resource_append\nimport distributed.bokeh\nfrom distributed.utils import log_errors\n\nclient = AsyncHTTPClient()\n\nmessages = distributed.bokeh.messages  # monkey-patching\n\nif os.path.exists('.dask-web-ui.json'):\n    with open('.dask-web-ui.json', 'r') as f:\n        options = json.load(f)\nelse:\n    options = {'host': '127.0.0.1',\n               'tcp-port': 8786,\n               'http-port': 9786}\n\n\n@gen.coroutine\ndef http_get(route):\n    \"\"\" Get data from JSON route, store in messages deques \"\"\"\n    with log_errors():\n        try:\n            response = yield client.fetch(\n                    'http://%(host)s:%(http-port)d/' % options\n                     + route + '.json')\n        except ConnectionRefusedError:\n            import sys; sys.exit(0)\n        msg = json.loads(response.body.decode())\n        messages[route]['deque'].append(msg)\n        messages[route]['times'].append(time())\n\n\nlast_index = [0]\n@gen.coroutine\ndef workers():\n    \"\"\" Get data from JSON route, store in messages deques \"\"\"\n    with log_errors():\n        response = yield client.fetch(\n                'http://%(host)s:%(http-port)d/workers.json' % options)\n        msg = json.loads(response.body.decode())\n        if msg:\n            messages['workers']['deque'].append(msg)\n            messages['workers']['times'].append(time())\n            resource_append(messages['workers']['plot-data'], msg)\n            index = messages['workers']['index']\n            index.append(last_index[0] + 1)\n            last_index[0] += 1\n\n\n@gen.coroutine\ndef progress():\n    with log_errors():\n        stream = yield progress_stream('%(host)s:%(tcp-port)d' % options, 0.050)\n        while True:\n            try:\n                msg = yield read(stream)\n            except StreamClosedError:\n                break\n            else:\n                messages['progress'] = msg\n\n\ndef on_server_loaded(server_context):\n    n = 60\n    messages['workers'] = {'interval': 500,\n                           'deque': deque(maxlen=n),\n                           'times': deque(maxlen=n),\n                           'index': deque(maxlen=n),\n                           'plot-data': {'time': deque(maxlen=n),\n                                         'cpu': deque(maxlen=n),\n                                         'memory-percent': deque(maxlen=n),\n                                         'network-send': deque(maxlen=n)}}\n    server_context.add_periodic_callback(workers, 500)\n\n    messages['tasks'] = {'interval': 100,\n                         'deque': deque(maxlen=100),\n                         'times': deque(maxlen=100)}\n    server_context.add_periodic_callback(lambda: http_get('tasks'), 100)\n\n    messages['progress'] = {'all': {}, 'in_memory': {},\n                            'erred': {}, 'released': {}}\n\n    IOLoop.current().add_callback(progress)\n", "path": "distributed/bokeh/status/server_lifecycle.py"}]}