summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2015-02-12 13:17:04 -0800
committerJoffrey F <joffrey@docker.com>2015-02-12 13:17:04 -0800
commit21e189c923d2fa8c362e13ecef5f81f1b837b817 (patch)
tree014c4e0aaa5f1dd719c506465e84e5b562dd9585
parent1565464ff052c0a7cb25c32a0500eb9137515b13 (diff)
parent1f1e399958ee2f05e0a417dd1009565b11956c49 (diff)
downloaddocker-py-21e189c923d2fa8c362e13ecef5f81f1b837b817.tar.gz
Merge branch 'nir0s-add-stats-api-support'
-rw-r--r--docker/client.py11
-rw-r--r--docs/api.md21
-rw-r--r--docs/change_log.md1
-rw-r--r--tests/fake_api.py9
-rw-r--r--tests/fake_stat.py153
-rw-r--r--tests/integration_test.py2
-rw-r--r--tests/test.py12
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 #
##################