Step 1: Move vm scripts to the right place
[integration/test.git] / test / csit / libraries / Docker.py
1 """
2 Docker library for OpenDaylight project robot system test framework.
3 Authors: Marcus Williams - irc @ mgkwill - Intel Inc.
4 Updated: 2015-05-05
5
6 *Copyright (c) 2015 Intel Corp. and others.  All rights reserved.
7 *
8 * This program and the accompanying materials are made available under the
9 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
10 * and is available at http://www.eclipse.org/legal/epl-v10.html
11 """
12
13 import docker
14 from robot.api import logger
15
16
17 def docker_create(docker_image_name, passed_args_dict=None):
18     """
19     Args:
20         :param docker_image_name: A string that describes the docker image to use
21             example 'socketplane/openvswitch'
22
23         :param passed_args_dict: keyword docker-py create container args. defaults to command=None,
24             hostname=None, user=None, detach=False, stdin_open=False, tty=False,
25             mem_limit=0, ports=None, environment=None, dns=None, volumes=None,
26             volumes_from=None, network_disabled=False, name=None, entrypoint=None,
27             cpu_shares=None, working_dir=None, domainname=None, memswap_limit=0,
28             cpuset=None, host_config=None
29
30     Returns:
31         :returns dict: docker-info - identifying info about docker container created
32     """
33     logger.info("Creating docker %s" % docker_image_name)
34     logger.info(passed_args_dict)
35     docker_client = docker_get_client()
36
37     default_args_dict = dict(command=None,
38                              hostname=None,
39                              user=None,
40                              detach=False,
41                              stdin_open=False,
42                              tty=False,
43                              mem_limit=0,
44                              ports=None,
45                              environment=None,
46                              dns=None,
47                              volumes=None,
48                              volumes_from=None,
49                              network_disabled=False,
50                              name=None,
51                              entrypoint=None,
52                              cpu_shares=None,
53                              working_dir=None,
54                              domainname=None,
55                              memswap_limit=0,
56                              cpuset=None,
57                              host_config=None
58                              )
59     args_dict = docker_process_args(passed_args_dict, default_args_dict, "docker_create")
60
61     docker_client.images(name=docker_image_name)
62     docker_uid_dict = docker_client\
63         .create_container(docker_image_name, **args_dict)
64     docker_info = docker_client.inspect_container(docker_uid_dict.get("Id"))
65     return docker_info
66
67
68 def docker_start(docker_name, passed_args_dict=None):
69     """Start docker container.
70
71     Args:
72         :param docker_name: A string that describes the docker image to use.
73             Either the uid or docker container name must be used.
74
75         :param passed_args_dict: keyword docker-py start container args. defaults to binds=None,
76             port_bindings=None, lxc_conf=None, publish_all_ports=False,
77             links=None, privileged=False, dns=None, dns_search=None,
78             volumes_from=None, network_mode=None, restart_policy=None,
79             cap_add=None, cap_drop=None, devices=None, extra_hosts=None
80     Returns:
81         :returns bool: returns false if container fails to start
82             and true otherwise
83     """
84     logger.info("Starting docker %s" % docker_name)
85     logger.info(passed_args_dict)
86     docker_client = docker_get_client()
87
88     default_args_dict = dict(binds=None,
89                              port_bindings=None,
90                              lxc_conf=None,
91                              publish_all_ports=False,
92                              links=None,
93                              privileged=False,
94                              dns=None,
95                              dns_search=None,
96                              volumes_from=None,
97                              network_mode=None,
98                              restart_policy=None,
99                              cap_add=None,
100                              cap_drop=None,
101                              devices=None,
102                              extra_hosts=None
103                              )
104     args_dict = docker_process_args(passed_args_dict, default_args_dict, "docker_start")
105
106     docker_client.start(docker_name, **args_dict)
107
108     if "True" in str(docker_client.inspect_container(docker_name)
109                      .get("State").get("Running")):
110         logger.info("Started docker %s successfully" % docker_name)
111         return True
112     else:
113         logger.info("Starting docker %s failed" % docker_name)
114         return False
115
116
117 def docker_remove(docker_name, passed_args_dict=None):
118     """Remove docker container.
119
120     Args:
121         :param docker_name: A string that describes the docker image to use
122             example 'socketplane/openvswitch'
123
124         :param passed_args_dict: keyword docker-py remove container args. defaults to v=False,
125             link=False, force=False
126
127     Returns:
128         :returns bool: True if container was removed false otherwise
129     """
130     logger.info("Removing docker %s" % docker_name)
131     logger.info(passed_args_dict)
132     docker_client = docker_get_client()
133
134     default_args_dict = dict(v=False,
135                              link=False,
136                              force=False
137                              )
138     args_dict = docker_process_args(passed_args_dict, default_args_dict, "docker_remove")
139
140     docker_client.remove_container(docker_name, **args_dict)
141     docker_containers = docker_client.containers(all=True)
142     for container in docker_containers:
143         if docker_name in container.get("Id") or docker_name in container.get("Names"):
144             logger.info("Removing docker %s failed" % docker_name)
145             return False
146     logger.info("Removed docker %s successfully" % docker_name)
147     return True
148
149
150 def docker_stop(docker_name, timeout=10):
151     """Stop docker container.
152
153     Args:
154         :param docker_name: A string that describes the docker image to use.
155             Either the uid or docker container name must be used.
156
157         :param timeout: docker-py stop container args. defaults to timeout=10
158     Returns:
159         :returns bool: returns false if container fails to stop
160             and true otherwise
161     """
162     logger.info("Stopping docker %s with timeout %d" % (docker_name, timeout))
163     docker_client = docker_get_client()
164
165     docker_client.stop(docker_name, timeout)
166
167     if "False" in str(docker_client.inspect_container(docker_name)
168                       .get("State").get("Running")):
169         logger.info("Stopped docker %s successfully" % docker_name)
170         return True
171     else:
172         logger.debug("Stopping docker %s failed" % docker_name)
173         return False
174
175
176 def docker_return_logs(docker_name, passed_args_dict=None):
177     """Return docker container logs.
178
179     Args:
180         :param docker_name: A string that describes the docker image to use.
181             Either the uid or docker container name must be used.
182
183         :param passed_args_dict: keyword docker-py logs container args. defaults to
184             stdout=True, stderr=True, stream=False, timestamps=False, tail='all'
185     Returns:
186         :returns string: returns a string containing docker logs
187     """
188     logger.info("Returning logs for docker %s" % docker_name)
189     logger.info(passed_args_dict)
190     docker_client = docker_get_client()
191
192     default_args_dict = dict(stdout=True,
193                              stderr=True,
194                              stream=False,
195                              timestamps=False,
196                              tail='all'
197                              )
198     args_dict = docker_process_args(passed_args_dict, default_args_dict, "docker_return_logs")
199
200     return docker_client.logs(docker_name, **args_dict)
201
202
203 def docker_execute(docker_name, cmd, passed_args_dict=None):
204     """Run a command on a docker container.
205
206     Args:
207         :param docker_name: A string that describes the docker image to use.
208             Either the uid or docker container name must be used.
209
210         :param cmd: A string of the command to run
211             Example 'ip a'
212
213         :param passed_args_dict: dictionary of key word
214         docker-py exec container args. defaults to detach=False,
215             stdout=True, stderr=True, stream=False, tty=False
216     Returns:
217         :returns string: returns string representing the results of the command
218
219     NOTE: In docker-py version >=1.2 execute will be deprecated in
220         favor of exec_create and exec_start
221     """
222     logger.info("Executing command %s on docker %s" % (cmd, docker_name))
223     logger.info(passed_args_dict)
224     docker_client = docker_get_client()
225
226     default_args_dict = dict(detach=False,
227                              stdout=True,
228                              stderr=True,
229                              stream=False,
230                              tty=False
231                              )
232     args_dict = docker_process_args(passed_args_dict, default_args_dict, "docker_execute")
233
234     return docker_client.execute(docker_name, cmd, **args_dict)
235
236
237 def docker_get_ip4(docker_name):
238     """Inspects a docker container and returns its IP address.
239
240     Args:
241         :param docker_name: A string that describes the docker image to use.
242             Either the uid or docker container name must be used.
243
244     Returns:
245         :returns string: returns string of IP address
246     """
247     logger.info("Getting IP of docker %s" % docker_name)
248     docker_client = docker_get_client()
249     return str(docker_client.inspect_container(docker_name)
250                .get("NetworkSettings")
251                .get("IPAddress"))
252
253
254 def docker_ping(docker_name, ip, count=3):
255     """Pings from a docker container and returns results.
256
257     Args:
258         :param docker_name: A string that describes the docker image to use.
259             Either the uid or docker container name must be used.
260
261         :param ip: A string of the IP address to ping
262
263         :param count: An integer of the count to ping
264
265     Returns:
266         :returns string: returns string of results
267     """
268     logger.info("Pinging from docker %s to %s %d times" % (docker_name, ip, count))
269     ping_cmd = str(ip) + "ping -c " + str(count)
270     return docker_execute(docker_name, ping_cmd)
271
272
273 def docker_list_containers(passed_args_dict=None):
274     """Return a list of docker containers.
275
276        Returns:
277         :returns list: returns list of docker
278             containers in following format:
279             [{'Id': u'069a56ec06f965f98efa752467737faa58431ebb471bc51e9b2bd485fcc4916c'},
280             {'Id': u'769aff6170eec78e7c502fea4770cfbb7b7e53a2dc44070566d01e18b6d57c14'}]
281     """
282     logger.info("Listing docker containers")
283     logger.info(passed_args_dict)
284
285     default_args_dict = dict(quiet=True,
286                              all=True,
287                              trunc=True,
288                              latest=False,
289                              since=None,
290                              before=None,
291                              limit=-1,
292                              size=False,
293                              filters=None
294                              )
295     args_dict = docker_process_args(passed_args_dict, default_args_dict,
296                                     "docker_list_containers")
297
298     return docker.Client(
299         base_url='unix://var/run/docker.sock',
300         timeout=10)\
301         .containers(**args_dict)
302
303
304 def docker_create_host_config(passed_args_dict):
305     """Return a list of docker create host config for port bindings.
306
307        Parameters:
308         :param passed_args_dict: dictionary of the keyword values to use.
309
310        Returns:
311         :returns list: returns host config for a container
312             create command in following format:
313             {'PortBindings': {'6640/tcp': [{'HostIp': '', 'HostPort': '6640'}],
314             '6653/tcp': [{'HostIp': '', 'HostPort': '6653'}],
315             '9001/tcp': [{'HostIp': '', 'HostPort': '9001'}]}}
316     """
317     logger.info("Creating host config.")
318
319     default_args_dict = dict(binds=None,
320                              port_bindings=None,
321                              lxc_conf=None,
322                              publish_all_ports=False,
323                              links=None,
324                              privileged=False,
325                              dns=None,
326                              dns_search=None,
327                              volumes_from=None,
328                              network_mode=None,
329                              restart_policy=None,
330                              cap_add=None,
331                              cap_drop=None,
332                              devices=None,
333                              extra_hosts=None
334                              )
335     args_dict = docker_process_args(passed_args_dict, default_args_dict,
336                                     "docker_create_host_config")
337
338     return docker.utils.create_host_config(**args_dict)
339
340
341 def docker_process_args(passed_args_dict, default_args_dict, method_name):
342     """Accepts two dicts and combines them preferring passed
343         args while filling unspecified args with default values.
344
345     Parameters:
346         :param passed_args_dict: A dict of the passed keyword args for the method.
347
348         :param default_args_dict: A dict of the default keyword args for the method.
349
350     Returns:
351         :returns dict: returns dict containing passed args, with
352         defaults for all other keyword args.
353     """
354     logger.info("Processing args for %s method" % method_name)
355     logger.info(passed_args_dict)
356     logger.info(default_args_dict)
357     processed_args_dict = {}
358
359     if passed_args_dict is None:
360         passed_args_dict = {}
361
362     try:
363         for key in default_args_dict:
364             if key in passed_args_dict:
365                 processed_args_dict[key] = passed_args_dict[key]
366             else:
367                 processed_args_dict[key] = default_args_dict[key]
368     except TypeError:
369         logger.debug("Error: One or both of the passed arguments is not a dictionary")
370
371     return processed_args_dict
372
373
374 def docker_get_client(*passed_args_dict):
375     """Returns docker-py client.
376
377     Parameters:
378         :param passed_args_dict: dictionary of the keyword values to use.
379
380     Returns:
381         :returns obj: returns docker-py client object.
382     """
383     default_args_dict = dict(base_url="unix://var/run/docker.sock",
384                              version=None,
385                              timeout=10,
386                              tls=False)
387     args_dict = docker_process_args(passed_args_dict, default_args_dict, "docker_get_client")
388     return docker.Client(**args_dict)