Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

container.py 51KB

1 year ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339
  1. from datetime import datetime
  2. from .. import errors
  3. from .. import utils
  4. from ..constants import DEFAULT_DATA_CHUNK_SIZE
  5. from ..types import CancellableStream
  6. from ..types import ContainerConfig
  7. from ..types import EndpointConfig
  8. from ..types import HostConfig
  9. from ..types import NetworkingConfig
  10. class ContainerApiMixin:
  11. @utils.check_resource('container')
  12. def attach(self, container, stdout=True, stderr=True,
  13. stream=False, logs=False, demux=False):
  14. """
  15. Attach to a container.
  16. The ``.logs()`` function is a wrapper around this method, which you can
  17. use instead if you want to fetch/stream container output without first
  18. retrieving the entire backlog.
  19. Args:
  20. container (str): The container to attach to.
  21. stdout (bool): Include stdout.
  22. stderr (bool): Include stderr.
  23. stream (bool): Return container output progressively as an iterator
  24. of strings, rather than a single string.
  25. logs (bool): Include the container's previous output.
  26. demux (bool): Keep stdout and stderr separate.
  27. Returns:
  28. By default, the container's output as a single string (two if
  29. ``demux=True``: one for stdout and one for stderr).
  30. If ``stream=True``, an iterator of output strings. If
  31. ``demux=True``, two iterators are returned: one for stdout and one
  32. for stderr.
  33. Raises:
  34. :py:class:`docker.errors.APIError`
  35. If the server returns an error.
  36. """
  37. params = {
  38. 'logs': logs and 1 or 0,
  39. 'stdout': stdout and 1 or 0,
  40. 'stderr': stderr and 1 or 0,
  41. 'stream': stream and 1 or 0
  42. }
  43. headers = {
  44. 'Connection': 'Upgrade',
  45. 'Upgrade': 'tcp'
  46. }
  47. u = self._url("/containers/{0}/attach", container)
  48. response = self._post(u, headers=headers, params=params, stream=True)
  49. output = self._read_from_socket(
  50. response, stream, self._check_is_tty(container), demux=demux)
  51. if stream:
  52. return CancellableStream(output, response)
  53. else:
  54. return output
  55. @utils.check_resource('container')
  56. def attach_socket(self, container, params=None, ws=False):
  57. """
  58. Like ``attach``, but returns the underlying socket-like object for the
  59. HTTP request.
  60. Args:
  61. container (str): The container to attach to.
  62. params (dict): Dictionary of request parameters (e.g. ``stdout``,
  63. ``stderr``, ``stream``).
  64. For ``detachKeys``, ~/.docker/config.json is used by default.
  65. ws (bool): Use websockets instead of raw HTTP.
  66. Raises:
  67. :py:class:`docker.errors.APIError`
  68. If the server returns an error.
  69. """
  70. if params is None:
  71. params = {
  72. 'stdout': 1,
  73. 'stderr': 1,
  74. 'stream': 1
  75. }
  76. if 'detachKeys' not in params \
  77. and 'detachKeys' in self._general_configs:
  78. params['detachKeys'] = self._general_configs['detachKeys']
  79. if ws:
  80. return self._attach_websocket(container, params)
  81. headers = {
  82. 'Connection': 'Upgrade',
  83. 'Upgrade': 'tcp'
  84. }
  85. u = self._url("/containers/{0}/attach", container)
  86. return self._get_raw_response_socket(
  87. self.post(
  88. u, None, params=self._attach_params(params), stream=True,
  89. headers=headers
  90. )
  91. )
  92. @utils.check_resource('container')
  93. def commit(self, container, repository=None, tag=None, message=None,
  94. author=None, changes=None, conf=None):
  95. """
  96. Commit a container to an image. Similar to the ``docker commit``
  97. command.
  98. Args:
  99. container (str): The image hash of the container
  100. repository (str): The repository to push the image to
  101. tag (str): The tag to push
  102. message (str): A commit message
  103. author (str): The name of the author
  104. changes (str): Dockerfile instructions to apply while committing
  105. conf (dict): The configuration for the container. See the
  106. `Engine API documentation
  107. <https://docs.docker.com/reference/api/docker_remote_api/>`_
  108. for full details.
  109. Raises:
  110. :py:class:`docker.errors.APIError`
  111. If the server returns an error.
  112. """
  113. params = {
  114. 'container': container,
  115. 'repo': repository,
  116. 'tag': tag,
  117. 'comment': message,
  118. 'author': author,
  119. 'changes': changes
  120. }
  121. u = self._url("/commit")
  122. return self._result(
  123. self._post_json(u, data=conf, params=params), json=True
  124. )
  125. def containers(self, quiet=False, all=False, trunc=False, latest=False,
  126. since=None, before=None, limit=-1, size=False,
  127. filters=None):
  128. """
  129. List containers. Similar to the ``docker ps`` command.
  130. Args:
  131. quiet (bool): Only display numeric Ids
  132. all (bool): Show all containers. Only running containers are shown
  133. by default
  134. trunc (bool): Truncate output
  135. latest (bool): Show only the latest created container, include
  136. non-running ones.
  137. since (str): Show only containers created since Id or Name, include
  138. non-running ones
  139. before (str): Show only container created before Id or Name,
  140. include non-running ones
  141. limit (int): Show `limit` last created containers, include
  142. non-running ones
  143. size (bool): Display sizes
  144. filters (dict): Filters to be processed on the image list.
  145. Available filters:
  146. - `exited` (int): Only containers with specified exit code
  147. - `status` (str): One of ``restarting``, ``running``,
  148. ``paused``, ``exited``
  149. - `label` (str|list): format either ``"key"``, ``"key=value"``
  150. or a list of such.
  151. - `id` (str): The id of the container.
  152. - `name` (str): The name of the container.
  153. - `ancestor` (str): Filter by container ancestor. Format of
  154. ``<image-name>[:tag]``, ``<image-id>``, or
  155. ``<image@digest>``.
  156. - `before` (str): Only containers created before a particular
  157. container. Give the container name or id.
  158. - `since` (str): Only containers created after a particular
  159. container. Give container name or id.
  160. A comprehensive list can be found in the documentation for
  161. `docker ps
  162. <https://docs.docker.com/engine/reference/commandline/ps>`_.
  163. Returns:
  164. A list of dicts, one per container
  165. Raises:
  166. :py:class:`docker.errors.APIError`
  167. If the server returns an error.
  168. """
  169. params = {
  170. 'limit': 1 if latest else limit,
  171. 'all': 1 if all else 0,
  172. 'size': 1 if size else 0,
  173. 'trunc_cmd': 1 if trunc else 0,
  174. 'since': since,
  175. 'before': before
  176. }
  177. if filters:
  178. params['filters'] = utils.convert_filters(filters)
  179. u = self._url("/containers/json")
  180. res = self._result(self._get(u, params=params), True)
  181. if quiet:
  182. return [{'Id': x['Id']} for x in res]
  183. if trunc:
  184. for x in res:
  185. x['Id'] = x['Id'][:12]
  186. return res
  187. def create_container(self, image, command=None, hostname=None, user=None,
  188. detach=False, stdin_open=False, tty=False, ports=None,
  189. environment=None, volumes=None,
  190. network_disabled=False, name=None, entrypoint=None,
  191. working_dir=None, domainname=None, host_config=None,
  192. mac_address=None, labels=None, stop_signal=None,
  193. networking_config=None, healthcheck=None,
  194. stop_timeout=None, runtime=None,
  195. use_config_proxy=True, platform=None):
  196. """
  197. Creates a container. Parameters are similar to those for the ``docker
  198. run`` command except it doesn't support the attach options (``-a``).
  199. The arguments that are passed directly to this function are
  200. host-independent configuration options. Host-specific configuration
  201. is passed with the `host_config` argument. You'll normally want to
  202. use this method in combination with the :py:meth:`create_host_config`
  203. method to generate ``host_config``.
  204. **Port bindings**
  205. Port binding is done in two parts: first, provide a list of ports to
  206. open inside the container with the ``ports`` parameter, then declare
  207. bindings with the ``host_config`` parameter. For example:
  208. .. code-block:: python
  209. container_id = client.api.create_container(
  210. 'busybox', 'ls', ports=[1111, 2222],
  211. host_config=client.api.create_host_config(port_bindings={
  212. 1111: 4567,
  213. 2222: None
  214. })
  215. )
  216. You can limit the host address on which the port will be exposed like
  217. such:
  218. .. code-block:: python
  219. client.api.create_host_config(
  220. port_bindings={1111: ('127.0.0.1', 4567)}
  221. )
  222. Or without host port assignment:
  223. .. code-block:: python
  224. client.api.create_host_config(port_bindings={1111: ('127.0.0.1',)})
  225. If you wish to use UDP instead of TCP (default), you need to declare
  226. ports as such in both the config and host config:
  227. .. code-block:: python
  228. container_id = client.api.create_container(
  229. 'busybox', 'ls', ports=[(1111, 'udp'), 2222],
  230. host_config=client.api.create_host_config(port_bindings={
  231. '1111/udp': 4567, 2222: None
  232. })
  233. )
  234. To bind multiple host ports to a single container port, use the
  235. following syntax:
  236. .. code-block:: python
  237. client.api.create_host_config(port_bindings={
  238. 1111: [1234, 4567]
  239. })
  240. You can also bind multiple IPs to a single container port:
  241. .. code-block:: python
  242. client.api.create_host_config(port_bindings={
  243. 1111: [
  244. ('192.168.0.100', 1234),
  245. ('192.168.0.101', 1234)
  246. ]
  247. })
  248. **Using volumes**
  249. Volume declaration is done in two parts. Provide a list of
  250. paths to use as mountpoints inside the container with the
  251. ``volumes`` parameter, and declare mappings from paths on the host
  252. in the ``host_config`` section.
  253. .. code-block:: python
  254. container_id = client.api.create_container(
  255. 'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'],
  256. host_config=client.api.create_host_config(binds={
  257. '/home/user1/': {
  258. 'bind': '/mnt/vol2',
  259. 'mode': 'rw',
  260. },
  261. '/var/www': {
  262. 'bind': '/mnt/vol1',
  263. 'mode': 'ro',
  264. }
  265. })
  266. )
  267. You can alternatively specify binds as a list. This code is equivalent
  268. to the example above:
  269. .. code-block:: python
  270. container_id = client.api.create_container(
  271. 'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'],
  272. host_config=client.api.create_host_config(binds=[
  273. '/home/user1/:/mnt/vol2',
  274. '/var/www:/mnt/vol1:ro',
  275. ])
  276. )
  277. **Networking**
  278. You can specify networks to connect the container to by using the
  279. ``networking_config`` parameter. At the time of creation, you can
  280. only connect a container to a single networking, but you
  281. can create more connections by using
  282. :py:meth:`~connect_container_to_network`.
  283. For example:
  284. .. code-block:: python
  285. networking_config = client.api.create_networking_config({
  286. 'network1': client.api.create_endpoint_config(
  287. ipv4_address='172.28.0.124',
  288. aliases=['foo', 'bar'],
  289. links=['container2']
  290. )
  291. })
  292. ctnr = client.api.create_container(
  293. img, command, networking_config=networking_config
  294. )
  295. Args:
  296. image (str): The image to run
  297. command (str or list): The command to be run in the container
  298. hostname (str): Optional hostname for the container
  299. user (str or int): Username or UID
  300. detach (bool): Detached mode: run container in the background and
  301. return container ID
  302. stdin_open (bool): Keep STDIN open even if not attached
  303. tty (bool): Allocate a pseudo-TTY
  304. ports (list of ints): A list of port numbers
  305. environment (dict or list): A dictionary or a list of strings in
  306. the following format ``["PASSWORD=xxx"]`` or
  307. ``{"PASSWORD": "xxx"}``.
  308. volumes (str or list): List of paths inside the container to use
  309. as volumes.
  310. network_disabled (bool): Disable networking
  311. name (str): A name for the container
  312. entrypoint (str or list): An entrypoint
  313. working_dir (str): Path to the working directory
  314. domainname (str): The domain name to use for the container
  315. host_config (dict): A dictionary created with
  316. :py:meth:`create_host_config`.
  317. mac_address (str): The Mac Address to assign the container
  318. labels (dict or list): A dictionary of name-value labels (e.g.
  319. ``{"label1": "value1", "label2": "value2"}``) or a list of
  320. names of labels to set with empty values (e.g.
  321. ``["label1", "label2"]``)
  322. stop_signal (str): The stop signal to use to stop the container
  323. (e.g. ``SIGINT``).
  324. stop_timeout (int): Timeout to stop the container, in seconds.
  325. Default: 10
  326. networking_config (dict): A networking configuration generated
  327. by :py:meth:`create_networking_config`.
  328. runtime (str): Runtime to use with this container.
  329. healthcheck (dict): Specify a test to perform to check that the
  330. container is healthy.
  331. use_config_proxy (bool): If ``True``, and if the docker client
  332. configuration file (``~/.docker/config.json`` by default)
  333. contains a proxy configuration, the corresponding environment
  334. variables will be set in the container being created.
  335. platform (str): Platform in the format ``os[/arch[/variant]]``.
  336. Returns:
  337. A dictionary with an image 'Id' key and a 'Warnings' key.
  338. Raises:
  339. :py:class:`docker.errors.ImageNotFound`
  340. If the specified image does not exist.
  341. :py:class:`docker.errors.APIError`
  342. If the server returns an error.
  343. """
  344. if isinstance(volumes, str):
  345. volumes = [volumes, ]
  346. if isinstance(environment, dict):
  347. environment = utils.utils.format_environment(environment)
  348. if use_config_proxy:
  349. environment = self._proxy_configs.inject_proxy_environment(
  350. environment
  351. ) or None
  352. config = self.create_container_config(
  353. image, command, hostname, user, detach, stdin_open, tty,
  354. ports, environment, volumes,
  355. network_disabled, entrypoint, working_dir, domainname,
  356. host_config, mac_address, labels,
  357. stop_signal, networking_config, healthcheck,
  358. stop_timeout, runtime
  359. )
  360. return self.create_container_from_config(config, name, platform)
  361. def create_container_config(self, *args, **kwargs):
  362. return ContainerConfig(self._version, *args, **kwargs)
  363. def create_container_from_config(self, config, name=None, platform=None):
  364. u = self._url("/containers/create")
  365. params = {
  366. 'name': name
  367. }
  368. if platform:
  369. if utils.version_lt(self._version, '1.41'):
  370. raise errors.InvalidVersion(
  371. 'platform is not supported for API version < 1.41'
  372. )
  373. params['platform'] = platform
  374. res = self._post_json(u, data=config, params=params)
  375. return self._result(res, True)
  376. def create_host_config(self, *args, **kwargs):
  377. """
  378. Create a dictionary for the ``host_config`` argument to
  379. :py:meth:`create_container`.
  380. Args:
  381. auto_remove (bool): enable auto-removal of the container on daemon
  382. side when the container's process exits.
  383. binds (dict): Volumes to bind. See :py:meth:`create_container`
  384. for more information.
  385. blkio_weight_device: Block IO weight (relative device weight) in
  386. the form of: ``[{"Path": "device_path", "Weight": weight}]``.
  387. blkio_weight: Block IO weight (relative weight), accepts a weight
  388. value between 10 and 1000.
  389. cap_add (list of str): Add kernel capabilities. For example,
  390. ``["SYS_ADMIN", "MKNOD"]``.
  391. cap_drop (list of str): Drop kernel capabilities.
  392. cpu_period (int): The length of a CPU period in microseconds.
  393. cpu_quota (int): Microseconds of CPU time that the container can
  394. get in a CPU period.
  395. cpu_shares (int): CPU shares (relative weight).
  396. cpuset_cpus (str): CPUs in which to allow execution (``0-3``,
  397. ``0,1``).
  398. cpuset_mems (str): Memory nodes (MEMs) in which to allow execution
  399. (``0-3``, ``0,1``). Only effective on NUMA systems.
  400. device_cgroup_rules (:py:class:`list`): A list of cgroup rules to
  401. apply to the container.
  402. device_read_bps: Limit read rate (bytes per second) from a device
  403. in the form of: `[{"Path": "device_path", "Rate": rate}]`
  404. device_read_iops: Limit read rate (IO per second) from a device.
  405. device_write_bps: Limit write rate (bytes per second) from a
  406. device.
  407. device_write_iops: Limit write rate (IO per second) from a device.
  408. devices (:py:class:`list`): Expose host devices to the container,
  409. as a list of strings in the form
  410. ``<path_on_host>:<path_in_container>:<cgroup_permissions>``.
  411. For example, ``/dev/sda:/dev/xvda:rwm`` allows the container
  412. to have read-write access to the host's ``/dev/sda`` via a
  413. node named ``/dev/xvda`` inside the container.
  414. device_requests (:py:class:`list`): Expose host resources such as
  415. GPUs to the container, as a list of
  416. :py:class:`docker.types.DeviceRequest` instances.
  417. dns (:py:class:`list`): Set custom DNS servers.
  418. dns_opt (:py:class:`list`): Additional options to be added to the
  419. container's ``resolv.conf`` file
  420. dns_search (:py:class:`list`): DNS search domains.
  421. extra_hosts (dict): Additional hostnames to resolve inside the
  422. container, as a mapping of hostname to IP address.
  423. group_add (:py:class:`list`): List of additional group names and/or
  424. IDs that the container process will run as.
  425. init (bool): Run an init inside the container that forwards
  426. signals and reaps processes
  427. ipc_mode (str): Set the IPC mode for the container.
  428. isolation (str): Isolation technology to use. Default: ``None``.
  429. links (dict): Mapping of links using the
  430. ``{'container': 'alias'}`` format. The alias is optional.
  431. Containers declared in this dict will be linked to the new
  432. container using the provided alias. Default: ``None``.
  433. log_config (LogConfig): Logging configuration
  434. lxc_conf (dict): LXC config.
  435. mem_limit (float or str): Memory limit. Accepts float values
  436. (which represent the memory limit of the created container in
  437. bytes) or a string with a units identification char
  438. (``100000b``, ``1000k``, ``128m``, ``1g``). If a string is
  439. specified without a units character, bytes are assumed as an
  440. mem_reservation (float or str): Memory soft limit.
  441. mem_swappiness (int): Tune a container's memory swappiness
  442. behavior. Accepts number between 0 and 100.
  443. memswap_limit (str or int): Maximum amount of memory + swap a
  444. container is allowed to consume.
  445. mounts (:py:class:`list`): Specification for mounts to be added to
  446. the container. More powerful alternative to ``binds``. Each
  447. item in the list is expected to be a
  448. :py:class:`docker.types.Mount` object.
  449. network_mode (str): One of:
  450. - ``bridge`` Create a new network stack for the container on
  451. the bridge network.
  452. - ``none`` No networking for this container.
  453. - ``container:<name|id>`` Reuse another container's network
  454. stack.
  455. - ``host`` Use the host network stack.
  456. This mode is incompatible with ``port_bindings``.
  457. oom_kill_disable (bool): Whether to disable OOM killer.
  458. oom_score_adj (int): An integer value containing the score given
  459. to the container in order to tune OOM killer preferences.
  460. pid_mode (str): If set to ``host``, use the host PID namespace
  461. inside the container.
  462. pids_limit (int): Tune a container's pids limit. Set ``-1`` for
  463. unlimited.
  464. port_bindings (dict): See :py:meth:`create_container`
  465. for more information.
  466. Imcompatible with ``host`` in ``network_mode``.
  467. privileged (bool): Give extended privileges to this container.
  468. publish_all_ports (bool): Publish all ports to the host.
  469. read_only (bool): Mount the container's root filesystem as read
  470. only.
  471. restart_policy (dict): Restart the container when it exits.
  472. Configured as a dictionary with keys:
  473. - ``Name`` One of ``on-failure``, or ``always``.
  474. - ``MaximumRetryCount`` Number of times to restart the
  475. container on failure.
  476. security_opt (:py:class:`list`): A list of string values to
  477. customize labels for MLS systems, such as SELinux.
  478. shm_size (str or int): Size of /dev/shm (e.g. ``1G``).
  479. storage_opt (dict): Storage driver options per container as a
  480. key-value mapping.
  481. sysctls (dict): Kernel parameters to set in the container.
  482. tmpfs (dict): Temporary filesystems to mount, as a dictionary
  483. mapping a path inside the container to options for that path.
  484. For example:
  485. .. code-block:: python
  486. {
  487. '/mnt/vol2': '',
  488. '/mnt/vol1': 'size=3G,uid=1000'
  489. }
  490. ulimits (:py:class:`list`): Ulimits to set inside the container,
  491. as a list of :py:class:`docker.types.Ulimit` instances.
  492. userns_mode (str): Sets the user namespace mode for the container
  493. when user namespace remapping option is enabled. Supported
  494. values are: ``host``
  495. uts_mode (str): Sets the UTS namespace mode for the container.
  496. Supported values are: ``host``
  497. volumes_from (:py:class:`list`): List of container names or IDs to
  498. get volumes from.
  499. runtime (str): Runtime to use with this container.
  500. Returns:
  501. (dict) A dictionary which can be passed to the ``host_config``
  502. argument to :py:meth:`create_container`.
  503. Example:
  504. >>> client.api.create_host_config(
  505. ... privileged=True,
  506. ... cap_drop=['MKNOD'],
  507. ... volumes_from=['nostalgic_newton'],
  508. ... )
  509. {'CapDrop': ['MKNOD'], 'LxcConf': None, 'Privileged': True,
  510. 'VolumesFrom': ['nostalgic_newton'], 'PublishAllPorts': False}
  511. """
  512. if not kwargs:
  513. kwargs = {}
  514. if 'version' in kwargs:
  515. raise TypeError(
  516. "create_host_config() got an unexpected "
  517. "keyword argument 'version'"
  518. )
  519. kwargs['version'] = self._version
  520. return HostConfig(*args, **kwargs)
  521. def create_networking_config(self, *args, **kwargs):
  522. """
  523. Create a networking config dictionary to be used as the
  524. ``networking_config`` parameter in :py:meth:`create_container`.
  525. Args:
  526. endpoints_config (dict): A dictionary mapping network names to
  527. endpoint configurations generated by
  528. :py:meth:`create_endpoint_config`.
  529. Returns:
  530. (dict) A networking config.
  531. Example:
  532. >>> client.api.create_network('network1')
  533. >>> networking_config = client.api.create_networking_config({
  534. 'network1': client.api.create_endpoint_config()
  535. })
  536. >>> container = client.api.create_container(
  537. img, command, networking_config=networking_config
  538. )
  539. """
  540. return NetworkingConfig(*args, **kwargs)
  541. def create_endpoint_config(self, *args, **kwargs):
  542. """
  543. Create an endpoint config dictionary to be used with
  544. :py:meth:`create_networking_config`.
  545. Args:
  546. aliases (:py:class:`list`): A list of aliases for this endpoint.
  547. Names in that list can be used within the network to reach the
  548. container. Defaults to ``None``.
  549. links (dict): Mapping of links for this endpoint using the
  550. ``{'container': 'alias'}`` format. The alias is optional.
  551. Containers declared in this dict will be linked to this
  552. container using the provided alias. Defaults to ``None``.
  553. ipv4_address (str): The IP address of this container on the
  554. network, using the IPv4 protocol. Defaults to ``None``.
  555. ipv6_address (str): The IP address of this container on the
  556. network, using the IPv6 protocol. Defaults to ``None``.
  557. link_local_ips (:py:class:`list`): A list of link-local (IPv4/IPv6)
  558. addresses.
  559. driver_opt (dict): A dictionary of options to provide to the
  560. network driver. Defaults to ``None``.
  561. Returns:
  562. (dict) An endpoint config.
  563. Example:
  564. >>> endpoint_config = client.api.create_endpoint_config(
  565. aliases=['web', 'app'],
  566. links={'app_db': 'db', 'another': None},
  567. ipv4_address='132.65.0.123'
  568. )
  569. """
  570. return EndpointConfig(self._version, *args, **kwargs)
  571. @utils.check_resource('container')
  572. def diff(self, container):
  573. """
  574. Inspect changes on a container's filesystem.
  575. Args:
  576. container (str): The container to diff
  577. Returns:
  578. (list) A list of dictionaries containing the attributes `Path`
  579. and `Kind`.
  580. Raises:
  581. :py:class:`docker.errors.APIError`
  582. If the server returns an error.
  583. """
  584. return self._result(
  585. self._get(self._url("/containers/{0}/changes", container)), True
  586. )
  587. @utils.check_resource('container')
  588. def export(self, container, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
  589. """
  590. Export the contents of a filesystem as a tar archive.
  591. Args:
  592. container (str): The container to export
  593. chunk_size (int): The number of bytes returned by each iteration
  594. of the generator. If ``None``, data will be streamed as it is
  595. received. Default: 2 MB
  596. Returns:
  597. (generator): The archived filesystem data stream
  598. Raises:
  599. :py:class:`docker.errors.APIError`
  600. If the server returns an error.
  601. """
  602. res = self._get(
  603. self._url("/containers/{0}/export", container), stream=True
  604. )
  605. return self._stream_raw_result(res, chunk_size, False)
  606. @utils.check_resource('container')
  607. def get_archive(self, container, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE,
  608. encode_stream=False):
  609. """
  610. Retrieve a file or folder from a container in the form of a tar
  611. archive.
  612. Args:
  613. container (str): The container where the file is located
  614. path (str): Path to the file or folder to retrieve
  615. chunk_size (int): The number of bytes returned by each iteration
  616. of the generator. If ``None``, data will be streamed as it is
  617. received. Default: 2 MB
  618. encode_stream (bool): Determines if data should be encoded
  619. (gzip-compressed) during transmission. Default: False
  620. Returns:
  621. (tuple): First element is a raw tar data stream. Second element is
  622. a dict containing ``stat`` information on the specified ``path``.
  623. Raises:
  624. :py:class:`docker.errors.APIError`
  625. If the server returns an error.
  626. Example:
  627. >>> c = docker.APIClient()
  628. >>> f = open('./sh_bin.tar', 'wb')
  629. >>> bits, stat = c.api.get_archive(container, '/bin/sh')
  630. >>> print(stat)
  631. {'name': 'sh', 'size': 1075464, 'mode': 493,
  632. 'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''}
  633. >>> for chunk in bits:
  634. ... f.write(chunk)
  635. >>> f.close()
  636. """
  637. params = {
  638. 'path': path
  639. }
  640. headers = {
  641. "Accept-Encoding": "gzip, deflate"
  642. } if encode_stream else {
  643. "Accept-Encoding": "identity"
  644. }
  645. url = self._url('/containers/{0}/archive', container)
  646. res = self._get(url, params=params, stream=True, headers=headers)
  647. self._raise_for_status(res)
  648. encoded_stat = res.headers.get('x-docker-container-path-stat')
  649. return (
  650. self._stream_raw_result(res, chunk_size, False),
  651. utils.decode_json_header(encoded_stat) if encoded_stat else None
  652. )
  653. @utils.check_resource('container')
  654. def inspect_container(self, container):
  655. """
  656. Identical to the `docker inspect` command, but only for containers.
  657. Args:
  658. container (str): The container to inspect
  659. Returns:
  660. (dict): Similar to the output of `docker inspect`, but as a
  661. single dict
  662. Raises:
  663. :py:class:`docker.errors.APIError`
  664. If the server returns an error.
  665. """
  666. return self._result(
  667. self._get(self._url("/containers/{0}/json", container)), True
  668. )
  669. @utils.check_resource('container')
  670. def kill(self, container, signal=None):
  671. """
  672. Kill a container or send a signal to a container.
  673. Args:
  674. container (str): The container to kill
  675. signal (str or int): The signal to send. Defaults to ``SIGKILL``
  676. Raises:
  677. :py:class:`docker.errors.APIError`
  678. If the server returns an error.
  679. """
  680. url = self._url("/containers/{0}/kill", container)
  681. params = {}
  682. if signal is not None:
  683. if not isinstance(signal, str):
  684. signal = int(signal)
  685. params['signal'] = signal
  686. res = self._post(url, params=params)
  687. self._raise_for_status(res)
  688. @utils.check_resource('container')
  689. def logs(self, container, stdout=True, stderr=True, stream=False,
  690. timestamps=False, tail='all', since=None, follow=None,
  691. until=None):
  692. """
  693. Get logs from a container. Similar to the ``docker logs`` command.
  694. The ``stream`` parameter makes the ``logs`` function return a blocking
  695. generator you can iterate over to retrieve log output as it happens.
  696. Args:
  697. container (str): The container to get logs from
  698. stdout (bool): Get ``STDOUT``. Default ``True``
  699. stderr (bool): Get ``STDERR``. Default ``True``
  700. stream (bool): Stream the response. Default ``False``
  701. timestamps (bool): Show timestamps. Default ``False``
  702. tail (str or int): Output specified number of lines at the end of
  703. logs. Either an integer of number of lines or the string
  704. ``all``. Default ``all``
  705. since (datetime, int, or float): Show logs since a given datetime,
  706. integer epoch (in seconds) or float (in fractional seconds)
  707. follow (bool): Follow log output. Default ``False``
  708. until (datetime, int, or float): Show logs that occurred before
  709. the given datetime, integer epoch (in seconds), or
  710. float (in fractional seconds)
  711. Returns:
  712. (generator or str)
  713. Raises:
  714. :py:class:`docker.errors.APIError`
  715. If the server returns an error.
  716. """
  717. if follow is None:
  718. follow = stream
  719. params = {'stderr': stderr and 1 or 0,
  720. 'stdout': stdout and 1 or 0,
  721. 'timestamps': timestamps and 1 or 0,
  722. 'follow': follow and 1 or 0,
  723. }
  724. if tail != 'all' and (not isinstance(tail, int) or tail < 0):
  725. tail = 'all'
  726. params['tail'] = tail
  727. if since is not None:
  728. if isinstance(since, datetime):
  729. params['since'] = utils.datetime_to_timestamp(since)
  730. elif (isinstance(since, int) and since > 0):
  731. params['since'] = since
  732. elif (isinstance(since, float) and since > 0.0):
  733. params['since'] = since
  734. else:
  735. raise errors.InvalidArgument(
  736. 'since value should be datetime or positive int/float, '
  737. 'not {}'.format(type(since))
  738. )
  739. if until is not None:
  740. if utils.version_lt(self._version, '1.35'):
  741. raise errors.InvalidVersion(
  742. 'until is not supported for API version < 1.35'
  743. )
  744. if isinstance(until, datetime):
  745. params['until'] = utils.datetime_to_timestamp(until)
  746. elif (isinstance(until, int) and until > 0):
  747. params['until'] = until
  748. elif (isinstance(until, float) and until > 0.0):
  749. params['until'] = until
  750. else:
  751. raise errors.InvalidArgument(
  752. 'until value should be datetime or positive int/float, '
  753. 'not {}'.format(type(until))
  754. )
  755. url = self._url("/containers/{0}/logs", container)
  756. res = self._get(url, params=params, stream=stream)
  757. output = self._get_result(container, stream, res)
  758. if stream:
  759. return CancellableStream(output, res)
  760. else:
  761. return output
  762. @utils.check_resource('container')
  763. def pause(self, container):
  764. """
  765. Pauses all processes within a container.
  766. Args:
  767. container (str): The container to pause
  768. Raises:
  769. :py:class:`docker.errors.APIError`
  770. If the server returns an error.
  771. """
  772. url = self._url('/containers/{0}/pause', container)
  773. res = self._post(url)
  774. self._raise_for_status(res)
  775. @utils.check_resource('container')
  776. def port(self, container, private_port):
  777. """
  778. Lookup the public-facing port that is NAT-ed to ``private_port``.
  779. Identical to the ``docker port`` command.
  780. Args:
  781. container (str): The container to look up
  782. private_port (int): The private port to inspect
  783. Returns:
  784. (list of dict): The mapping for the host ports
  785. Raises:
  786. :py:class:`docker.errors.APIError`
  787. If the server returns an error.
  788. Example:
  789. .. code-block:: bash
  790. $ docker run -d -p 80:80 ubuntu:14.04 /bin/sleep 30
  791. 7174d6347063a83f412fad6124c99cffd25ffe1a0807eb4b7f9cec76ac8cb43b
  792. .. code-block:: python
  793. >>> client.api.port('7174d6347063', 80)
  794. [{'HostIp': '0.0.0.0', 'HostPort': '80'}]
  795. """
  796. res = self._get(self._url("/containers/{0}/json", container))
  797. self._raise_for_status(res)
  798. json_ = res.json()
  799. private_port = str(private_port)
  800. h_ports = None
  801. # Port settings is None when the container is running with
  802. # network_mode=host.
  803. port_settings = json_.get('NetworkSettings', {}).get('Ports')
  804. if port_settings is None:
  805. return None
  806. if '/' in private_port:
  807. return port_settings.get(private_port)
  808. for protocol in ['tcp', 'udp', 'sctp']:
  809. h_ports = port_settings.get(private_port + '/' + protocol)
  810. if h_ports:
  811. break
  812. return h_ports
  813. @utils.check_resource('container')
  814. def put_archive(self, container, path, data):
  815. """
  816. Insert a file or folder in an existing container using a tar archive as
  817. source.
  818. Args:
  819. container (str): The container where the file(s) will be extracted
  820. path (str): Path inside the container where the file(s) will be
  821. extracted. Must exist.
  822. data (bytes or stream): tar data to be extracted
  823. Returns:
  824. (bool): True if the call succeeds.
  825. Raises:
  826. :py:class:`docker.errors.APIError`
  827. If the server returns an error.
  828. """
  829. params = {'path': path}
  830. url = self._url('/containers/{0}/archive', container)
  831. res = self._put(url, params=params, data=data)
  832. self._raise_for_status(res)
  833. return res.status_code == 200
  834. @utils.minimum_version('1.25')
  835. def prune_containers(self, filters=None):
  836. """
  837. Delete stopped containers
  838. Args:
  839. filters (dict): Filters to process on the prune list.
  840. Returns:
  841. (dict): A dict containing a list of deleted container IDs and
  842. the amount of disk space reclaimed in bytes.
  843. Raises:
  844. :py:class:`docker.errors.APIError`
  845. If the server returns an error.
  846. """
  847. params = {}
  848. if filters:
  849. params['filters'] = utils.convert_filters(filters)
  850. url = self._url('/containers/prune')
  851. return self._result(self._post(url, params=params), True)
  852. @utils.check_resource('container')
  853. def remove_container(self, container, v=False, link=False, force=False):
  854. """
  855. Remove a container. Similar to the ``docker rm`` command.
  856. Args:
  857. container (str): The container to remove
  858. v (bool): Remove the volumes associated with the container
  859. link (bool): Remove the specified link and not the underlying
  860. container
  861. force (bool): Force the removal of a running container (uses
  862. ``SIGKILL``)
  863. Raises:
  864. :py:class:`docker.errors.APIError`
  865. If the server returns an error.
  866. """
  867. params = {'v': v, 'link': link, 'force': force}
  868. res = self._delete(
  869. self._url("/containers/{0}", container), params=params
  870. )
  871. self._raise_for_status(res)
  872. @utils.check_resource('container')
  873. def rename(self, container, name):
  874. """
  875. Rename a container. Similar to the ``docker rename`` command.
  876. Args:
  877. container (str): ID of the container to rename
  878. name (str): New name for the container
  879. Raises:
  880. :py:class:`docker.errors.APIError`
  881. If the server returns an error.
  882. """
  883. url = self._url("/containers/{0}/rename", container)
  884. params = {'name': name}
  885. res = self._post(url, params=params)
  886. self._raise_for_status(res)
  887. @utils.check_resource('container')
  888. def resize(self, container, height, width):
  889. """
  890. Resize the tty session.
  891. Args:
  892. container (str or dict): The container to resize
  893. height (int): Height of tty session
  894. width (int): Width of tty session
  895. Raises:
  896. :py:class:`docker.errors.APIError`
  897. If the server returns an error.
  898. """
  899. params = {'h': height, 'w': width}
  900. url = self._url("/containers/{0}/resize", container)
  901. res = self._post(url, params=params)
  902. self._raise_for_status(res)
  903. @utils.check_resource('container')
  904. def restart(self, container, timeout=10):
  905. """
  906. Restart a container. Similar to the ``docker restart`` command.
  907. Args:
  908. container (str or dict): The container to restart. If a dict, the
  909. ``Id`` key is used.
  910. timeout (int): Number of seconds to try to stop for before killing
  911. the container. Once killed it will then be restarted. Default
  912. is 10 seconds.
  913. Raises:
  914. :py:class:`docker.errors.APIError`
  915. If the server returns an error.
  916. """
  917. params = {'t': timeout}
  918. url = self._url("/containers/{0}/restart", container)
  919. conn_timeout = self.timeout
  920. if conn_timeout is not None:
  921. conn_timeout += timeout
  922. res = self._post(url, params=params, timeout=conn_timeout)
  923. self._raise_for_status(res)
  924. @utils.check_resource('container')
  925. def start(self, container, *args, **kwargs):
  926. """
  927. Start a container. Similar to the ``docker start`` command, but
  928. doesn't support attach options.
  929. **Deprecation warning:** Passing configuration options in ``start`` is
  930. no longer supported. Users are expected to provide host config options
  931. in the ``host_config`` parameter of
  932. :py:meth:`~ContainerApiMixin.create_container`.
  933. Args:
  934. container (str): The container to start
  935. Raises:
  936. :py:class:`docker.errors.APIError`
  937. If the server returns an error.
  938. :py:class:`docker.errors.DeprecatedMethod`
  939. If any argument besides ``container`` are provided.
  940. Example:
  941. >>> container = client.api.create_container(
  942. ... image='busybox:latest',
  943. ... command='/bin/sleep 30')
  944. >>> client.api.start(container=container.get('Id'))
  945. """
  946. if args or kwargs:
  947. raise errors.DeprecatedMethod(
  948. 'Providing configuration in the start() method is no longer '
  949. 'supported. Use the host_config param in create_container '
  950. 'instead.'
  951. )
  952. url = self._url("/containers/{0}/start", container)
  953. res = self._post(url)
  954. self._raise_for_status(res)
  955. @utils.check_resource('container')
  956. def stats(self, container, decode=None, stream=True, one_shot=None):
  957. """
  958. Stream statistics for a specific container. Similar to the
  959. ``docker stats`` command.
  960. Args:
  961. container (str): The container to stream statistics from
  962. decode (bool): If set to true, stream will be decoded into dicts
  963. on the fly. Only applicable if ``stream`` is True.
  964. False by default.
  965. stream (bool): If set to false, only the current stats will be
  966. returned instead of a stream. True by default.
  967. one_shot (bool): If set to true, Only get a single stat instead of
  968. waiting for 2 cycles. Must be used with stream=false. False by
  969. default.
  970. Raises:
  971. :py:class:`docker.errors.APIError`
  972. If the server returns an error.
  973. """
  974. url = self._url("/containers/{0}/stats", container)
  975. params = {
  976. 'stream': stream
  977. }
  978. if one_shot is not None:
  979. if utils.version_lt(self._version, '1.41'):
  980. raise errors.InvalidVersion(
  981. 'one_shot is not supported for API version < 1.41'
  982. )
  983. params['one-shot'] = one_shot
  984. if stream:
  985. if one_shot:
  986. raise errors.InvalidArgument(
  987. 'one_shot is only available in conjunction with '
  988. 'stream=False'
  989. )
  990. return self._stream_helper(
  991. self._get(url, stream=True, params=params), decode=decode
  992. )
  993. else:
  994. if decode:
  995. raise errors.InvalidArgument(
  996. "decode is only available in conjunction with stream=True"
  997. )
  998. return self._result(self._get(url, params=params), json=True)
  999. @utils.check_resource('container')
  1000. def stop(self, container, timeout=None):
  1001. """
  1002. Stops a container. Similar to the ``docker stop`` command.
  1003. Args:
  1004. container (str): The container to stop
  1005. timeout (int): Timeout in seconds to wait for the container to
  1006. stop before sending a ``SIGKILL``. If None, then the
  1007. StopTimeout value of the container will be used.
  1008. Default: None
  1009. Raises:
  1010. :py:class:`docker.errors.APIError`
  1011. If the server returns an error.
  1012. """
  1013. if timeout is None:
  1014. params = {}
  1015. timeout = 10
  1016. else:
  1017. params = {'t': timeout}
  1018. url = self._url("/containers/{0}/stop", container)
  1019. conn_timeout = self.timeout
  1020. if conn_timeout is not None:
  1021. conn_timeout += timeout
  1022. res = self._post(url, params=params, timeout=conn_timeout)
  1023. self._raise_for_status(res)
  1024. @utils.check_resource('container')
  1025. def top(self, container, ps_args=None):
  1026. """
  1027. Display the running processes of a container.
  1028. Args:
  1029. container (str): The container to inspect
  1030. ps_args (str): An optional arguments passed to ps (e.g. ``aux``)
  1031. Returns:
  1032. (str): The output of the top
  1033. Raises:
  1034. :py:class:`docker.errors.APIError`
  1035. If the server returns an error.
  1036. """
  1037. u = self._url("/containers/{0}/top", container)
  1038. params = {}
  1039. if ps_args is not None:
  1040. params['ps_args'] = ps_args
  1041. return self._result(self._get(u, params=params), True)
  1042. @utils.check_resource('container')
  1043. def unpause(self, container):
  1044. """
  1045. Unpause all processes within a container.
  1046. Args:
  1047. container (str): The container to unpause
  1048. """
  1049. url = self._url('/containers/{0}/unpause', container)
  1050. res = self._post(url)
  1051. self._raise_for_status(res)
  1052. @utils.minimum_version('1.22')
  1053. @utils.check_resource('container')
  1054. def update_container(
  1055. self, container, blkio_weight=None, cpu_period=None, cpu_quota=None,
  1056. cpu_shares=None, cpuset_cpus=None, cpuset_mems=None, mem_limit=None,
  1057. mem_reservation=None, memswap_limit=None, kernel_memory=None,
  1058. restart_policy=None
  1059. ):
  1060. """
  1061. Update resource configs of one or more containers.
  1062. Args:
  1063. container (str): The container to inspect
  1064. blkio_weight (int): Block IO (relative weight), between 10 and 1000
  1065. cpu_period (int): Limit CPU CFS (Completely Fair Scheduler) period
  1066. cpu_quota (int): Limit CPU CFS (Completely Fair Scheduler) quota
  1067. cpu_shares (int): CPU shares (relative weight)
  1068. cpuset_cpus (str): CPUs in which to allow execution
  1069. cpuset_mems (str): MEMs in which to allow execution
  1070. mem_limit (float or str): Memory limit
  1071. mem_reservation (float or str): Memory soft limit
  1072. memswap_limit (int or str): Total memory (memory + swap), -1 to
  1073. disable swap
  1074. kernel_memory (int or str): Kernel memory limit
  1075. restart_policy (dict): Restart policy dictionary
  1076. Returns:
  1077. (dict): Dictionary containing a ``Warnings`` key.
  1078. Raises:
  1079. :py:class:`docker.errors.APIError`
  1080. If the server returns an error.
  1081. """
  1082. url = self._url('/containers/{0}/update', container)
  1083. data = {}
  1084. if blkio_weight:
  1085. data['BlkioWeight'] = blkio_weight
  1086. if cpu_period:
  1087. data['CpuPeriod'] = cpu_period
  1088. if cpu_shares:
  1089. data['CpuShares'] = cpu_shares
  1090. if cpu_quota:
  1091. data['CpuQuota'] = cpu_quota
  1092. if cpuset_cpus:
  1093. data['CpusetCpus'] = cpuset_cpus
  1094. if cpuset_mems:
  1095. data['CpusetMems'] = cpuset_mems
  1096. if mem_limit:
  1097. data['Memory'] = utils.parse_bytes(mem_limit)
  1098. if mem_reservation:
  1099. data['MemoryReservation'] = utils.parse_bytes(mem_reservation)
  1100. if memswap_limit:
  1101. data['MemorySwap'] = utils.parse_bytes(memswap_limit)
  1102. if kernel_memory:
  1103. data['KernelMemory'] = utils.parse_bytes(kernel_memory)
  1104. if restart_policy:
  1105. if utils.version_lt(self._version, '1.23'):
  1106. raise errors.InvalidVersion(
  1107. 'restart policy update is not supported '
  1108. 'for API version < 1.23'
  1109. )
  1110. data['RestartPolicy'] = restart_policy
  1111. res = self._post_json(url, data=data)
  1112. return self._result(res, True)
  1113. @utils.check_resource('container')
  1114. def wait(self, container, timeout=None, condition=None):
  1115. """
  1116. Block until a container stops, then return its exit code. Similar to
  1117. the ``docker wait`` command.
  1118. Args:
  1119. container (str or dict): The container to wait on. If a dict, the
  1120. ``Id`` key is used.
  1121. timeout (int): Request timeout
  1122. condition (str): Wait until a container state reaches the given
  1123. condition, either ``not-running`` (default), ``next-exit``,
  1124. or ``removed``
  1125. Returns:
  1126. (dict): The API's response as a Python dictionary, including
  1127. the container's exit code under the ``StatusCode`` attribute.
  1128. Raises:
  1129. :py:class:`requests.exceptions.ReadTimeout`
  1130. If the timeout is exceeded.
  1131. :py:class:`docker.errors.APIError`
  1132. If the server returns an error.
  1133. """
  1134. url = self._url("/containers/{0}/wait", container)
  1135. params = {}
  1136. if condition is not None:
  1137. if utils.version_lt(self._version, '1.30'):
  1138. raise errors.InvalidVersion(
  1139. 'wait condition is not supported for API version < 1.30'
  1140. )
  1141. params['condition'] = condition
  1142. res = self._post(url, timeout=timeout, params=params)
  1143. return self._result(res, True)