Kaydet (Commit) f9b04c10 authored tarafından Joffrey F's avatar Joffrey F

Improve host devices support

Add unit tests for utils.parse_devices
Rewrite documentation
Support dict and string format for device declaration
Signed-off-by: 's avatarJoffrey F <joffrey@docker.com>
üst 2f2d50d0
...@@ -4,7 +4,7 @@ from .utils import ( ...@@ -4,7 +4,7 @@ from .utils import (
kwargs_from_env, convert_filters, datetime_to_timestamp, create_host_config, kwargs_from_env, convert_filters, datetime_to_timestamp, create_host_config,
create_container_config, parse_bytes, ping_registry, parse_env_file, create_container_config, parse_bytes, ping_registry, parse_env_file,
version_lt, version_gte, decode_json_header, split_command, version_lt, version_gte, decode_json_header, split_command,
create_ipam_config, create_ipam_pool, create_ipam_config, create_ipam_pool, parse_devices
) # flake8: noqa ) # flake8: noqa
from .types import Ulimit, LogConfig # flake8: noqa from .types import Ulimit, LogConfig # flake8: noqa
......
...@@ -400,7 +400,7 @@ def parse_host(addr, platform=None): ...@@ -400,7 +400,7 @@ def parse_host(addr, platform=None):
port = int(port) port = int(port)
except Exception: except Exception:
raise errors.DockerException( raise errors.DockerException(
"Invalid port: %s", addr "Invalid port: {0}".format(addr)
) )
elif proto in ("http", "https") and ':' not in addr: elif proto in ("http", "https") and ':' not in addr:
...@@ -417,7 +417,14 @@ def parse_host(addr, platform=None): ...@@ -417,7 +417,14 @@ def parse_host(addr, platform=None):
def parse_devices(devices): def parse_devices(devices):
device_list = [] device_list = []
for device in devices: for device in devices:
device_mapping = device.split(":") if isinstance(device, dict):
device_list.append(device)
continue
if not isinstance(device, six.string_types):
raise errors.DockerException(
'Invalid device type {0}'.format(type(device))
)
device_mapping = device.split(':')
if device_mapping: if device_mapping:
path_on_host = device_mapping[0] path_on_host = device_mapping[0]
if len(device_mapping) > 1: if len(device_mapping) > 1:
...@@ -428,9 +435,11 @@ def parse_devices(devices): ...@@ -428,9 +435,11 @@ def parse_devices(devices):
permissions = device_mapping[2] permissions = device_mapping[2]
else: else:
permissions = 'rwm' permissions = 'rwm'
device_list.append({"PathOnHost": path_on_host, device_list.append({
"PathInContainer": path_in_container, 'PathOnHost': path_on_host,
"CgroupPermissions": permissions}) 'PathInContainer': path_in_container,
'CgroupPermissions': permissions
})
return device_list return device_list
......
...@@ -12,7 +12,18 @@ cli.create_container( ...@@ -12,7 +12,18 @@ cli.create_container(
) )
``` ```
Each string is a single mapping using the colon (':') as the separator. So the Each string is a single mapping using the following format:
above example essentially allow the container to have read write permissions to `<path_on_host>:<path_in_container>:<cgroup_permissions>`
access the host's /dev/sda via a node named /dev/xvda in the container. The The above example allows the container to have read-write access to
devices parameter is a list to allow multiple devices to be mapped. the host's `/dev/sda` via a node named `/dev/xvda` inside the container.
As a more verbose alternative, each host device definition can be specified as
a dictionary with the following keys:
```python
{
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/xvda',
'CgroupPermissions': 'rwm'
}
```
...@@ -104,17 +104,12 @@ for example: ...@@ -104,17 +104,12 @@ for example:
* mem_swappiness (int): Tune a container's memory swappiness behavior. * mem_swappiness (int): Tune a container's memory swappiness behavior.
Accepts number between 0 and 100. Accepts number between 0 and 100.
* cpu_group (int): The length of a CPU period in microseconds. * cpu_group (int): The length of a CPU period in microseconds.
* cpu_period (int): Microseconds of CPU time that the container can get in a CPU period. * cpu_period (int): Microseconds of CPU time that the container can get in a
CPU period.
* group_add (list): List of additional group names and/or IDs that the * group_add (list): List of additional group names and/or IDs that the
container process will run as. container process will run as.
* devices (list): A list of devices to add to the container specified as dicts * devices (list): Host device bindings. See [host devices](host-devices.md)
in the form: for more information.
```
{ "PathOnHost": "/dev/deviceName",
"PathInContainer": "/dev/deviceName",
"CgroupPermissions": "mrw"
}
```
**Returns** (dict) HostConfig dictionary **Returns** (dict) HostConfig dictionary
......
...@@ -18,7 +18,7 @@ from docker.utils import ( ...@@ -18,7 +18,7 @@ from docker.utils import (
parse_repository_tag, parse_host, convert_filters, kwargs_from_env, parse_repository_tag, parse_host, convert_filters, kwargs_from_env,
create_host_config, Ulimit, LogConfig, parse_bytes, parse_env_file, create_host_config, Ulimit, LogConfig, parse_bytes, parse_env_file,
exclude_paths, convert_volume_binds, decode_json_header, tar, exclude_paths, convert_volume_binds, decode_json_header, tar,
split_command, create_ipam_config, create_ipam_pool, split_command, create_ipam_config, create_ipam_pool, parse_devices,
) )
from docker.utils.utils import create_endpoint_config from docker.utils.utils import create_endpoint_config
from docker.utils.ports import build_port_bindings, split_port from docker.utils.ports import build_port_bindings, split_port
...@@ -406,6 +406,65 @@ class ParseRepositoryTagTest(base.BaseTestCase): ...@@ -406,6 +406,65 @@ class ParseRepositoryTagTest(base.BaseTestCase):
) )
class ParseDeviceTest(base.BaseTestCase):
def test_dict(self):
devices = parse_devices([{
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
}])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
})
def test_partial_string_definition(self):
devices = parse_devices(['/dev/sda1'])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/sda1',
'CgroupPermissions': 'rwm'
})
def test_permissionless_string_definition(self):
devices = parse_devices(['/dev/sda1:/dev/mnt1'])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'rwm'
})
def test_full_string_definition(self):
devices = parse_devices(['/dev/sda1:/dev/mnt1:r'])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
})
def test_hybrid_list(self):
devices = parse_devices([
'/dev/sda1:/dev/mnt1:rw',
{
'PathOnHost': '/dev/sda2',
'PathInContainer': '/dev/mnt2',
'CgroupPermissions': 'r'
}
])
self.assertEqual(devices[0], {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'rw'
})
self.assertEqual(devices[1], {
'PathOnHost': '/dev/sda2',
'PathInContainer': '/dev/mnt2',
'CgroupPermissions': 'r'
})
class UtilsTest(base.BaseTestCase): class UtilsTest(base.BaseTestCase):
longMessage = True longMessage = True
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment