Skip to content

Commit 7a917cc

Browse files
author
Davanum Srinivas
committed
Ability to specify Host Devices during container start
The command line and daemon started supporting --device parameter during docker start a while ago in the following commit: moby/moby@e855c4b Since the command line looks like this, --device=[] Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc) This patch allows a list of strings to be passed into the start() method and we parse out the 3 components just like in the above mentioned commit
1 parent b93fca6 commit 7a917cc

File tree

4 files changed

+76
-1
lines changed

4 files changed

+76
-1
lines changed

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,23 @@ c.start(container_id, binds={
385385
})
386386
```
387387

388+
389+
Access to devices on the host
390+
=============================
391+
392+
If you need to directly expose some host devices to a container, you can use
393+
the devices parameter in the `Client.start` method as shown below
394+
395+
```python
396+
c.start(container_id, devices=['/dev/sda:/dev/xvda:rwm'])
397+
```
398+
399+
Each string is a single mapping using the colon (':') as the separator. So the
400+
above example essentially allow the container to have read write permissions to
401+
access the host's /dev/sda via a node named /dev/xvda in the container. The
402+
devices parameter is a list to allow multiple devices to be mapped.
403+
404+
388405
Connection to daemon using HTTPS
389406
================================
390407

docker/client.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ def search(self, term):
827827
def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
828828
publish_all_ports=False, links=None, privileged=False,
829829
dns=None, dns_search=None, volumes_from=None, network_mode=None,
830-
restart_policy=None, cap_add=None, cap_drop=None):
830+
restart_policy=None, cap_add=None, cap_drop=None, devices=None):
831831
if isinstance(container, dict):
832832
container = container.get('Id')
833833

@@ -895,6 +895,9 @@ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
895895
if cap_drop:
896896
start_config['CapDrop'] = cap_drop
897897

898+
if devices:
899+
start_config['Devices'] = utils.parse_devices(devices)
900+
898901
url = self._url("/containers/{0}/start".format(container))
899902
res = self._post_json(url, data=start_config)
900903
self._raise_for_status(res)

docker/utils/utils.py

+20
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,23 @@ def parse_host(addr):
233233
if proto == "http+unix":
234234
return "%s://%s" % (proto, host)
235235
return "%s://%s:%d" % (proto, host, port)
236+
237+
238+
def parse_devices(devices):
239+
device_list = []
240+
for device in devices:
241+
device_mapping = device.split(",")
242+
if device_mapping:
243+
path_on_host = device_mapping[0]
244+
if len(device_mapping) > 1:
245+
path_in_container = device_mapping[1]
246+
else:
247+
path_in_container = path_on_host
248+
if len(device_mapping) > 2:
249+
permissions = device_mapping[2]
250+
else:
251+
permissions = 'rwm'
252+
device_list.append({"PathOnHost": path_on_host,
253+
"PathInContainer": path_in_container,
254+
"CgroupPermissions": permissions})
255+
return device_list

tests/test.py

+35
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,41 @@ def test_start_container_with_dropped_capabilities(self):
891891
docker.client.DEFAULT_TIMEOUT_SECONDS
892892
)
893893

894+
def test_start_container_with_devices(self):
895+
try:
896+
self.client.start(fake_api.FAKE_CONTAINER_ID,
897+
devices=['/dev/sda:/dev/xvda:rwm',
898+
'/dev/sdb:/dev/xvdb',
899+
'/dev/sdc'])
900+
except Exception as e:
901+
self.fail('Command should not raise exception: {0}'.format(e))
902+
args = fake_request.call_args
903+
self.assertEqual(
904+
args[0][0],
905+
url_prefix + 'containers/3cc2351ab11b/start'
906+
)
907+
self.assertEqual(
908+
json.loads(args[1]['data']),
909+
{"PublishAllPorts": False, "Privileged": False,
910+
"Devices": [{'CgroupPermissions': 'rwm',
911+
'PathInContainer': '/dev/sda:/dev/xvda:rwm',
912+
'PathOnHost': '/dev/sda:/dev/xvda:rwm'},
913+
{'CgroupPermissions': 'rwm',
914+
'PathInContainer': '/dev/sdb:/dev/xvdb',
915+
'PathOnHost': '/dev/sdb:/dev/xvdb'},
916+
{'CgroupPermissions': 'rwm',
917+
'PathInContainer': '/dev/sdc',
918+
'PathOnHost': '/dev/sdc'}]}
919+
)
920+
self.assertEqual(
921+
args[1]['headers'],
922+
{'Content-Type': 'application/json'}
923+
)
924+
self.assertEqual(
925+
args[1]['timeout'],
926+
docker.client.DEFAULT_TIMEOUT_SECONDS
927+
)
928+
894929
def test_resize_container(self):
895930
try:
896931
self.client.resize(

0 commit comments

Comments
 (0)