Merge "Improve Yardstick user-guide"
[yardstick.git] / api / resources / env_action.py
1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9 from __future__ import absolute_import
10
11 import errno
12 import logging
13 import os
14 import subprocess
15 import threading
16 import time
17 import uuid
18 import glob
19 import collections
20
21 from six.moves import configparser
22 from oslo_serialization import jsonutils
23 from docker import Client
24 from docker.utils import create_host_config
25
26 from api.database.handler import AsyncTaskHandler
27 from api.utils import influx
28 from api.utils.common import result_handler
29 from yardstick.common import constants as consts
30 from yardstick.common import utils as common_utils
31 from yardstick.common import openstack_utils
32 from yardstick.common.httpClient import HttpClient
33
34
35 logger = logging.getLogger(__name__)
36 logger.setLevel(logging.DEBUG)
37
38
39 def createGrafanaContainer(args):
40     task_id = str(uuid.uuid4())
41
42     thread = threading.Thread(target=_create_grafana, args=(task_id,))
43     thread.start()
44
45     return result_handler('success', {'task_id': task_id})
46
47
48 def _create_grafana(task_id):
49     _create_task(task_id)
50
51     client = Client(base_url=consts.DOCKER_URL)
52
53     try:
54         image = '{}:{}'.format(consts.GRAFANA_IMAGE, consts.GRAFANA_TAG)
55         if not _check_image_exist(client, image):
56             client.pull(consts.GRAFANA_IMAGE, consts.GRAFANA_TAG)
57
58         _create_grafana_container(client)
59
60         time.sleep(5)
61
62         _create_data_source()
63
64         _create_dashboard()
65
66         _update_task_status(task_id)
67     except Exception as e:
68         _update_task_error(task_id, str(e))
69         logger.exception('Error: %s', e)
70
71
72 def _create_dashboard():
73     url = 'http://admin:admin@%s:3000/api/dashboards/db' % consts.GRAFANA_IP
74     path = os.path.join(consts.REPOS_DIR, 'dashboard', '*dashboard.json')
75
76     for i in sorted(glob.iglob(path)):
77         with open(i) as f:
78             data = jsonutils.load(f)
79         HttpClient().post(url, data)
80
81
82 def _create_data_source():
83     url = 'http://admin:admin@%s:3000/api/datasources' % consts.GRAFANA_IP
84     data = {
85         "name": "yardstick",
86         "type": "influxdb",
87         "access": "proxy",
88         "url": "http://%s:8086" % consts.INFLUXDB_IP,
89         "password": "root",
90         "user": "root",
91         "database": "yardstick",
92         "basicAuth": True,
93         "basicAuthUser": "admin",
94         "basicAuthPassword": "admin",
95         "isDefault": False,
96     }
97     HttpClient().post(url, data)
98
99
100 def _create_grafana_container(client):
101     ports = [3000]
102     port_bindings = {k: k for k in ports}
103     restart_policy = {"MaximumRetryCount": 0, "Name": "always"}
104     host_config = client.create_host_config(port_bindings=port_bindings,
105                                             restart_policy=restart_policy)
106
107     container = client.create_container(image='%s:%s' % (consts.GRAFANA_IMAGE,
108                                                          consts.GRAFANA_TAG),
109                                         ports=ports,
110                                         detach=True,
111                                         tty=True,
112                                         host_config=host_config)
113     client.start(container)
114
115
116 def _check_image_exist(client, t):
117     return any(t in a['RepoTags'][0] for a in client.images() if a['RepoTags'])
118
119
120 def createInfluxDBContainer(args):
121     task_id = str(uuid.uuid4())
122
123     thread = threading.Thread(target=_create_influxdb, args=(task_id,))
124     thread.start()
125
126     return result_handler('success', {'task_id': task_id})
127
128
129 def _create_influxdb(task_id):
130     _create_task(task_id)
131
132     client = Client(base_url=consts.DOCKER_URL)
133
134     try:
135         _change_output_to_influxdb()
136
137         if not _check_image_exist(client, '%s:%s' % (consts.INFLUXDB_IMAGE,
138                                                      consts.INFLUXDB_TAG)):
139             client.pull(consts.INFLUXDB_IMAGE, tag=consts.INFLUXDB_TAG)
140
141         _create_influxdb_container(client)
142
143         time.sleep(5)
144
145         _config_influxdb()
146
147         _update_task_status(task_id)
148     except Exception as e:
149         _update_task_error(task_id, str(e))
150         logger.debug('Error: %s', e)
151
152
153 def _create_influxdb_container(client):
154
155     ports = [8083, 8086]
156     port_bindings = {k: k for k in ports}
157     restart_policy = {"MaximumRetryCount": 0, "Name": "always"}
158     host_config = client.create_host_config(port_bindings=port_bindings,
159                                             restart_policy=restart_policy)
160
161     container = client.create_container(image='%s:%s' % (consts.INFLUXDB_IMAGE,
162                                                          consts.INFLUXDB_TAG),
163                                         ports=ports,
164                                         detach=True,
165                                         tty=True,
166                                         host_config=host_config)
167     client.start(container)
168
169
170 def _config_influxdb():
171     try:
172         client = influx.get_data_db_client()
173         client.create_user(consts.INFLUXDB_USER,
174                            consts.INFLUXDB_PASS,
175                            consts.INFLUXDB_DB_NAME)
176         client.create_database(consts.INFLUXDB_DB_NAME)
177         logger.info('Success to config influxDB')
178     except Exception as e:
179         logger.debug('Failed to config influxDB: %s', e)
180
181
182 def _change_output_to_influxdb():
183     common_utils.makedirs(consts.CONF_DIR)
184
185     parser = configparser.ConfigParser()
186     parser.read(consts.CONF_SAMPLE_FILE)
187
188     parser.set('DEFAULT', 'dispatcher', 'influxdb')
189     parser.set('dispatcher_influxdb', 'target',
190                'http://%s:8086' % consts.INFLUXDB_IP)
191
192     with open(consts.CONF_FILE, 'w') as f:
193         parser.write(f)
194
195
196 def prepareYardstickEnv(args):
197     task_id = str(uuid.uuid4())
198
199     thread = threading.Thread(target=_prepare_env_daemon, args=(task_id,))
200     thread.start()
201
202     return result_handler('success', {'task_id': task_id})
203
204
205 def _already_source_openrc():
206     """Check if openrc is sourced already"""
207     return all(os.environ.get(k) for k in ['OS_AUTH_URL', 'OS_USERNAME',
208                                            'OS_PASSWORD', 'EXTERNAL_NETWORK'])
209
210
211 def _prepare_env_daemon(task_id):
212     _create_task(task_id)
213
214     try:
215         _create_directories()
216
217         rc_file = consts.OPENRC
218
219         if not _already_source_openrc():
220             if not os.path.exists(rc_file):
221                 installer_ip = os.environ.get('INSTALLER_IP', '192.168.200.2')
222                 installer_type = os.environ.get('INSTALLER_TYPE', 'compass')
223                 _get_remote_rc_file(rc_file, installer_ip, installer_type)
224                 _source_file(rc_file)
225                 _append_external_network(rc_file)
226             _source_file(rc_file)
227
228         _clean_images()
229
230         _load_images()
231
232         _update_task_status(task_id)
233     except Exception as e:
234         _update_task_error(task_id, str(e))
235         logger.debug('Error: %s', e)
236
237
238 def _create_directories():
239     common_utils.makedirs(consts.CONF_DIR)
240
241
242 def _source_file(rc_file):
243     common_utils.source_env(rc_file)
244
245
246 def _get_remote_rc_file(rc_file, installer_ip, installer_type):
247
248     os_fetch_script = os.path.join(consts.RELENG_DIR, consts.FETCH_SCRIPT)
249
250     try:
251         cmd = [os_fetch_script, '-d', rc_file, '-i', installer_type,
252                '-a', installer_ip]
253         p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
254         p.communicate()
255
256         if p.returncode != 0:
257             logger.debug('Failed to fetch credentials from installer')
258     except OSError as e:
259         if e.errno != errno.EEXIST:
260             raise
261
262
263 def _append_external_network(rc_file):
264     neutron_client = openstack_utils.get_neutron_client()
265     networks = neutron_client.list_networks()['networks']
266     try:
267         ext_network = next(n['name'] for n in networks if n['router:external'])
268     except StopIteration:
269         logger.warning("Can't find external network")
270     else:
271         cmd = 'export EXTERNAL_NETWORK=%s' % ext_network
272         try:
273             with open(rc_file, 'a') as f:
274                 f.write(cmd + '\n')
275         except OSError as e:
276             if e.errno != errno.EEXIST:
277                 raise
278
279
280 def _clean_images():
281     cmd = [consts.CLEAN_IMAGES_SCRIPT]
282     p = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=consts.REPOS_DIR)
283     output = p.communicate()[0]
284     logger.debug('The result is: %s', output)
285
286
287 def _load_images():
288     cmd = [consts.LOAD_IMAGES_SCRIPT]
289     p = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=consts.REPOS_DIR)
290     output = p.communicate()[0]
291     logger.debug('The result is: %s', output)
292
293
294 def _create_task(task_id):
295     async_handler = AsyncTaskHandler()
296     task_dict = {
297         'task_id': task_id,
298         'status': 0
299     }
300     async_handler.insert(task_dict)
301
302
303 def _update_task_status(task_id):
304     async_handler = AsyncTaskHandler()
305
306     task = async_handler.get_task_by_taskid(task_id)
307     async_handler.update_status(task, 1)
308
309
310 def _update_task_error(task_id, error):
311     async_handler = AsyncTaskHandler()
312
313     task = async_handler.get_task_by_taskid(task_id)
314     async_handler.update_status(task, 2)
315     async_handler.update_error(task, error)
316
317
318 def update_openrc(args):
319     try:
320         openrc_vars = args['openrc']
321     except KeyError:
322         return result_handler(consts.API_ERROR, 'openrc must be provided')
323     else:
324         if not isinstance(openrc_vars, collections.Mapping):
325             return result_handler(consts.API_ERROR, 'args should be a dict')
326
327     lines = ['export {}={}\n'.format(k, v) for k, v in openrc_vars.items()]
328     logger.debug('Writing: %s', ''.join(lines))
329
330     logger.info('Writing openrc: Writing')
331     common_utils.makedirs(consts.CONF_DIR)
332
333     with open(consts.OPENRC, 'w') as f:
334         f.writelines(lines)
335     logger.info('Writing openrc: Done')
336
337     logger.info('Source openrc: Sourcing')
338     try:
339         _source_file(consts.OPENRC)
340     except Exception as e:
341         logger.exception('Failed to source openrc')
342         return result_handler(consts.API_ERROR, str(e))
343     logger.info('Source openrc: Done')
344
345     return result_handler(consts.API_SUCCESS, {'openrc': openrc_vars})