1 ##############################################################################
2 # Copyright (c) 2017 Huawei Technologies Co.,Ltd.
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 ##############################################################################
16 from api import ApiResource
17 from api.database.v2.handlers import V2ImageHandler
18 from api.database.v2.handlers import V2EnvironmentHandler
19 from yardstick.common.utils import result_handler
20 from yardstick.common.utils import source_env
21 from yardstick.common import openstack_utils
22 from yardstick.common.openstack_utils import get_glance_client
23 from yardstick.common import constants as consts
25 LOG = logging.getLogger(__name__)
26 LOG.setLevel(logging.DEBUG)
30 'path': os.path.join(consts.IMAGE_DIR, 'yardstick-image.img'),
31 'url': 'http://artifacts.opnfv.org/yardstick/images/yardstick-image.img'
34 'path': os.path.join(consts.IMAGE_DIR, 'xenial-server-cloudimg-amd64-disk1.img'),
35 'url': 'cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img'
38 'path': os.path.join(consts.IMAGE_DIR, 'cirros-0.3.5-x86_64-disk.img'),
39 'url': 'http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img'
44 class V2Images(ApiResource):
48 source_env(consts.OPENRC)
50 return result_handler(consts.API_ERROR, 'source openrc error')
52 image_list = openstack_utils.list_images()
54 if image_list is False:
55 return result_handler(consts.API_ERROR, 'get images error')
57 images = {i.name: format_image_info(i) for i in image_list}
59 return result_handler(consts.API_SUCCESS, {'status': 1, 'images': images})
62 return self._dispatch_post()
64 def load_image(self, args):
66 image_name = args['name']
68 return result_handler(consts.API_ERROR, 'image name must provided')
70 if image_name not in IMAGE_MAP:
71 return result_handler(consts.API_ERROR, 'wrong image name')
73 thread = threading.Thread(target=self._do_load_image, args=(image_name,))
75 return result_handler(consts.API_SUCCESS, {'image': image_name})
77 def upload_image(self, args):
79 image_file = args['file']
81 return result_handler(consts.API_ERROR, 'file must be provided')
84 environment_id = args['environment_id']
86 return result_handler(consts.API_ERROR, 'environment_id must be provided')
89 uuid.UUID(environment_id)
91 return result_handler(consts.API_ERROR, 'invalid environment id')
93 environment_handler = V2EnvironmentHandler()
95 environment = environment_handler.get_by_uuid(environment_id)
97 return result_handler(consts.API_ERROR, 'no such environment')
99 file_path = os.path.join(consts.IMAGE_DIR, image_file.filename)
100 LOG.info('saving file')
101 image_file.save(file_path)
103 LOG.info('loading image')
104 self._load_image(image_file.filename, file_path)
106 LOG.info('creating image in DB')
107 image_handler = V2ImageHandler()
108 image_id = str(uuid.uuid4())
111 'name': image_file.filename,
112 'environment_id': environment_id
114 image_handler.insert(image_init_data)
116 LOG.info('update image in environment')
117 if environment.image_id:
118 image_list = environment.image_id.split(',')
119 image_list.append(image_id)
120 new_image_id = ','.join(image_list)
122 new_image_id = image_id
124 environment_handler.update_attr(environment_id, {'image_id': new_image_id})
126 return result_handler(consts.API_SUCCESS, {'uuid': image_id})
128 def upload_image_by_url(self, args):
132 return result_handler(consts.API_ERROR, 'url must be provided')
135 environment_id = args['environment_id']
137 return result_handler(consts.API_ERROR, 'environment_id must be provided')
140 uuid.UUID(environment_id)
142 return result_handler(consts.API_ERROR, 'invalid environment id')
144 environment_handler = V2EnvironmentHandler()
146 environment = environment_handler.get_by_uuid(environment_id)
148 return result_handler(consts.API_ERROR, 'no such environment')
150 thread = threading.Thread(target=self._do_upload_image_by_url, args=(url,))
153 file_name = url.split('/')[-1]
155 LOG.info('creating image in DB')
156 image_handler = V2ImageHandler()
157 image_id = str(uuid.uuid4())
161 'environment_id': environment_id
163 image_handler.insert(image_init_data)
165 LOG.info('update image in environment')
166 if environment.image_id:
167 image_list = environment.image_id.split(',')
168 image_list.append(image_id)
169 new_image_id = ','.join(image_list)
171 new_image_id = image_id
173 environment_handler.update_attr(environment_id, {'image_id': new_image_id})
175 return result_handler(consts.API_SUCCESS, {'uuid': image_id})
177 def delete_image(self, args):
179 image_name = args['name']
181 return result_handler(consts.API_ERROR, 'image name must provided')
183 if image_name not in IMAGE_MAP:
184 return result_handler(consts.API_ERROR, 'wrong image name')
186 glance_client = get_glance_client()
188 image = next((i for i in glance_client.images.list() if i.name == image_name))
189 except StopIteration:
190 return result_handler(consts.API_ERROR, 'can not find image')
192 glance_client.images.delete(image.id)
194 return result_handler(consts.API_SUCCESS, {})
196 def _do_upload_image_by_url(self, url):
197 file_name = url.split('/')[-1]
198 path = os.path.join(consts.IMAGE_DIR, file_name)
200 LOG.info('download image')
201 self._download_image(url, path)
203 LOG.info('loading image')
204 self._load_image(file_name, path)
206 def _do_load_image(self, image_name):
207 if not os.path.exists(IMAGE_MAP[image_name]['path']):
208 self._download_image(IMAGE_MAP[image_name]['url'],
209 IMAGE_MAP[image_name]['path'])
211 self._load_image(image_name, IMAGE_MAP[image_name]['path'])
213 def _load_image(self, image_name, image_path):
214 LOG.info('source openrc')
215 source_env(consts.OPENRC)
217 LOG.info('load image')
218 glance_client = get_glance_client()
219 image = glance_client.images.create(name=image_name,
222 container_format='bare')
223 with open(image_path, 'rb') as f:
224 glance_client.images.upload(image.id, f)
228 def _download_image(self, url, path):
229 start = datetime.datetime.now().replace(microsecond=0)
231 LOG.info('download image from: %s', url)
232 self._download_file(url, path)
234 end = datetime.datetime.now().replace(microsecond=0)
235 LOG.info('download image success, total: %s s', end - start)
237 def _download_handler(self, start, end, url, filename):
239 headers = {'Range': 'bytes=%d-%d' % (start, end)}
240 r = requests.get(url, headers=headers, stream=True)
242 with open(filename, "r+b") as fp:
247 def _download_file(self, url, path, num_thread=5):
249 r = requests.head(url)
251 file_size = int(r.headers['content-length'])
252 except (TypeError, ValueError):
255 with open(path, 'wb') as f:
256 f.truncate(file_size)
259 part = file_size // num_thread
260 for i in range(num_thread):
262 end = start + part if i != num_thread - 1 else file_size
264 kwargs = {'start': start, 'end': end, 'url': url, 'filename': path}
265 t = threading.Thread(target=self._download_handler, kwargs=kwargs)
268 thread_list.append(t)
270 for t in thread_list:
274 class V2Image(ApiResource):
275 def get(self, image_id):
279 return result_handler(consts.API_ERROR, 'invalid image id')
281 image_handler = V2ImageHandler()
283 image = image_handler.get_by_uuid(image_id)
285 return result_handler(consts.API_ERROR, 'no such image id')
287 images = openstack_utils.list_images()
289 image = next((i for i in images if i.name == image.name))
290 except StopIteration:
293 return_image = format_image_info(image)
294 return_image['id'] = image_id
296 return result_handler(consts.API_SUCCESS, {'image': return_image})
298 def delete(self, image_id):
302 return result_handler(consts.API_ERROR, 'invalid image id')
304 image_handler = V2ImageHandler()
306 image = image_handler.get_by_uuid(image_id)
308 return result_handler(consts.API_ERROR, 'no such image id')
310 LOG.info('delete image in openstack')
311 glance_client = get_glance_client()
313 image_o = next((i for i in glance_client.images.list() if i.name == image.name))
314 except StopIteration:
315 return result_handler(consts.API_ERROR, 'can not find image')
317 glance_client.images.delete(image_o.id)
319 LOG.info('delete image in environment')
320 environment_id = image.environment_id
321 environment_handler = V2EnvironmentHandler()
322 environment = environment_handler.get_by_uuid(environment_id)
323 image_list = environment.image_id.split(',')
324 image_list.remove(image_id)
325 environment_handler.update_attr(environment_id, {'image_id': ','.join(image_list)})
327 LOG.info('delete image in DB')
328 image_handler.delete_by_uuid(image_id)
330 return result_handler(consts.API_SUCCESS, {'image': image_id})
333 def format_image_info(image):
339 image_dict['name'] = image.name
340 image_dict['size'] = float(image.size) / 1024 / 1024
341 image_dict['status'] = image.status.upper()
342 image_dict['time'] = image.updated_at