Skip to content

Commit 1df3226

Browse files
committed
Implement methods for managing networks
Signed-off-by: Aanand Prasad <[email protected]>
1 parent 1c97c69 commit 1df3226

File tree

5 files changed

+290
-1
lines changed

5 files changed

+290
-1
lines changed

docker/api/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
from .exec_api import ExecApiMixin
66
from .image import ImageApiMixin
77
from .volume import VolumeApiMixin
8+
from .network import NetworkApiMixin

docker/api/network.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import json
2+
3+
from ..utils import check_resource
4+
5+
6+
class NetworkApiMixin(object):
7+
def networks(self, names=None, ids=None):
8+
filters = {}
9+
if names:
10+
filters['name'] = names
11+
if ids:
12+
filters['id'] = ids
13+
14+
params = {'filters': json.dumps(filters)}
15+
16+
url = self._url("/networks")
17+
res = self._get(url, params=params)
18+
return self._result(res, json=True)
19+
20+
def create_network(self, name, driver=None):
21+
data = {
22+
'name': name,
23+
'driver': driver,
24+
}
25+
url = self._url("/networks/create")
26+
res = self._post_json(url, data=data)
27+
return self._result(res, json=True)
28+
29+
def remove_network(self, net_id):
30+
url = self._url("/networks/{0}", net_id)
31+
res = self._delete(url)
32+
self._raise_for_status(res)
33+
34+
def inspect_network(self, net_id):
35+
url = self._url("/networks/{0}", net_id)
36+
res = self._get(url)
37+
return self._result(res, json=True)
38+
39+
@check_resource
40+
def connect_container_to_network(self, container, net_id):
41+
data = {"container": container}
42+
url = self._url("/networks/{0}/connect", net_id)
43+
self._post_json(url, data=data)
44+
45+
@check_resource
46+
def disconnect_container_from_network(self, container, net_id):
47+
data = {"container": container}
48+
url = self._url("/networks/{0}/disconnect", net_id)
49+
self._post_json(url, data=data)

docker/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class Client(
3939
api.DaemonApiMixin,
4040
api.ExecApiMixin,
4141
api.ImageApiMixin,
42-
api.VolumeApiMixin):
42+
api.VolumeApiMixin,
43+
api.NetworkApiMixin):
4344
def __init__(self, base_url=None, version=None,
4445
timeout=constants.DEFAULT_TIMEOUT_SECONDS, tls=False):
4546
super(Client, self).__init__()

tests/integration_test.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import shutil
2222
import signal
2323
import socket
24+
import sys
2425
import tarfile
2526
import tempfile
2627
import threading
@@ -95,6 +96,7 @@ def setUp(self):
9596
self.tmp_containers = []
9697
self.tmp_folders = []
9798
self.tmp_volumes = []
99+
self.tmp_networks = []
98100

99101
def tearDown(self):
100102
for img in self.tmp_imgs:
@@ -108,6 +110,11 @@ def tearDown(self):
108110
self.client.remove_container(container)
109111
except docker.errors.APIError:
110112
pass
113+
for network in self.tmp_networks:
114+
try:
115+
self.client.remove_network(network)
116+
except docker.errors.APIError:
117+
pass
111118
for folder in self.tmp_folders:
112119
shutil.rmtree(folder)
113120

@@ -1590,6 +1597,103 @@ def runTest(self):
15901597
['not-ignored'],
15911598
)
15921599

1600+
1601+
#######################
1602+
# NETWORK TESTS #
1603+
#######################
1604+
1605+
1606+
@requires_api_version('1.21')
1607+
class TestNetworks(BaseTestCase):
1608+
def create_network(self, *args, **kwargs):
1609+
net_name = 'dockerpy{}'.format(random.randrange(sys.maxint))[:14]
1610+
net_id = self.client.create_network(net_name, *args, **kwargs)['id']
1611+
self.tmp_networks.append(net_id)
1612+
return (net_name, net_id)
1613+
1614+
def test_list_networks(self):
1615+
networks = self.client.networks()
1616+
initial_size = len(networks)
1617+
1618+
net_name, net_id = self.create_network()
1619+
1620+
networks = self.client.networks()
1621+
self.assertEqual(len(networks), initial_size + 1)
1622+
self.assertTrue(net_id in [n['id'] for n in networks])
1623+
1624+
networks_by_name = self.client.networks(names=[net_name])
1625+
self.assertEqual([n['id'] for n in networks_by_name], [net_id])
1626+
1627+
networks_by_partial_id = self.client.networks(ids=[net_id[:8]])
1628+
self.assertEqual([n['id'] for n in networks_by_partial_id], [net_id])
1629+
1630+
def test_inspect_network(self):
1631+
net_name, net_id = self.create_network()
1632+
1633+
net = self.client.inspect_network(net_id)
1634+
self.assertEqual(net, {
1635+
u'name': net_name,
1636+
u'id': net_id,
1637+
u'driver': 'bridge',
1638+
u'containers': {},
1639+
})
1640+
1641+
def test_create_network_with_host_driver_fails(self):
1642+
net_name = 'dockerpy{}'.format(random.randrange(sys.maxint))[:14]
1643+
1644+
with pytest.raises(APIError):
1645+
self.client.create_network(net_name, driver='host')
1646+
1647+
def test_remove_network(self):
1648+
initial_size = len(self.client.networks())
1649+
1650+
net_name, net_id = self.create_network()
1651+
self.assertEqual(len(self.client.networks()), initial_size + 1)
1652+
1653+
self.client.remove_network(net_id)
1654+
self.assertEqual(len(self.client.networks()), initial_size)
1655+
1656+
def test_connect_and_disconnect_container(self):
1657+
net_name, net_id = self.create_network()
1658+
1659+
container = self.client.create_container('busybox', 'top')
1660+
self.tmp_containers.append(container)
1661+
self.client.start(container)
1662+
1663+
network_data = self.client.inspect_network(net_id)
1664+
self.assertFalse(network_data.get('containers'))
1665+
1666+
self.client.connect_container_to_network(container, net_id)
1667+
network_data = self.client.inspect_network(net_id)
1668+
self.assertEqual(
1669+
list(network_data['containers'].keys()),
1670+
[container['Id']])
1671+
1672+
self.client.disconnect_container_from_network(container, net_id)
1673+
network_data = self.client.inspect_network(net_id)
1674+
self.assertFalse(network_data.get('containers'))
1675+
1676+
def test_connect_on_container_create(self):
1677+
net_name, net_id = self.create_network()
1678+
1679+
container = self.client.create_container(
1680+
image='busybox',
1681+
command='top',
1682+
host_config=self.client.create_host_config(network_mode=net_name),
1683+
)
1684+
self.tmp_containers.append(container)
1685+
self.client.start(container)
1686+
1687+
network_data = self.client.inspect_network(net_id)
1688+
self.assertEqual(
1689+
list(network_data['containers'].keys()),
1690+
[container['Id']])
1691+
1692+
self.client.disconnect_container_from_network(container, net_id)
1693+
network_data = self.client.inspect_network(net_id)
1694+
self.assertFalse(network_data.get('containers'))
1695+
1696+
15931697
#######################
15941698
# PY SPECIFIC TESTS #
15951699
#######################

tests/test.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,41 @@ def test_list_containers(self):
369369
timeout=DEFAULT_TIMEOUT_SECONDS
370370
)
371371

372+
def test_list_networks(self):
373+
networks = [
374+
{
375+
"name": "none",
376+
"id": "8e4e55c6863ef424",
377+
"type": "null",
378+
"endpoints": []
379+
},
380+
{
381+
"name": "host",
382+
"id": "062b6d9ea7913fde",
383+
"type": "host",
384+
"endpoints": []
385+
},
386+
]
387+
388+
get = mock.Mock(return_value=response(
389+
status_code=200, content=json.dumps(networks).encode('utf-8')))
390+
391+
with mock.patch('docker.Client.get', get):
392+
self.assertEqual(self.client.networks(), networks)
393+
394+
self.assertEqual(get.call_args[0][0], url_prefix + 'networks')
395+
396+
filters = json.loads(get.call_args[1]['params']['filters'])
397+
self.assertFalse(filters)
398+
399+
self.client.networks(names=['foo'])
400+
filters = json.loads(get.call_args[1]['params']['filters'])
401+
self.assertEqual(filters, {'name': ['foo']})
402+
403+
self.client.networks(ids=['123'])
404+
filters = json.loads(get.call_args[1]['params']['filters'])
405+
self.assertEqual(filters, {'id': ['123']})
406+
372407
#####################
373408
# CONTAINER TESTS #
374409
#####################
@@ -2229,6 +2264,105 @@ def test_remove_volume(self):
22292264
self.assertEqual(args[0][0], 'DELETE')
22302265
self.assertEqual(args[0][1], '{0}volumes/{1}'.format(url_prefix, name))
22312266

2267+
#####################
2268+
# NETWORK TESTS #
2269+
#####################
2270+
2271+
def test_create_network(self):
2272+
network_data = {
2273+
"id": 'abc12345',
2274+
"warning": "",
2275+
}
2276+
2277+
network_response = response(status_code=200, content=network_data)
2278+
post = mock.Mock(return_value=network_response)
2279+
2280+
with mock.patch('docker.Client.post', post):
2281+
result = self.client.create_network('foo')
2282+
self.assertEqual(result, network_data)
2283+
2284+
self.assertEqual(
2285+
post.call_args[0][0],
2286+
url_prefix + 'networks/create')
2287+
2288+
self.assertEqual(
2289+
json.loads(post.call_args[1]['data']),
2290+
{"name": "foo"})
2291+
2292+
self.client.create_network('foo', 'bridge')
2293+
2294+
self.assertEqual(
2295+
json.loads(post.call_args[1]['data']),
2296+
{"name": "foo", "driver": "bridge"})
2297+
2298+
def test_remove_network(self):
2299+
network_id = 'abc12345'
2300+
delete = mock.Mock(return_value=response(status_code=200))
2301+
2302+
with mock.patch('docker.Client.delete', delete):
2303+
self.client.remove_network(network_id)
2304+
2305+
args = delete.call_args
2306+
self.assertEqual(args[0][0],
2307+
url_prefix + 'networks/{0}'.format(network_id))
2308+
2309+
def test_inspect_network(self):
2310+
network_id = 'abc12345'
2311+
network_name = 'foo'
2312+
network_data = {
2313+
six.u('name'): network_name,
2314+
six.u('id'): network_id,
2315+
six.u('driver'): 'bridge',
2316+
six.u('containers'): {},
2317+
}
2318+
2319+
network_response = response(status_code=200, content=network_data)
2320+
get = mock.Mock(return_value=network_response)
2321+
2322+
with mock.patch('docker.Client.get', get):
2323+
result = self.client.inspect_network(network_id)
2324+
self.assertEqual(result, network_data)
2325+
2326+
args = get.call_args
2327+
self.assertEqual(args[0][0],
2328+
url_prefix + 'networks/{0}'.format(network_id))
2329+
2330+
def test_connect_container_to_network(self):
2331+
network_id = 'abc12345'
2332+
container_id = 'def45678'
2333+
2334+
post = mock.Mock(return_value=response(status_code=201))
2335+
2336+
with mock.patch('docker.Client.post', post):
2337+
self.client.connect_container_to_network(
2338+
{'Id': container_id}, network_id)
2339+
2340+
self.assertEqual(
2341+
post.call_args[0][0],
2342+
url_prefix + 'networks/{0}/connect'.format(network_id))
2343+
2344+
self.assertEqual(
2345+
json.loads(post.call_args[1]['data']),
2346+
{'container': container_id})
2347+
2348+
def test_disconnect_container_from_network(self):
2349+
network_id = 'abc12345'
2350+
container_id = 'def45678'
2351+
2352+
post = mock.Mock(return_value=response(status_code=201))
2353+
2354+
with mock.patch('docker.Client.post', post):
2355+
self.client.disconnect_container_from_network(
2356+
{'Id': container_id}, network_id)
2357+
2358+
self.assertEqual(
2359+
post.call_args[0][0],
2360+
url_prefix + 'networks/{0}/disconnect'.format(network_id))
2361+
2362+
self.assertEqual(
2363+
json.loads(post.call_args[1]['data']),
2364+
{'container': container_id})
2365+
22322366
#######################
22332367
# PY SPECIFIC TESTS #
22342368
#######################

0 commit comments

Comments
 (0)