diff options
author | Joffrey F <joffrey@docker.com> | 2015-02-12 13:17:04 -0800 |
---|---|---|
committer | Joffrey F <joffrey@docker.com> | 2015-02-12 13:17:04 -0800 |
commit | 21e189c923d2fa8c362e13ecef5f81f1b837b817 (patch) | |
tree | 014c4e0aaa5f1dd719c506465e84e5b562dd9585 | |
parent | 1565464ff052c0a7cb25c32a0500eb9137515b13 (diff) | |
parent | 1f1e399958ee2f05e0a417dd1009565b11956c49 (diff) | |
download | docker-py-21e189c923d2fa8c362e13ecef5f81f1b837b817.tar.gz |
Merge branch 'nir0s-add-stats-api-support'
-rw-r--r-- | docker/client.py | 11 | ||||
-rw-r--r-- | docs/api.md | 21 | ||||
-rw-r--r-- | docs/change_log.md | 1 | ||||
-rw-r--r-- | tests/fake_api.py | 9 | ||||
-rw-r--r-- | tests/fake_stat.py | 153 | ||||
-rw-r--r-- | tests/integration_test.py | 2 | ||||
-rw-r--r-- | tests/test.py | 12 |
7 files changed, 208 insertions, 1 deletions
diff --git a/docker/client.py b/docker/client.py index c7abd87..6c14706 100644 --- a/docker/client.py +++ b/docker/client.py @@ -869,11 +869,22 @@ class Client(requests.Session): res = self._post_json(url, data=start_config) self._raise_for_status(res) + def stats(self, container): + if utils.compare_version('1.17', self._version) < 0: + raise errors.InvalidVersion( + 'Stats retrieval is not supported in API < 1.17!') + + if isinstance(container, dict): + container = container.get('Id') + url = self._url("/containers/{0}/stats".format(container)) + return self._stream_helper(self._get(url, stream=True)) + def stop(self, container, timeout=10): if isinstance(container, dict): container = container.get('Id') params = {'t': timeout} url = self._url("/containers/{0}/stop".format(container)) + res = self._post(url, params=params, timeout=(timeout + self.timeout)) self._raise_for_status(res) diff --git a/docs/api.md b/docs/api.md index b07cbfa..a523878 100644 --- a/docs/api.md +++ b/docs/api.md @@ -708,6 +708,27 @@ from. Optionally a single string joining container id's with commas None ``` +## stats + +The Docker API parallel to the `docker stats` command. +This will stream statistics for a specific container. + +**Params**: + +* container (str): The container to start + +```python +>>> from docker import Client +>>> cli = Client(base_url='tcp://127.0.0.1:2375') +>>> stats_obj = cli.stats('elasticsearch') +>>> for stat in stats: +>>> print(stat) +{"read":"2015-02-11T21:47:30.49388286+02:00","network":{"rx_bytes":666052,"rx_packets":4409 ... +... +... +... +``` + ## stop Stops a container. Similar to the `docker stop` command. diff --git a/docs/change_log.md b/docs/change_log.md index d2e8cd7..346b5a2 100644 --- a/docs/change_log.md +++ b/docs/change_log.md @@ -1,6 +1,7 @@ Change Log ========== + 0.7.2 ----- diff --git a/tests/fake_api.py b/tests/fake_api.py index ab2e4fd..6d6d80e 100644 --- a/tests/fake_api.py +++ b/tests/fake_api.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import fake_stat + CURRENT_VERSION = 'v1.17' FAKE_CONTAINER_ID = '3cc2351ab11b' @@ -337,6 +339,11 @@ def post_fake_tag_image(): return status_code, response +def get_fake_stats(): + status_code = 200 + response = fake_stat.OBJ + return status_code, response + # Maps real api url to fake response callback prefix = 'http+unix://var/run/docker.sock' fake_responses = { @@ -376,6 +383,8 @@ fake_responses = { post_fake_execute, '{1}/{0}/exec/3cc2351ab11b/start'.format(CURRENT_VERSION, prefix): post_fake_execute_start, + '{1}/{0}/containers/3cc2351ab11b/stats'.format(CURRENT_VERSION, prefix): + get_fake_stats, '{1}/{0}/containers/3cc2351ab11b/stop'.format(CURRENT_VERSION, prefix): post_fake_stop_container, '{1}/{0}/containers/3cc2351ab11b/kill'.format(CURRENT_VERSION, prefix): diff --git a/tests/fake_stat.py b/tests/fake_stat.py new file mode 100644 index 0000000..470e014 --- /dev/null +++ b/tests/fake_stat.py @@ -0,0 +1,153 @@ +OBJ = { + "read": "2015-02-11T19:20:46.667237763+02:00", + "network": { + "rx_bytes": 567224, + "rx_packets": 3773, + "rx_errors": 0, + "rx_dropped": 0, + "tx_bytes": 1176, + "tx_packets": 13, + "tx_errors": 0, + "tx_dropped": 0 + }, + "cpu_stats": { + "cpu_usage": { + "total_usage": 157260874053, + "percpu_usage": [ + 52196306950, + 24118413549, + 53292684398, + 27653469156 + ], + "usage_in_kernelmode": 37140000000, + "usage_in_usermode": 62140000000 + }, + "system_cpu_usage": 3.0881377e+14, + "throttling_data": { + "periods": 0, + "throttled_periods": 0, + "throttled_time": 0 + } + }, + "memory_stats": { + "usage": 179314688, + "max_usage": 258166784, + "stats": { + "active_anon": 90804224, + "active_file": 2195456, + "cache": 3096576, + "hierarchical_memory_limit": 1.844674407371e+19, + "inactive_anon": 85516288, + "inactive_file": 798720, + "mapped_file": 2646016, + "pgfault": 101034, + "pgmajfault": 1207, + "pgpgin": 115814, + "pgpgout": 75613, + "rss": 176218112, + "rss_huge": 12582912, + "total_active_anon": 90804224, + "total_active_file": 2195456, + "total_cache": 3096576, + "total_inactive_anon": 85516288, + "total_inactive_file": 798720, + "total_mapped_file": 2646016, + "total_pgfault": 101034, + "total_pgmajfault": 1207, + "total_pgpgin": 115814, + "total_pgpgout": 75613, + "total_rss": 176218112, + "total_rss_huge": 12582912, + "total_unevictable": 0, + "total_writeback": 0, + "unevictable": 0, + "writeback": 0 + }, + "failcnt": 0, + "limit": 8039038976 + }, + "blkio_stats": { + "io_service_bytes_recursive": [ + { + "major": 8, + "minor": 0, + "op": "Read", + "value": 72843264 + }, + { + "major": 8, + "minor": 0, + "op": "Write", + "value": 4096 + }, + { + "major": 8, + "minor": 0, + "op": "Sync", + "value": 4096 + }, + { + "major": 8, + "minor": 0, + "op": "Async", + "value": 72843264 + }, + { + "major": 8, + "minor": 0, + "op": "Total", + "value": 72847360 + } + ], + "io_serviced_recursive": [ + { + "major": 8, + "minor": 0, + "op": "Read", + "value": 10581 + }, + { + "major": 8, + "minor": 0, + "op": "Write", + "value": 1 + }, + { + "major": 8, + "minor": 0, + "op": "Sync", + "value": 1 + }, + { + "major": 8, + "minor": 0, + "op": "Async", + "value": 10581 + }, + { + "major": 8, + "minor": 0, + "op": "Total", + "value": 10582 + } + ], + "io_queue_recursive": [ + + ], + "io_service_time_recursive": [ + + ], + "io_wait_time_recursive": [ + + ], + "io_merged_recursive": [ + + ], + "io_time_recursive": [ + + ], + "sectors_recursive": [ + + ] + } +} diff --git a/tests/integration_test.py b/tests/integration_test.py index b6ea674..3044f5b 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -29,7 +29,7 @@ import six from test import Cleanup # FIXME: missing tests for -# export; history; import_image; insert; port; push; tag; get; load +# export; history; import_image; insert; port; push; tag; get; load; stats; DEFAULT_BASE_URL = os.environ.get('DOCKER_HOST') diff --git a/tests/test.py b/tests/test.py index bf7b3fb..f55027a 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1672,6 +1672,18 @@ class DockerClientTest(Cleanup, unittest.TestCase): timeout=docker.client.DEFAULT_TIMEOUT_SECONDS ) + def test_container_stats(self): + try: + self.client.stats(fake_api.FAKE_CONTAINER_ID) + except Exception as e: + self.fail('Command should not raise exception: {0}'.format(e)) + + fake_request.assert_called_with( + url_prefix + 'containers/3cc2351ab11b/stats', + timeout=60, + stream=True + ) + ################## # IMAGES TESTS # ################## |