cc1  v2.1
CC1 source code docs
All Classes Namespaces Files Functions Variables Pages
storage_image.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 # @COPYRIGHT_begin
3 #
4 # Copyright [2010-2014] Institute of Nuclear Physics PAN, Krakow, Poland
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 # @COPYRIGHT_end
19 
20 ##
21 # @package src.cm.views.user.storage_image
22 # @alldecoratedby{src.cm.utils.decorators.user_log}
23 #
24 # @author Tomek Sośnicki <tom.sosnicki@gmail.com>
25 # @author Miłosz Zdybał <milosz.zdybal@ifj.edu.pl>
26 # @author Maciej Nabożny <mn@mnabozny.pl>
27 #
28 
29 import urllib
30 
31 from cm.utils.decorators import user_log
32 from cm.utils.exception import CMException
33 from cm.utils import log
34 from cm.utils.threads.image import DownloadImage, CreateImage
35 from cm.models.user import User
36 from cm.models.storage_image import StorageImage
37 from cm.models.vm import VM
38 from common.states import image_states
39 from common.hardware import disk_controllers, disk_filesystems, live_attach_disk_controllers
40 import os
41 import subprocess
42 
43 
44 @user_log(log=True)
45 ##
46 #
47 # Creates new StorageImage.
48 #
49 # @cmview_user
50 # @param_post{name,string}
51 # @param_post{description,string}
52 # @param_post{filesystem,int} id of the filesystem. Supported filesystems are
53 # common.hardware.disk_filesystems
54 # @param_post{size,int} size of the SystemImage to create [MB]
55 # @param_post{disk_controller}
56 #
57 # @response{dict} StorageImage.dict property of newly created StorageImage
58 #
59 def create(caller_id, name, description, filesystem, size, disk_controller):
60  if size < 1:
61  raise CMException('image_invalid_size')
62 
63  user = User.get(caller_id)
64  user.check_storage(size)
65  image = StorageImage.create(user=user, disk_controller=disk_controller, description=description, name=name,
66  size=size)
67 
68  try:
69  image.save()
70  except Exception, e:
71  log.error(caller_id, "Unable to save image to DB: %s" % str(e))
72  raise CMException('image_create')
73 
74  CreateImage(image, filesystem).start()
75  return image.dict
76 
77 
78 @user_log(log=True)
79 ##
80 #
81 # Downloads specified StorageImage.
82 #
83 # @cmview_user
84 # @param_post{name,string}
85 # @param_post{description,string}
86 # @param_post{path,string} HTTP or FTP path to StorageImage to download
87 # @param_post{disk_controller}
88 #
89 def download(caller_id, name, description, path, disk_controller):
90  user = User.get(caller_id)
91 
92  if path.startswith('/'):
93  size = os.path.getsize(path.strip())
94  else:
95  if not any([path.startswith('http://'), path.startswith('https://'), path.startswith('ftp://')]):
96  path = 'http://' + path.strip()
97 
98  # size value is taken
99  try:
100  connection = urllib.urlopen(path)
101  size = int(connection.info()["Content-Length"])
102  except IOError:
103  log.exception(caller_id, 'Cannot find image')
104  raise CMException('image_not_found')
105  except KeyError:
106  log.exception(caller_id, 'Cannot calculate size')
107  raise CMException('image_calculate_size')
108 
109  user.check_storage(size / (1024 * 1024))
110 
111  image = StorageImage.create(name=name, description=description, user=user, disk_controller=disk_controller)
112 
113  try:
114  image.save()
115  except Exception, e:
116  log.error(caller_id, "Unable to save image to DB: %s" % str(e))
117  raise CMException('image_create')
118 
119  DownloadImage(image, path, size).start()
120 
121 
122 @user_log(log=False)
123 ##
124 #
125 # Returns StorageImages.
126 #
127 # @cmview_user
128 # @response{list(dict)} StorageImage.dict property of each unlocked caller's
129 # StorageImage
130 #
131 def get_list(caller_id):
132  # retrieve list of the type requested
133  images = StorageImage.objects.exclude(state=image_states['locked']).filter(user__id=caller_id)
134  return [img.dict for img in images]
135 
136 
137 @user_log(log=True)
138 ##
139 #
140 # @cmview_user
141 # @param_post{storage_image_id,int} id of the requested StorageImage
142 #
143 # @response{dict} StorageImage.dict property of the requested StorageImage
144 #
145 def get_by_id(caller_id, storage_image_id):
146  return StorageImage.get(caller_id, storage_image_id).dict
147 
148 
149 @user_log(log=True)
150 ##
151 #
152 # Deletes given StorageImage from CM storage space. Such an StorageImage
153 # can in no way be restored. It's also deleted from database.
154 #
155 # @cmview_user
156 # @param_post{storage_image_ids,list(int)} id of the StorageImage to delete
157 #
158 def delete(caller_id, storage_image_ids):
159 
160  results = []
161  for storage_image_id in storage_image_ids:
162  image = StorageImage.get(caller_id, storage_image_id)
163 
164  if image.state != image_states['ok']:
165  results.append({'status': 'image_delete', 'data': ''})
166  continue
167 
168  image.check_attached()
169  try:
170  subprocess.call(['rm', image.path])
171  except Exception:
172  results.append({'status': 'image_delete', 'data': ''})
173  continue
174  image.state = image_states['locked']
175  image.save()
176  results.append({'status': 'ok', 'data': ''})
177 
178  return results
179 
180 
181 @user_log(log=True)
182 ##
183 #
184 # Sets Image's new attributes. Those should be get by src.cm.manager.image.get_by_id().
185 #
186 # @cmview_user
187 # @param_post{storage_image_id,int} id of the Image to edit
188 # @param_post{name,string} new Image name
189 # @param_post{description,string} new Image description
190 # @param_post{disk_controller} new Image controller optional
191 #
192 def edit(caller_id, storage_image_id, name=None, description=None, disk_controller=None):
193 
194  image = StorageImage.get(caller_id, storage_image_id)
195 
196  if not image.state in [image_states['ok'], image_states['adding']]:
197  raise CMException('image_edit')
198 
199  image.name = name or image.name
200  image.description = description or image.description
201  image.disk_controller = disk_controller or image.disk_controller
202 
203  try:
204  image.save(update_fields=['name', 'description', 'disk_controller'])
205  except:
206  raise CMException('image_edit')
207 
208 
209 @user_log(log=True)
210 ##
211 #
212 # Attaches selected StorageImage to specified VM. Such a disk may be mounted
213 # to VM so that data generated by VM could be stored on it. VM also has
214 # access to data already stored on that attached StorageImage.
215 #
216 # @cmview_user
217 # @param_post{storage_image_id,int} id of a StorageImage block device - Disk Volume Image
218 # @param_post{vm_id,int} id of the VM which StorageImage should be attached to
219 #
220 def attach(caller_id, storage_image_id, vm_id):
221  vm = VM.get(caller_id, vm_id)
222  disk = StorageImage.get(caller_id, storage_image_id)
223 
224  # Check if disk is already attached to a vm
225  if disk.vm:
226  raise CMException('image_attached')
227 
228  disk.attach(vm)
229 
230  try:
231  disk.save()
232  except:
233  raise CMException('storage_image_attach')
234 
235 
236 @user_log(log=True)
237 ##
238 #
239 # Detaches specified StorageImage from specified VM.
240 #
241 # @cmview_user
242 # @param_post{vm_id,int} id of the VM StorageImage should be detached from
243 # @param_post{storage_image_id,int} id of the StorageImage to detach
244 #
245 def detach(caller_id, storage_image_id, vm_id):
246  vm = VM.get(caller_id, vm_id)
247  disk = StorageImage.get(caller_id, storage_image_id)
248 
249  disk.detach(vm)
250 
251  try:
252  disk.save()
253  except:
254  raise CMException('storage_image_attach')
255 
256 
257 @user_log(log=True)
258 ##
259 #
260 # Converts StorageImage to SystemImage so that it may be selected as os
261 # container when creating new VM.
262 #
263 # @cmview_user
264 # @param_post{storage_image_id,int}
265 # @param_post{platform}
266 # @param_post{disk_controller}
267 # @param_post{network_device}
268 # @param_post{video_device}
269 #
270 def convert_to_system_image(caller_id, storage_image_id, platform, disk_controller, network_device, video_device):
271  image = StorageImage.get(caller_id, storage_image_id)
272  image.platform = platform
273  image.disk_controller = disk_controller
274  image.network_device = network_device
275  image.video_device = video_device
276 
277  try:
278  image.recast('cm.systemimage')
279  image.save()
280  except Exception:
281  raise CMException('image_change_type')
282 
283 
284 @user_log(log=True)
285 ##
286 #
287 # @cmview_user
288 # @response{list(dict)} common.hardware.disk_filesystems
289 #
290 def get_filesystems(caller_id):
291  return disk_filesystems
292 
293 
294 @user_log(log=True)
295 ##
296 #
297 # @cmview_user
298 # @response{list(dict)} keys: \c id, \c name, \c live_attach
299 #
300 def get_disk_controllers(caller_id):
301  return [{'id': ctrl_id,
302  'name': name,
303  'live_attach': (name in live_attach_disk_controllers)}
304  for name, ctrl_id in disk_controllers.iteritems()]
305