summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-02-21 07:48:19 +0000
committerGerrit Code Review <review@openstack.org>2017-02-21 07:48:19 +0000
commit1996d2b144e005137d8aacd7f1d523956d3e0560 (patch)
treebe774135ea1b291fbdc677821ee52398e26393cf
parent6ba25fe131dd051c5671eca82606087e6aec078f (diff)
parentd379968d79a3eb7a71d944c96a35d183a1d90b5b (diff)
Merge "Delete and move outdated docs"
-rw-r--r--doc/source/devref/index.rst3
-rw-r--r--doc/source/devref/k8s_api_watcher_design.rst1041
-rw-r--r--doc/source/devref/libnetwork_remote_driver_design.rst398
3 files changed, 0 insertions, 1442 deletions
diff --git a/doc/source/devref/index.rst b/doc/source/devref/index.rst
index 7dd6350..f20a6af 100644
--- a/doc/source/devref/index.rst
+++ b/doc/source/devref/index.rst
@@ -35,10 +35,7 @@ Programming HowTos and Tutorials
35 :maxdepth: 4 35 :maxdepth: 4
36 36
37 goals_and_use_cases 37 goals_and_use_cases
38 libnetwork_remote_driver_design
39 kuryr_mitaka_milestone 38 kuryr_mitaka_milestone
40 k8s_api_watcher_design
41
42 39
43Indices and tables 40Indices and tables
44------------------ 41------------------
diff --git a/doc/source/devref/k8s_api_watcher_design.rst b/doc/source/devref/k8s_api_watcher_design.rst
deleted file mode 100644
index 71ea846..0000000
--- a/doc/source/devref/k8s_api_watcher_design.rst
+++ /dev/null
@@ -1,1041 +0,0 @@
1..
2 This work is licensed under a Creative Commons Attribution 3.0 Unported
3 License.
4
5 http://creativecommons.org/licenses/by/3.0/legalcode
6
7 Convention for heading levels in Neutron devref:
8 ======= Heading 0 (reserved for the title in a document)
9 ------- Heading 1
10 ~~~~~~~ Heading 2
11 +++++++ Heading 3
12 ''''''' Heading 4
13 (Avoid deeper levels because they do not render well.)
14
15=============================
16Kubernetes API Watcher Design
17=============================
18
19This documentation describes the `Kubernetes API <http://kubernetes.io/docs/api/>`_
20watcher daemon component, **Raven**, of Kuryr.
21
22What is Raven
23-------------
24
25Raven is a daemon watches the internal Kubernetes (K8s) state through its API
26server and receives the changes with the event notifications. Raven then
27translate the state changes of K8s into requests against Neutron API and
28constructs the virtual network topology on Neutron.
29
30Raven must act as the centralized component for the translations due to the
31constraints come from the concurrent deployments of the pods on worker nodes.
32Unless it's centralized, each plugin on each worker node would make requests
33against Neutron API and it would lead the conflicts of the requests due to the
34race condition because of the lack of the lock or the serialization mechanisms
35for the requests against Neutron API.
36
37Raven doesn't take care of the bindings between the virtual ports and the
38physical interfaces on worker nodes. It is the responsibility of Kuryr `CNI <https://github.com/appc/cni>`_
39plugin for K8s and it shall recognize which Neutron port should be bound to the
40physical interface associated with the pod to be deployed. So Raven focuses
41only on building the virtual network topology translated from the events of the
42internal state changes of K8s through its API server.
43
44Goal
45~~~~
46
47Through Raven the changes to K8s API server are translated into the appropriate
48requests against Neutron API and we can make sure their logical networking states
49are synchronized and consistent when the pods are deployed.
50
51Translation Mapping
52-------------------
53
54The detailed specification of the translation mappings is described in another
55document, :doc:`../specs/newton/kuryr_k8s_integration`. In this document we touch what
56to be translated briefly.
57
58The main focus of Raven is the following resources.
59
60* Namespace
61* Pod
62* Service (Optional)
63* Endpoints (Optional)
64
65Namespaces are translated into the basic Neutron constructs, Neutron networks
66and subnets for the cluster and the service using the explicitly predefined
67values in the configuration file, or implicitly specified by the environment
68variables, e.g., ``FLANNEL_NET=172.16.0.0/16`` as specified
69`in the deployment phase <https://github.com/kubernetes/kubernetes/search?utf8=%E2%9C%93&q=FLANNEL_NET>`_.
70Raven also creates Neutron routers for connecting the cluster subnets and the service subnets.
71
72When each namespace is created, a cluster network that contains a cluster
73subnet, a service network that contains a service subnet, and a router that
74connects the cluster subnet and the service subnet are created through Neutron
75API. The apiserver ensures namespaces are created before pods are created.
76
77Pods contain the information required for creating Neutron ports. If pods are
78associated with the specific namespace, the ports are created and associated
79with the subnets for the namespace.
80
81Although it's optional, Raven can emulate `kube-proxy <http://kubernetes.io/docs/user-guide/services/#virtual-ips-and-service-proxies>`_.
82This is for the network controller that leverages isolated datapath from ``docker0``
83bridge such as Open vSwitch datapath. Services contain the information for the
84emulation. Raven maps kube-proxy to Neutron load balancers with VIPs. In this case
85Raven also creates a LBaaS pool member for each Endpoints to be translated
86coordinating with the associated service translation. For "externalIPs" type K8s
87service, Raven associates a floating IP with a load balancer for enabling the public
88accesses.
89
90================= =================
91Kubernetes Neutron
92================= =================
93Namespace Network
94(Cluster subnet) (Subnet)
95Pod Port
96Service LBaaS Pool
97 LBaaS VIP
98 (FloatingIP)
99Endpoints LBaaS Pool Member
100================= =================
101
102
103.. _k8s-api-behaviour:
104
105K8s API behaviour
106-----------------
107
108We look at the responses from the pod endpoints as an example.
109
110The following behaviour is based on the 1.2.0 release, which is the latest one
111as of March 17th, 2016::
112
113 $ ./kubectl.sh version
114 Client Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.0", GitCommit:"5cb86ee022267586db386f62781338b0483733b3", GitTreeState:"clean"}
115 Server Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.0", GitCommit:"5cb86ee022267586db386f62781338b0483733b3", GitTreeState:"clean"}
116
117Regular requests
118~~~~~~~~~~~~~~~~
119
120If there's no pod, the K8s API server returns the following JSON response that
121has the empty list for the ``"items"`` property::
122
123 $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods
124 HTTP/1.1 200 OK
125 Content-Type: application/json
126 Date: Tue, 15 Mar 2016 07:15:46 GMT
127 Content-Length: 145
128
129 {
130 "kind": "PodList",
131 "apiVersion": "v1",
132 "metadata": {
133 "selfLink": "/api/v1/pods",
134 "resourceVersion": "227806"
135 },
136 "items": []
137 }
138
139We deploy a pod as follow::
140
141 $ ./kubectl.sh run --image=nginx nginx-app --port=80
142 replicationcontroller "nginx-app" created
143
144Then the response from the API server contains the pod information in
145``"items"`` property of the JSON response::
146
147 $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods
148 HTTP/1.1 200 OK
149 Content-Type: application/json
150 Date: Tue, 15 Mar 2016 08:18:25 GMT
151 Transfer-Encoding: chunked
152
153 {
154 "kind": "PodList",
155 "apiVersion": "v1",
156 "metadata": {
157 "selfLink": "/api/v1/pods",
158 "resourceVersion": "228211"
159 },
160 "items": [
161 {
162 "metadata": {
163 "name": "nginx-app-o0kvl",
164 "generateName": "nginx-app-",
165 "namespace": "default",
166 "selfLink": "/api/v1/namespaces/default/pods/nginx-app-o0kvl",
167 "uid": "090cc0c8-ea84-11e5-8c79-42010af00003",
168 "resourceVersion": "228094",
169 "creationTimestamp": "2016-03-15T08:00:51Z",
170 "labels": {
171 "run": "nginx-app"
172 },
173 "annotations": {
174 "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"
175 }
176 },
177 "spec": {
178 "volumes": [
179 {
180 "name": "default-token-wpfjn",
181 "secret": {
182 "secretName": "default-token-wpfjn"
183 }
184 }
185 ],
186 "containers": [
187 {
188 "name": "nginx-app",
189 "image": "nginx",
190 "ports": [
191 {
192 "containerPort": 80,
193 "protocol": "TCP"
194 }
195 ],
196 "resources": {},
197 "volumeMounts": [
198 {
199 "name": "default-token-wpfjn",
200 "readOnly": true,
201 "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
202 }
203 ],
204 "terminationMessagePath": "/dev/termination-log",
205 "imagePullPolicy": "Always"
206 }
207 ],
208 "restartPolicy": "Always",
209 "terminationGracePeriodSeconds": 30,
210 "dnsPolicy": "ClusterFirst",
211 "serviceAccountName": "default",
212 "serviceAccount": "default",
213 "nodeName": "10.240.0.4",
214 "securityContext": {}
215 },
216 "status": {
217 "phase": "Running",
218 "conditions": [
219 {
220 "type": "Ready",
221 "status": "True",
222 "lastProbeTime": null,
223 "lastTransitionTime": "2016-03-15T08:00:52Z"
224 }
225 ],
226 "hostIP": "10.240.0.4",
227 "podIP": "172.16.49.2",
228 "startTime": "2016-03-15T08:00:51Z",
229 "containerStatuses": [
230 {
231 "name": "nginx-app",
232 "state": {
233 "running": {
234 "startedAt": "2016-03-15T08:00:52Z"
235 }
236 },
237 "lastState": {},
238 "ready": true,
239 "restartCount": 0,
240 "image": "nginx",
241 "imageID": "docker://sha256:af4b3d7d5401624ed3a747dc20f88e2b5e92e0ee9954aab8f1b5724d7edeca5e",
242 "containerID": "docker://b97168314ad58404dbce7cb94291db7a976d2cb824b39e5864bf4bdaf27af255"
243 }
244 ]
245 }
246 }
247 ]
248 }
249
250We get the current snapshot of the requested resources with the regular
251requests against the K8s API server.
252
253Requests with ``watch=true`` query string
254~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
255
256K8s provides the "watch" capability for the endpoints with ``/watch/`` prefix
257for the specific resource name, i.e., ``/api/v1/watch/pods``, or ``watch=true``
258query string.
259
260If there's no pod, we get only the response header and the connection is kept
261open::
262
263 $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods?watch=true
264 HTTP/1.1 200 OK
265 Transfer-Encoding: chunked
266 Date: Tue, 15 Mar 2016 08:00:09 GMT
267 Content-Type: text/plain; charset=utf-8
268 Transfer-Encoding: chunked
269
270We create a pod as we did for the case without the ``watch=true`` query string::
271
272 $ ./kubectl.sh run --image=nginx nginx-app --port=80
273 replicationcontroller "nginx-app" created
274
275Then we observe the JSON data corresponds to the event is given by each line.
276The event type is given in ``"type"`` property of the JSON data, i.e.,
277``"ADDED"``, ``"MODIFIED"`` and ``"DELETED"``::
278
279 $ curl -X GET -i http://127.0.0.1:8080/api/v1/pods?watch=true
280 HTTP/1.1 200 OK
281 Transfer-Encoding: chunked
282 Date: Tue, 15 Mar 2016 08:00:09 GMT
283 Content-Type: text/plain; charset=utf-8
284 Transfer-Encoding: chunked
285
286 {"type":"ADDED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228082","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","securityContext":{}},"status":{"phase":"Pending"}}}
287 {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228084","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"10.240.0.4","securityContext":{}},"status":{"phase":"Pending"}}}
288 {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228088","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"10.240.0.4","securityContext":{}},"status":{"phase":"Pending","conditions":[{"type":"Ready","status":"False","lastProbeTime":null,"lastTransitionTime":"2016-03-15T08:00:51Z","reason":"ContainersNotReady","message":"containers with unready status: [nginx-app]"}],"hostIP":"10.240.0.4","startTime":"2016-03-15T08:00:51Z","containerStatuses":[{"name":"nginx-app","state":{"waiting":{"reason":"ContainerCreating","message":"Image: nginx is ready, container is creating"}},"lastState":{},"ready":false,"restartCount":0,"image":"nginx","imageID":""}]}}}
289 {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-app-o0kvl","generateName":"nginx-app-","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/nginx-app-o0kvl","uid":"090cc0c8-ea84-11e5-8c79-42010af00003","resourceVersion":"228094","creationTimestamp":"2016-03-15T08:00:51Z","labels":{"run":"nginx-app"},"annotations":{"kubernetes.io/created-by":"{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"nginx-app\",\"uid\":\"090bfb57-ea84-11e5-8c79-42010af00003\",\"apiVersion\":\"v1\",\"resourceVersion\":\"228081\"}}\n"}},"spec":{"volumes":[{"name":"default-token-wpfjn","secret":{"secretName":"default-token-wpfjn"}}],"containers":[{"name":"nginx-app","image":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"volumeMounts":[{"name":"default-token-wpfjn","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"10.240.0.4","securityContext":{}},"status":{"phase":"Running","conditions":[{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2016-03-15T08:00:52Z"}],"hostIP":"10.240.0.4","podIP":"172.16.49.2","startTime":"2016-03-15T08:00:51Z","containerStatuses":[{"name":"nginx-app","state":{"running":{"startedAt":"2016-03-15T08:00:52Z"}},"lastState":{},"ready":true,"restartCount":0,"image":"nginx","imageID":"docker://sha256:af4b3d7d5401624ed3a747dc20f88e2b5e92e0ee9954aab8f1b5724d7edeca5e","containerID":"docker://b97168314ad58404dbce7cb94291db7a976d2cb824b39e5864bf4bdaf27af255"}]}}}
290
291Raven Technical Design Overview
292-------------------------------
293
294Problem Statement
295~~~~~~~~~~~~~~~~~
296
297To conform to the I/O bound requirement described in :ref:`k8s-api-behaviour`,
298the multiplexed concurrent network I/O is demanded.
299`eventlet <http://eventlet.net/>`_ is used in various OpenStack projects for this
300purpose as well as other libraries such as `Twisted <https://twistedmatrix.com/trac/>`_,
301`Tornado <http://tornadoweb.org/>`_ and `gevent <http://www.gevent.org/>`_.
302However, it has problems as described in
303"`What's wrong with eventlet? <https://wiki.openstack.org/wiki/Oslo/blueprints/asyncio#What.27s_wrong_with_eventlet.3F>`_"
304on the OpenStack wiki page.
305
306asyncio and Python 3 by default
307~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
308
309`asyncio <https://www.python.org/dev/peps/pep-3156/>`_ was introduced as a
310standard asynchronous I/O library in Python 3.4. Its event loop and coroutines
311provide the mechanism to multiplex network I/O in the asynchronous fashion.
312Compared with eventlet, we can explicitly mark the I/O operations asynchronous
313with ``yield from`` or ``await`` introduced in Python 3.5.
314
315`Trollius <http://trollius.readthedocs.org/>`_ is a port of asyncio to Python 2.x.
316However `Trollius documentation <http://trollius.readthedocs.org/deprecated.html>`_
317is describing a list of problems and even promoting the migration to Python 3
318with asyncio.
319
320Kuryr is still a quite young project in OpenStack Neutron big tent. In addition
321to that, since it's a container related project it should be able to be run
322inside a container. So do Raven. Therefore we take a path to support for only
323Python 3 and drop Python 2.
324
325With asyncio we can achieve concurrent networking I/O operations required by
326watchers watch multiple endpoints and translate their responses into requests
327against Neutron and K8s API server.
328
329Watchers
330~~~~~~~~
331
332A watcher can be represented as a pair of an API endpoint and a function used
333for the translation essentially. That is, the pair of what is translated and
334how it is. The API endpoint URI is associated with the stream of the event
335notifications and the translation function maps each event coming from the
336apiserver into another form such as the request against Neutron API server.
337
338Watchers can be considered as concerns and reactions. They should be decoupled
339from the actual task dispatcher and their consumers. A single or multiple
340watchers can be mixed into the single class that leverages them, i.e., Raven,
341or even multiple classes leverage them can have the same concern and the same
342reaction. The watchers can be able to be mixed into the single entity of the
343watcher user but they should work independently. For instance, ``AliceWatcher``
344does its work and knows nothing about other watchers such as ``BobWatcher``.
345They don't work together depending on one or each.
346
347A minimum watcher can be defined as follow.
348
349.. code-block:: python
350
351 from kuryr.raven import watchers
352
353 class SomeWatcher(watchers.K8sApiWatcher):
354 WATCH_ENDPOINT = '/'
355
356 def translate(self, deserialized_json):
357 pass
358
359The watcher is defined in the declarative way and ideally doesn't care when it
360is called and by whom. However, it needs to recognize the context such as the
361event type and behave appropriately according to the situation.
362
363Raven
364~~~~~
365
366Raven acts as a daemon and it should be able to be started or stopped by
367operators. It delegates the actual watch tasks to the watchers and dispatch
368them with the single JSON response corresponds to each endpoint on which the
369watcher has its concern.
370
371Hence, Raven holds one or multiple watchers, opens connections for each
372endpoint, makes HTTP requests, gets HTTP responses and parses every event
373notification and dispatches the translate methods of the watchers routed based
374on their corresponding endpoints.
375
376To register the watchers to Raven or any class, ``register_watchers`` decorator
377is used. It simply inserts the watchers into the dictionary in the class,
378``WATCH_ENDPOINTS_AND_CALLBACKS`` and it's up to the class how use the
379registered watchers. The classes passed to ``register_watchers`` are defined in
380the configuration file and you can specify only what you need.
381
382In the case of Raven, it starts the event loop, open connections for each
383registered watcher and keeps feeding the notified events to the translate
384methods of the watchers.
385
386Raven is a service implements ``oslo_service.service.Service``. When ``start``
387method is called, it starts the event loop and delegatations of the watch tasks.
388If ``SIGINT`` or ``SIGTERM`` signal is sent to Raven, it cancells all watch
389tasks, closes connections and stops immediately. Otherwise Raven lets watchers
390keep watching the API endpoints until the API server sends EOF strings. When
391``stop`` is called, it cancells all watch tasks, closes connections and stops
392as well.
393
394Ideally, the translate method can be a pure function that doesn't depend on the
395user of the watcher. However, the translation gets involved in requests against
396Neutron and possibly the K8s API server. And it depends on the Neutron client
397that shall be shared among the watchers. Hence, Raven calls the translate
398methods of the watchers binding itself to ``self``. That is, Raven can
399propagate its contexts to the watchers and in this way watchers can share the
400same contexts. However, it's responsibility of the writer of the watchers to
401track which variables are defined in Raven and what they are.
402
403Appendix A: JSON response from the apiserver for each resource
404--------------------------------------------------------------
405
406Namespace
407~~~~~~~~~
408
409::
410
411 /api/v1/namespaces?watch=true
412
413ADDED
414+++++
415
416::
417
418 {
419 "type": "ADDED",
420 "object": {
421 "kind": "Namespace",
422 "apiVersion": "v1",
423 "metadata": {
424 "name": "test",
425 "selfLink": "/api/v1/namespaces/test",
426 "uid": "f094ea6b-06c2-11e6-8128-42010af00003",
427 "resourceVersion": "497821",
428 "creationTimestamp": "2016-04-20T06:41:41Z"
429 },
430 "spec": {
431 "finalizers": [
432 "kubernetes"
433 ]
434 },
435 "status": {
436 "phase": "Active"
437 }
438 }
439 }
440
441MODIFIED
442++++++++
443
444::
445
446 {
447 "type": "MODIFIED",
448 "object": {
449 "kind": "Namespace",
450 "apiVersion": "v1",
451 "metadata": {
452 "name": "test",
453 "selfLink": "/api/v1/namespaces/test",
454 "uid": "f094ea6b-06c2-11e6-8128-42010af00003",
455 "resourceVersion": "519095",
456 "creationTimestamp": "2016-04-20T06:41:41Z",
457 "deletionTimestamp": "2016-04-21T08:47:53Z"
458 },
459 "spec": {
460 "finalizers": [
461 "kubernetes"
462 ]
463 },
464 "status": {
465 "phase": "Terminating"
466 }
467 }
468 }
469
470DELETED
471+++++++
472
473::
474
475 {
476 "type": "DELETED",
477 "object": {
478 "kind": "Namespace",
479 "apiVersion": "v1",
480 "metadata": {
481 "name": "test",
482 "selfLink": "/api/v1/namespaces/test",
483 "uid": "f094ea6b-06c2-11e6-8128-42010af00003",
484 "resourceVersion": "519099",
485 "creationTimestamp": "2016-04-20T06:41:41Z",
486 "deletionTimestamp": "2016-04-21T08:47:53Z"
487 },
488 "spec": {},
489 "status": {
490 "phase": "Terminating"
491 }
492 }
493 }
494
495
496Pod
497~~~
498
499::
500
501 /api/v1/pods?watch=true
502
503ADDED
504+++++
505
506::
507
508 {
509 "type": "ADDED",
510 "object": {
511 "kind": "Pod",
512 "apiVersion": "v1",
513 "metadata": {
514 "name": "my-nginx-y67ky",
515 "generateName": "my-nginx-",
516 "namespace": "default",
517 "selfLink": "/api/v1/namespaces/default/pods/my-nginx-y67ky",
518 "uid": "d42b0bb2-dc4e-11e5-8c79-42010af00003",
519 "resourceVersion": "63355",
520 "creationTimestamp": "2016-02-26T06:04:42Z",
521 "labels": {
522 "run": "my-nginx"
523 },
524 "annotations": {
525 "kubernetes.io/created-by": {
526 "kind": "SerializedReference",
527 "apiVersion": "v1",
528 "reference": {
529 "kind": "ReplicationController",
530 "namespace": "default",
531 "name": "my-nginx",
532 "uid": "d42a4ee1-dc4e-11e5-8c79-42010af00003",
533 "apiVersion": "v1",
534 "resourceVersion": "63348"
535 }
536 }
537 }
538 },
539 "spec": {
540 "volumes": [
541 {
542 "name": "default-token-wpfjn",
543 "secret": {
544 "secretName": "default-token-wpfjn"
545 }
546 }
547 ],
548 "containers": [
549 {
550 "name": "my-nginx",
551 "image": "nginx",
552 "ports": [
553 {
554 "containerPort": 80,
555 "protocol": "TCP"
556 }
557 ],
558 "resources": {},
559 "volumeMounts": [
560 {
561 "name": "default-token-wpfjn",
562 "readOnly": true,
563 "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
564 }
565 ],
566 "terminationMessagePath": "/dev/termination-log",
567 "imagePullPolicy": "Always"
568 }
569 ],
570 "restartPolicy": "Always",
571 "terminationGracePeriodSeconds": 30,
572 "dnsPolicy": "ClusterFirst",
573 "serviceAccountName": "default",
574 "serviceAccount": "default",
575 "nodeName": "10.240.0.4",
576 "securityContext": {}
577 },
578 "status": {
579 "phase": "Pending",
580 "conditions": [
581 {
582 "type": "Ready",
583 "status": "False",
584 "lastProbeTime": null,
585 "lastTransitionTime": "2016-02-26T06:04:43Z",
586 "reason": "ContainersNotReady",
587 "message": "containers with unready status: [my-nginx]"
588 }
589 ],
590 "hostIP": "10.240.0.4",
591 "startTime": "2016-02-26T06:04:43Z",
592 "containerStatuses": [
593 {
594 "name": "my-nginx",
595 "state": {
596 "waiting": {
597 "reason": "ContainerCreating",
598 "message": "Image: nginx is ready, container is creating"
599 }
600 },
601 "lastState": {},
602 "ready": false,
603 "restartCount": 0,
604 "image": "nginx",
605 "imageID": ""
606 }
607 ]
608 }
609 }
610 }
611
612MODIFIED
613++++++++
614
615::
616
617 {
618 "type": "MODIFIED",
619 "object": {
620 "kind": "Pod",
621 "apiVersion": "v1",
622 "metadata": {
623 "name": "my-nginx-y67ky",
624 "generateName": "my-nginx-",
625 "namespace": "default",
626 "selfLink": "/api/v1/namespaces/default/pods/my-nginx-y67ky",
627 "uid": "d42b0bb2-dc4e-11e5-8c79-42010af00003",
628 "resourceVersion": "63425",
629 "creationTimestamp": "2016-02-26T06:04:42Z",
630 "deletionTimestamp": "2016-02-26T06:06:16Z",
631 "deletionGracePeriodSeconds": 30,
632 "labels": {
633 "run": "my-nginx"
634 },
635 "annotations": {
636 "kubernetes.io/created-by": {
637 "kind": "SerializedReference",
638 "apiVersion": "v1",
639 "reference": {
640 "kind": "ReplicationController",
641 "namespace": "default",
642 "name": "my-nginx",
643 "uid": "d42a4ee1-dc4e-11e5-8c79-42010af00003",
644 "apiVersion": "v1",
645 "resourceVersion": "63348"
646 }
647 }
648 }
649 },
650 "spec": {
651 "volumes": [
652 {
653 "name": "default-token-wpfjn",
654 "secret": {
655 "secretName": "default-token-wpfjn"
656 }
657 }
658 ],
659 "containers": [
660 {
661 "name": "my-nginx",
662 "image": "nginx",
663 "ports": [
664 {
665 "containerPort": 80,
666 "protocol": "TCP"
667 }
668 ],
669 "resources": {},
670 "volumeMounts": [
671 {
672 "name": "default-token-wpfjn",
673 "readOnly": true,
674 "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
675 }
676 ],
677 "terminationMessagePath": "/dev/termination-log",
678 "imagePullPolicy": "Always"
679 }
680 ],
681 "restartPolicy": "Always",
682 "terminationGracePeriodSeconds": 30,
683 "dnsPolicy": "ClusterFirst",
684 "serviceAccountName": "default",
685 "serviceAccount": "default",
686 "nodeName": "10.240.0.4",
687 "securityContext": {}
688 },
689 "status": {
690 "phase": "Pending",
691 "conditions": [
692 {
693 "type": "Ready",
694 "status": "False",
695 "lastProbeTime": null,
696 "lastTransitionTime": "2016-02-26T06:04:43Z",
697 "reason": "ContainersNotReady",
698 "message": "containers with unready status: [my-nginx]"
699 }
700 ],
701 "hostIP": "10.240.0.4",
702 "startTime": "2016-02-26T06:04:43Z",
703 "containerStatuses": [
704 {
705 "name": "my-nginx",
706 "state": {
707 "waiting": {
708 "reason": "ContainerCreating",
709 "message": "Image: nginx is ready, container is creating"
710 }
711 },
712 "lastState": {},
713 "ready": false,
714 "restartCount": 0,
715 "image": "nginx",
716 "imageID": ""
717 }
718 ]
719 }
720 }
721 }
722
723DELETED
724+++++++
725
726::
727
728 {
729 "type": "DELETED",
730 "object": {
731 "kind": "Pod",
732 "apiVersion": "v1",
733 "metadata": {
734 "name": "my-nginx-y67ky",
735 "generateName": "my-nginx-",
736 "namespace": "default",
737 "selfLink": "/api/v1/namespaces/default/pods/my-nginx-y67ky",
738 "uid": "d42b0bb2-dc4e-11e5-8c79-42010af00003",
739 "resourceVersion": "63431",
740 "creationTimestamp": "2016-02-26T06:04:42Z",
741 "deletionTimestamp": "2016-02-26T06:05:46Z",
742 "deletionGracePeriodSeconds": 0,
743 "labels": {
744 "run": "my-nginx"
745 },
746 "annotations": {
747 "kubernetes.io/created-by": {
748 "kind": "SerializedReference",
749 "apiVersion": "v1",
750 "reference": {
751 "kind": "ReplicationController",
752 "namespace": "default",
753 "name": "my-nginx",
754 "uid": "d42a4ee1-dc4e-11e5-8c79-42010af00003",
755 "apiVersion": "v1",
756 "resourceVersion": "63348"
757 }
758 }
759 }
760 },
761 "spec": {
762 "volumes": [
763 {
764 "name": "default-token-wpfjn",
765 "secret": {
766 "secretName": "default-token-wpfjn"
767 }
768 }
769 ],
770 "containers": [
771 {
772 "name": "my-nginx",
773 "image": "nginx",
774 "ports": [
775 {
776 "containerPort": 80,
777 "protocol": "TCP"
778 }
779 ],
780 "resources": {},
781 "volumeMounts": [
782 {
783 "name": "default-token-wpfjn",
784 "readOnly": true,
785 "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
786 }
787 ],
788 "terminationMessagePath": "/dev/termination-log",
789 "imagePullPolicy": "Always"
790 }
791 ],
792 "restartPolicy": "Always",
793 "terminationGracePeriodSeconds": 30,
794 "dnsPolicy": "ClusterFirst",
795 "serviceAccountName": "default",
796 "serviceAccount": "default",
797 "nodeName": "10.240.0.4",
798 "securityContext": {}
799 },
800 "status": {
801 "phase": "Pending",
802 "conditions": [
803 {
804 "type": "Ready",
805 "status": "False",
806 "lastProbeTime": null,
807 "lastTransitionTime": "2016-02-26T06:04:43Z",
808 "reason": "ContainersNotReady",
809 "message": "containers with unready status: [my-nginx]"
810 }
811 ],
812 "hostIP": "10.240.0.4",
813 "startTime": "2016-02-26T06:04:43Z",
814 "containerStatuses": [
815 {
816 "name": "my-nginx",
817 "state": {
818 "waiting": {
819 "reason": "ContainerCreating",
820 "message": "Image: nginx is ready, container is creating"
821 }
822 },
823 "lastState": {},
824 "ready": false,
825 "restartCount": 0,
826 "image": "nginx",
827 "imageID": ""
828 }
829 ]
830 }
831 }
832 }
833
834Service
835~~~~~~~
836
837::
838
839 /api/v1/services?watch=true
840
841ADDED
842+++++
843
844::
845
846 {
847 "type": "ADDED",
848 "object": {
849 "kind": "Service",
850 "apiVersion": "v1",
851 "metadata": {
852 "name": "redis-master",
853 "namespace": "default",
854 "selfLink": "/api/v1/namespaces/default/services/redis-master",
855 "uid": "7aecfdac-d54c-11e5-8cc5-42010af00002",
856 "resourceVersion": "2074",
857 "creationTimestamp": "2016-02-17T08:00:16Z",
858 "labels": {
859 "app": "redis",
860 "role": "master",
861 "tier": "backend"
862 }
863 },
864 "spec": {
865 "ports": [
866 {
867 "protocol": "TCP",
868 "port": 6379,
869 "targetPort": 6379
870 }
871 ],
872 "selector": {
873 "app": "redis",
874 "role": "master",
875 "tier": "backend"
876 },
877 "clusterIP": "10.0.0.102",
878 "type": "ClusterIP",
879 "sessionAffinity": "None"
880 },
881 "status": {
882 "loadBalancer": {}
883 }
884 }
885 }
886
887MODIFIED
888++++++++
889
890The event could not be observed.
891
892DELETED
893+++++++
894
895::
896
897 {
898 "type": "DELETED",
899 "object": {
900 "kind": "Service",
901 "apiVersion": "v1",
902 "metadata": {
903 "name": "redis-master",
904 "namespace": "default",
905 "selfLink": "/api/v1/namespaces/default/services/redis-master",
906 "uid": "7aecfdac-d54c-11e5-8cc5-42010af00002",
907 "resourceVersion": "2806",
908 "creationTimestamp": "2016-02-17T08:00:16Z",
909 "labels": {
910 "app": "redis",
911 "role": "master",
912 "tier": "backend"
913 }
914 },
915 "spec": {
916 "ports": [
917 {
918 "protocol": "TCP",
919 "port": 6379,
920 "targetPort": 6379
921 }
922 ],
923 "selector": {
924 "app": "redis",
925 "role": "master",
926 "tier": "backend"
927 },
928 "clusterIP": "10.0.0.102",
929 "type": "ClusterIP",
930 "sessionAffinity": "None"
931 },
932 "status": {
933 "loadBalancer": {}
934 }
935 }
936 }
937
938Endpoints
939~~~~~~~~~
940
941::
942
943 /api/v1/endpoints?watch=true
944
945ADDED
946+++++
947
948::
949
950 {
951 "type": "ADDED",
952 "object": {
953 "apiVersion": "v1",
954 "kind": "Endpoints",
955 "subsets": [],
956 "metadata": {
957 "creationTimestamp": "2016-06-10T06:26:57Z",
958 "namespace": "default",
959 "labels": {
960 "app": "guestbook",
961 "tier": "frontend"
962 },
963 "selfLink": "/api/v1/namespaces/default/endpoints/frontend",
964 "name": "frontend",
965 "uid": "5542ba6b-2ed4-11e6-8128-42010af00003",
966 "resourceVersion": "1506396"
967 }
968 }
969 }
970
971MODIFIED
972++++++++
973
974::
975
976 {
977 "type": "MODIFIED",
978 "object": {
979 "apiVersion": "v1",
980 "kind": "Endpoints",
981 "subsets": [
982 {
983 "addresses": [
984 {
985 "targetRef": {
986 "kind": "Pod",
987 "name": "frontend-ib7ui",
988 "namespace": "default",
989 "uid": "554b2924-2ed4-11e6-8128-42010af00003",
990 "resourceVersion": "1506444"
991 },
992 "ip": "192.168.0.119"
993 },
994 {
995 "targetRef": {
996 "kind": "Pod",
997 "name": "frontend-tt8ok",
998 "namespace": "default",
999 "uid": "554b37db-2ed4-11e6-8128-42010af00003",
1000 "resourceVersion": "1506459"
1001 },
1002 "ip": "192.168.0.120"
1003 },
1004 {
1005 "targetRef": {
1006 "kind": "Pod",
1007 "name": "frontend-rxsaw",
1008 "namespace": "default",
1009 "uid": "554b43b8-2ed4-11e6-8128-42010af00003",
1010 "resourceVersion": "1506442"
1011 },
1012 "ip": "192.168.0.121"
1013 }
1014 ],
1015 "ports": [
1016 {
1017 "port": 80,
1018 "protocol": "TCP"
1019 }
1020 ]
1021 }
1022 ],
1023 "metadata": {
1024 "creationTimestamp": "2016-06-10T06:26:57Z",
1025 "namespace": "default",
1026 "labels": {
1027 "app": "guestbook",
1028 "tier": "frontend"
1029 },
1030 "selfLink": "/api/v1/namespaces/default/endpoints/frontend",
1031 "name": "frontend",
1032 "uid": "5542ba6b-2ed4-11e6-8128-42010af00003",
1033 "resourceVersion": "1506460"
1034 }
1035 }
1036 }
1037
1038DELETED
1039++++++++
1040
1041The event could not be observed.
diff --git a/doc/source/devref/libnetwork_remote_driver_design.rst b/doc/source/devref/libnetwork_remote_driver_design.rst
deleted file mode 100644
index 53af518..0000000
--- a/doc/source/devref/libnetwork_remote_driver_design.rst
+++ /dev/null
@@ -1,398 +0,0 @@
1=======================================
2Libnetwork Remote Network Driver Design
3=======================================
4
5What is Kuryr
6-------------
7
8Kuryr implements a `libnetwork remote network driver <https://github.com/docker/libnetwork/blob/master/docs/remote.md>`_
9and maps its calls to OpenStack `Neutron <https://wiki.openstack.org/wiki/Neutron>`_.
10It works as a translator between libnetwork's `Container Network Model <https://github.com/docker/libnetwork/blob/master/docs/design.md#the-container-network-model>`_ (CNM) and `Neutron's networking model <https://wiki.openstack.org/wiki/Neutron/APIv2-specification>`_.
11Kuryr also acts as a `libnetwork IPAM driver <https://github.com/docker/libnetwork/blob/master/docs/ipam.md>`_.
12
13Goal
14~~~~
15
16Through Kuryr any Neutron plugin can be used as libnetwork backend with no
17additional effort. Neutron APIs are vendor agnostic and thus all Neutron
18plugins will have the capability of providing the networking backend of Docker
19for a similar small plugging snippet as they have in nova.
20
21Kuryr also takes care of binding one of a veth pair to a network interface on
22the host, e.g., Linux bridge, Open vSwitch datapath and so on.
23
24
25Kuryr Workflow - Host Networking
26--------------------------------
27Kuryr resides in each host that runs Docker containers and serves `APIs <https://github.com/docker/libnetwork/blob/master/docs/design.md#api>`_
28required for the libnetwork remote network driver. It is planned to use the
29`Adding tags to resources <https://review.openstack.org/#/c/216021/>`_
30new Neutron feature by Kuryr, to map between
31Neutron resource Id's and Docker Id's (UUID's)
32
331. libnetwork discovers Kuryr via `plugin discovery mechanism <https://github.com/docker/docker/blob/master/docs/extend/plugin_api.md#plugin-discovery>`_ *before the first request is made*
34
35 - During this process libnetwork makes a HTTP POST call on
36 ``/Plugin.Activate`` and examines the driver type, which defaults to
37 ``"NetworkDriver"`` and ``"IpamDriver"``
38 - libnetwork also calls the following two API endpoints
39
40 1. ``/NetworkDriver.GetCapabilities`` to obtain the capability of Kuryr
41 which defaults to ``"local"``
42 2. ``/IpamDriver.GetDefaultAddressSpcaces`` to get the default address
43 spaces used for the IPAM
44
452. libnetwork registers Kuryr as a remote driver
46
473. A user makes requests against libnetwork with the network driver specifier for Kuryr
48
49 - i.e., ``--driver=kuryr`` or ``-d kuryr`` **and** ``--ipam-driver=kuryr``
50 for the Docker CLI
51
524. libnetwork makes API calls against Kuryr
53
545. Kuryr receives the requests and calls Neutron APIs with `Neutron client <http://docs.openstack.org/developer/python-neutronclient/>`_
55
566. Kuryr receives the responses from Neutron and compose the responses for
57 libnetwork
58
597. Kuryr returns the responses to libnetwork
60
618. libnetwork stores the returned information to its key/value datastore
62 backend
63
64 - the key/value datastore is abstracted by `libkv <https://github.com/docker/libkv>`_
65
66
67Libnetwork User Workflow (with Kuryr as remote network driver) - Host Networking
68---------------------------------------------------------------------------------
691. A user creates a network ``foo`` with the subnet information::
70
71 $ sudo docker network create --driver=kuryr --ipam-driver=kuryr \
72 --subnet 10.0.0.0/16 --gateway 10.0.0.1 --ip-range 10.0.0.0/24 foo
73 286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364
74
75 This makes a HTTP POST call on ``/IpamDriver.RequestPool`` with the following
76 JSON data::
77
78 {
79 "AddressSpace": "global_scope",
80 "Pool": "10.0.0.0/16",
81 "SubPool": "10.0.0.0/24",
82 "Options": {},
83 "V6": false
84 }
85
86 The value of ``SubPool`` comes from the value specified in ``--ip-range``
87 option in the command above and value of ``AddressSpace`` will be ``global_scope`` or ``local_scope`` depending on value of ``capability_scope`` configuration option. Kuryr creates a subnetpool, and then returns
88 the following response::
89
90 {
91 "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376",
92 "Pool": "10.0.0.0/24",
93 }
94
95 If the ``--gateway`` was specified like the command above, another HTTP POST
96 call against ``/IpamDriver.RequestAddress`` follows with the JSON data below::
97
98 {
99 "Address": "10.0.0.1",
100 "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376",
101 "Options": {"RequestAddressType": "com.docker.network.gateway"},
102 }
103
104 As the IPAM driver Kuryr allocates a requested IP address and returns the
105 following response::
106
107 {
108 "Address": "10.0.0.1/24",
109 "Data": {}
110 }
111
112 Finally a HTTP POST call on ``/NetworkDriver.CreateNetwork`` with the
113 following JSON data::
114
115 {
116 "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
117 "IPv4Data": [{
118 "Pool": "10.0.0.0/24",
119 "Gateway": "10.0.0.1/24",
120 "AddressSpace": ""
121 }],
122 "IPv6Data": [],
123 "Options": {"com.docker.network.enable_ipv6", false, "com.docker.network.generic": {}}
124 }
125
126 The Kuryr remote network driver will then generate a Neutron API request to
127 create subnet with pool cidr and an underlying Neutron network. When the
128 Neutron subnet and network has been created, the Kuryr remote network driver
129 will generate an empty success response to the docker daemon. Kuryr tags the
130 Neutron network with the NetworkID from docker.
131
1322. A user launches a container against network ``foo``::
133
134 $ sudo docker run --net=foo -itd --name=container1 busybox
135 78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703
136
137 This makes a HTTP POST call on ``/IpamDriver.RequestAddress`` with the
138 following JSON data::
139
140 {
141 "Address": "",
142 "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376",
143 "Options": {},
144 }
145
146 The IPAM driver Kuryr sends a port creation request to neutron and returns the following response with neutron provided ip address::
147
148 {
149 "Address": "10.0.0.2/24",
150 "Data": {}
151 }
152
153
154 Then another HTTP POST call on ``/NetworkDriver.CreateEndpoint`` with the
155 following JSON data is made::
156
157 {
158 "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
159 "Interface": {
160 "AddressIPv6": "",
161 "MacAddress": "",
162 "Address": "10.0.0.2/24"
163 },
164 "Options": {
165 "com.docker.network.endpoint.exposedports": [],
166 "com.docker.network.portmap": []
167 },
168 "EndpointID": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd"
169 }
170
171 The Kuryr remote network driver then generates a Neutron API request to
172 fetch port with the matching fields for interface in the request. Kuryr
173 then updates this port's name, tagging it with endpoint ID.
174
175 Following steps are taken:
176
177 1) On the endpoint creation Kuryr examines if there's a Port with CIDR
178 that corresponds to Address or AddressIPv6 requested.
179 2) If there's a Port, Kuryr tries to reuse it without creating a new
180 Port. Otherwise it creates a new one with the given address.
181 3) Kuryr tags the Neutron port with EndpointID.
182
183 When the Neutron port has been updated, the Kuryr remote driver will
184 generate a response to the docker daemon in following form:
185 (https://github.com/docker/libnetwork/blob/master/docs/remote.md#create-endpoint)::
186
187 {
188 "Interface": {"MacAddress": "08:22:e0:a8:7d:db"}
189 }
190
191
192 On receiving success response, libnetwork makes a HTTP POST call on ``/NetworkDriver.Join`` with
193 the following JSON data::
194
195 {
196 "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
197 "SandboxKey": "/var/run/docker/netns/052b9aa6e9cd",
198 "Options": null,
199 "EndpointID": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd"
200 }
201
202 Kuryr connects the container to the corresponding neutron network by doing
203 the following steps:
204
205 1) Generate a veth pair.
206 2) Connect one end of the veth pair to the container (which is running in a
207 namespace that was created by Docker).
208 3) Perform a neutron-port-type-dependent VIF-binding to the corresponding
209 Neutron port using the VIF binding layer and depending on the specific
210 port type.
211
212 After the VIF-binding is completed, the Kuryr remote network driver
213 generates a response to the Docker daemon as specified in the libnetwork
214 documentation for a join request.
215 (https://github.com/docker/libnetwork/blob/master/docs/remote.md#join)
216
2173. A user requests information about the network::
218
219 $ sudo docker network inspect foo
220 [
221 {
222 "Name": "foo",
223 "Id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
224 "Scope": "local",
225 "Driver": "kuryr",
226 "EnableIPv6": false,
227 "IPAM": {
228 "Driver": "kuryr",
229 "Options": {},
230 "Config": [{
231 "Subnet": "10.0.0.0/16",
232 "IPRange": "10.0.0.0/24",
233 "Gateway": "10.0.0.1"
234 }]
235 },
236 "Internal": false,
237 "Containers": {
238 "78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703": {
239 "endpoint": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd",
240 "mac_address": "02:42:c0:a8:7b:cb",
241 "ipv4_address": "10.0.0.2/24",
242 "ipv6_address": ""
243 }
244 },
245 "Options": {},
246 "Labels": {}
247 }
248 ]
249
250
2514. A user connects one more container to the network::
252
253 $ sudo docker network connect foo container2
254 d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646
255
256 $ sudo docker network inspect foo
257 [
258 {
259 "Name": "foo",
260 "Id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
261 "Scope": "local",
262 "Driver": "kuryr",
263 "EnableIPv6": false,
264 "IPAM": {
265 "Driver": "kuryr",
266 "Options": {},
267 "Config": [{
268 "Subnet": "10.0.0.0/16",
269 "IPRange": "10.0.0.0/24",
270 "Gateway": "10.0.0.1"
271 }]
272 },
273 "Internal": false,
274 "Containers": {
275 "78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703": {
276 "endpoint": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd",
277 "mac_address": "02:42:c0:a8:7b:cb",
278 "ipv4_address": "10.0.0.2/24",
279 "ipv6_address": ""
280 },
281 "d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646": {
282 "endpoint": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc",
283 "mac_address": "02:42:c0:a8:7b:cc",
284 "ipv4_address": "10.0.0.3/24",
285 "ipv6_address": ""
286 }
287 },
288 "Options": {},
289 "Labels": {}
290 }
291 ]
292
293
2945. A user disconnects a container from the network::
295
296 $ CID=d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646
297 $ sudo docker network disconnect foo $CID
298
299 This makes a HTTP POST call on ``/NetworkDriver.Leave`` with the following
300 JSON data::
301
302 {
303 "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
304 "EndpointID": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc"
305 }
306
307 Kuryr remote network driver will remove the VIF binding between the
308 container and the Neutron port, and generate an empty response to the
309 Docker daemon.
310
311 Then libnetwork makes a HTTP POST call on ``/NetworkDriver.DeleteEndpoint`` with the
312 following JSON data::
313
314 {
315 "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
316 "EndpointID": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc"
317 }
318
319 Kuryr remote network driver generates a Neutron API request to delete the
320 associated Neutron port, in case the relevant port subnet is empty, Kuryr
321 also deletes the subnet object using Neutron API and generate an empty
322 response to the Docker daemon::
323
324 {}
325
326 Finally libnetwork makes a HTTP POST call on ``/IpamDriver.ReleaseAddress``
327 with the following JSON data::
328
329 {
330 "Address": "10.0.0.3",
331 "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376"
332 }
333
334 Kuryr remote IPAM driver generates a Neutron API request to delete the associated Neutron port.
335 As the IPAM driver Kuryr deallocates the IP address and returns the following response::
336
337 {}
338
3396. A user deletes the network::
340
341 $ sudo docker network rm foo
342
343 This makes a HTTP POST call against ``/NetworkDriver.DeleteNetwork`` with the
344 following JSON data::
345
346 {
347 "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364"
348 }
349
350 Kuryr remote network driver generates a Neutron API request to delete the
351 corresponding Neutron network and subnets. When the Neutron network and subnets has been deleted,
352 the Kuryr remote network driver generate an empty response to the docker
353 daemon: {}
354
355 Then another HTTP POST call on ``/IpamDriver.ReleasePool`` with the
356 following JSON data is made::
357
358 {
359 "PoolID": "941f790073c3a2c70099ea527ee3a6205e037e84749f2c6e8a5287d9c62fd376"
360 }
361
362 Kuryr delete the corresponding subnetpool and returns the following response::
363
364 {}
365
366Mapping between the CNM and the Neutron's Networking Model
367----------------------------------------------------------
368
369Kuryr communicates with Neutron via `Neutron client <http://docs.openstack.org/developer/python-neutronclient/>`_
370and bridges between libnetwork and Neutron by translating their networking models.
371The following table depicts the current mapping between libnetwork and Neutron models:
372
373===================== ======================
374libnetwork Neutron
375===================== ======================
376Network Network
377Sandbox Subnet, Port and netns
378Endpoint Port
379===================== ======================
380
381libnetwork's Sandbox and Endpoint can be mapped into Neutron's Subnet and Port,
382however, Sandbox is invisible from users directly and Endpoint is only the
383visible and editable resource entity attachable to containers from users'
384perspective. Sandbox manages information exposed by Endpoint behind the scene
385automatically.
386
387
388Notes on implementing the libnetwork remote driver API in Kuryr
389---------------------------------------------------------------
390
3911. DiscoverNew Notification:
392 Neutron does not use the information related to discovery of new resources such
393 as new nodes and therefore the implementation of this API method does nothing.
394
3952. DiscoverDelete Notification:
396 Neutron does not use the information related to discovery of resources such as
397 nodes being deleted and therefore the implementation of this API method does
398 nothing.