Repository: docker/docker-py

Issue:
Support create network EnableIPv6 and Labels options 
Check the remote API:
https://docs.docker.com/engine/reference/api/docker_remote_api_v1.23/#create-a-network

There are two missing JSON parameters:
```
EnableIPv6 - Enable IPv6 on the network
Labels - Labels to set on the network, specified as a map: {"key":"value" [,"key2":"value2"]}
```

Discussion:

Patch:
diff --git a/docker/api/network.py b/docker/api/network.py
index 34cd8987..0ee0dab6 100644
--- a/docker/api/network.py
+++ b/docker/api/network.py
@@ -22,7 +22,8 @@ class NetworkApiMixin(object):
 
     @minimum_version('1.21')
     def create_network(self, name, driver=None, options=None, ipam=None,
-                       check_duplicate=None, internal=False):
+                       check_duplicate=None, internal=False, labels=None,
+                       enable_ipv6=False):
         if options is not None and not isinstance(options, dict):
             raise TypeError('options must be a dictionary')
 
@@ -34,6 +35,22 @@ class NetworkApiMixin(object):
             'CheckDuplicate': check_duplicate
         }
 
+        if labels is not None:
+            if version_lt(self._version, '1.23'):
+                raise InvalidVersion(
+                    'network labels were introduced in API 1.23'
+                )
+            if not isinstance(labels, dict):
+                raise TypeError('labels must be a dictionary')
+            data["Labels"] = labels
+
+        if enable_ipv6:
+            if version_lt(self._version, '1.23'):
+                raise InvalidVersion(
+                    'enable_ipv6 was introduced in API 1.23'
+                )
+            data['EnableIPv6'] = True
+
         if internal:
             if version_lt(self._version, '1.22'):
                 raise InvalidVersion('Internal networks are not '
@@ -76,8 +93,15 @@ class NetworkApiMixin(object):
 
     @check_resource
     @minimum_version('1.21')
-    def disconnect_container_from_network(self, container, net_id):
-        data = {"container": container}
+    def disconnect_container_from_network(self, container, net_id,
+                                          force=False):
+        data = {"Container": container}
+        if force:
+            if version_lt(self._version, '1.22'):
+                raise InvalidVersion(
+                    'Forced disconnect was introduced in API 1.22'
+                )
+            data['Force'] = force
         url = self._url("/networks/{0}/disconnect", net_id)
         res = self._post_json(url, data=data)
         self._raise_for_status(res)
diff --git a/docs/api.md b/docs/api.md
index 895d7d45..1699344a 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -283,22 +283,25 @@ The utility can be used as follows:
 ```python
 >>> import docker.utils
 >>> my_envs = docker.utils.parse_env_file('/path/to/file')
->>> docker.utils.create_container_config('1.18', '_mongodb', 'foobar',  environment=my_envs)
+>>> client.create_container('myimage', 'command', environment=my_envs)
 ```
 
-You can now use this with 'environment' for `create_container`.
-
-
 ## create_network
 
-Create a network, similar to the `docker network create` command.
+Create a network, similar to the `docker network create` command. See the
+[networks documentation](networks.md) for details.
 
 **Params**:
 
 * name (str): Name of the network
 * driver (str): Name of the driver used to create the network
-
 * options (dict): Driver options as a key-value dictionary
+* ipam (dict): Optional custom IP scheme for the network
+* check_duplicate (bool): Request daemon to check for networks with same name.
+  Default: `True`.
+* internal (bool): Restrict external access to the network. Default `False`.
+* labels (dict): Map of labels to set on the network. Default `None`.
+* enable_ipv6 (bool): Enable IPv6 on the network. Default `False`.
 
 **Returns** (dict): The created network reference object
 
@@ -352,6 +355,8 @@ Inspect changes on a container's filesystem.
 
 * container (str): container-id/name to be disconnected from a network
 * net_id (str): network id
+* force (bool): Force the container to disconnect from a network.
+  Default: `False`
 
 ## events
 


Test patch:
diff --git a/tests/integration/network_test.py b/tests/integration/network_test.py
index 27e1b14d..6726db4b 100644
--- a/tests/integration/network_test.py
+++ b/tests/integration/network_test.py
@@ -115,7 +115,8 @@ class TestNetworks(helpers.BaseTestCase):
         network_data = self.client.inspect_network(net_id)
         self.assertEqual(
             list(network_data['Containers'].keys()),
-            [container['Id']])
+            [container['Id']]
+        )
 
         with pytest.raises(docker.errors.APIError):
             self.client.connect_container_to_network(container, net_id)
@@ -127,6 +128,33 @@ class TestNetworks(helpers.BaseTestCase):
         with pytest.raises(docker.errors.APIError):
             self.client.disconnect_container_from_network(container, net_id)
 
+    @requires_api_version('1.22')
+    def test_connect_and_force_disconnect_container(self):
+        net_name, net_id = self.create_network()
+
+        container = self.client.create_container('busybox', 'top')
+        self.tmp_containers.append(container)
+        self.client.start(container)
+
+        network_data = self.client.inspect_network(net_id)
+        self.assertFalse(network_data.get('Containers'))
+
+        self.client.connect_container_to_network(container, net_id)
+        network_data = self.client.inspect_network(net_id)
+        self.assertEqual(
+            list(network_data['Containers'].keys()),
+            [container['Id']]
+        )
+
+        self.client.disconnect_container_from_network(container, net_id, True)
+        network_data = self.client.inspect_network(net_id)
+        self.assertFalse(network_data.get('Containers'))
+
+        with pytest.raises(docker.errors.APIError):
+            self.client.disconnect_container_from_network(
+                container, net_id, force=True
+            )
+
     @requires_api_version('1.22')
     def test_connect_with_aliases(self):
         net_name, net_id = self.create_network()
@@ -300,7 +328,8 @@ class TestNetworks(helpers.BaseTestCase):
         net_name, net_id = self.create_network()
         with self.assertRaises(docker.errors.APIError):
             self.client.create_network(net_name, check_duplicate=True)
-        self.client.create_network(net_name, check_duplicate=False)
+        net_id = self.client.create_network(net_name, check_duplicate=False)
+        self.tmp_networks.append(net_id['Id'])
 
     @requires_api_version('1.22')
     def test_connect_with_links(self):
@@ -387,3 +416,27 @@ class TestNetworks(helpers.BaseTestCase):
         _, net_id = self.create_network(internal=True)
         net = self.client.inspect_network(net_id)
         assert net['Internal'] is True
+
+    @requires_api_version('1.23')
+    def test_create_network_with_labels(self):
+        _, net_id = self.create_network(labels={
+            'com.docker.py.test': 'label'
+        })
+
+        net = self.client.inspect_network(net_id)
+        assert 'Labels' in net
+        assert len(net['Labels']) == 1
+        assert net['Labels'] == {
+            'com.docker.py.test': 'label'
+        }
+
+    @requires_api_version('1.23')
+    def test_create_network_with_labels_wrong_type(self):
+        with pytest.raises(TypeError):
+            self.create_network(labels=['com.docker.py.test=label', ])
+
+    @requires_api_version('1.23')
+    def test_create_network_ipv6_enabled(self):
+        _, net_id = self.create_network(enable_ipv6=True)
+        net = self.client.inspect_network(net_id)
+        assert net['EnableIPv6'] is True
diff --git a/tests/unit/network_test.py b/tests/unit/network_test.py
index 5bba9db2..2521688d 100644
--- a/tests/unit/network_test.py
+++ b/tests/unit/network_test.py
@@ -184,4 +184,4 @@ class NetworkTest(DockerClientTest):
 
         self.assertEqual(
             json.loads(post.call_args[1]['data']),
-            {'container': container_id})
+            {'Container': container_id})