cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
volume.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 from common.hardware import disk_filesystems, disk_controllers
20 from common.states import image_access, image_types
21 from datetime import datetime
22 from ec2.base.action import Action, CLMException
23 from ec2.error import MissingParameter, UndefinedError, InvalidVolumeID, \
24  DiskImageSizeTooLarge, InvalidParameterValue, VolumeInUse, InvalidVolume, \
25  InvalidInstanceID, InvalidFilter, InvalidAttachment
26 from ec2.helpers.entities import Entity
27 from ec2.helpers.filters import applyEc2Filters, validateEc2Filters
28 from ec2.helpers.parse import parseFilters, parseID, parseIDs, parseClmDate
29 
30 """@package src.ec2.volume
31 EC2 actions for volumes
32 
33 @author Oleksandr Gituliar <gituliar@gmail.com>
34 @author Łukasz Chrząszcz <l.chrzaszcz@gmail.com>
35 """
36 
37 # deleting nie jest potrzebne, bo do razu się usuwa
38 STATE = {
39  0: 'available', # ok
40  1: 'in-use', # locked
41  2: 'creating', # adding
42  3: 'error', # failed
43  4: 'deleted', # unavailable
44 }
45 
46 SIZE_RATIO = 1024; # EC2 uses GiB, we use MiB
47 
48 def get_volume_status(clm_volume_state):
49  if clm_volume_state == 0:
50  return 'available'
51  return 'failed'
52 
53 class CreateVolume(Action):
54  def _execute(self):
55  try:
56  size = int(self.parameters['Size']);
57  except KeyError, error:
58  raise MissingParameter(parameter=error.args[0])
59  except ValueError, error:
60  raise InvalidParameterValue
61 
62  ext4_id = disk_filesystems['ext4']
63 
64  volume = {}
65 
66  try:
67  volume_dict = {
68  'name' : 'EC2 Volume',
69  'size' : size * SIZE_RATIO,
70  'filesystem' : ext4_id,
71  'disk_controller' : disk_controllers['virtio'],
72  'description' : 'Storage created by EC2 API'
73  }
74  volume = self.cluster_manager.user.storage_image.create(volume_dict)
75  except CLMException, error:
76  if error.status == 'image_create':
77  raise UndefinedError
78  if error.status == 'user_storage_limit':
79  raise DiskImageSizeTooLarge
80  raise UndefinedError
81 
82  try:
83  edit_dict = {'storage_image_id' : volume['storage_image_id'],
84  'name' : 'vol-' + str(volume['storage_image_id']),
85  'description' : 'Storage created by EC2 API',
86  'disk_controller' : disk_controllers['virtio']}
87 
88 
89  self.cluster_manager.user.storage_image.edit( edit_dict )
90  except:
91  print 'Changing name for newly created storage image failed!'
92  pass # we can ignore error here, because it's not an essential operation
93 
94  return {
95  'volume_id' : volume['storage_image_id'],
96  'size' : size,
97  }
98 
99 class DescribeVolumes(Action):
100 
101  translation_filters = {'size' : 'size',
102  'volume-id' : 'volumeId',
103  'status' : 'status'}
104 
105  available_filters = ['size', 'volume-id', 'status']
106 
107  def _execute(self):
108  volume_ids = []
109  try:
110  for param, value in self.parameters.iteritems():
111  if param.startswith('VolumeId'):
112  volume_ids.append(value)
113  except ValueError:
114  raise InvalidParameterValue
115 
116  if volume_ids:
117  volume_ids = parseIDs(volume_ids, Entity.volume)
118  if not volume_ids:
119  raise InvalidParameterValue
120 
121  volumes = []
122 
123  filters = parseFilters(self.parameters)
124  if not validateEc2Filters(filters, self.available_filters):
125  raise InvalidFilter
126 
127 
128  # if extra arguments weren't given
129  clm_volumes = []
130  if not volume_ids:
131  clm_volumes += self.cluster_manager.user.storage_image.get_list()
132 
133  # and if they were
134  else:
135  try:
136  for volume_id in volume_ids:
137  clm_volumes.append(self.cluster_manager.user.storage_image.get_by_id({'storage_image_id':volume_id}))
138  except CLMException, error:
139  if error.status == 'storage_image_get':
140  raise InvalidVolume.NotFound
141  else:
142  raise UndefinedError
143 
144  for clm_volume in clm_volumes:
145  create_time = clm_volume['creation_date']
146  volume = {
147  'attachTime': None,
148  'createTime': parseClmDate(create_time),
149  'size': clm_volume['size'] / SIZE_RATIO,
150  'status': get_volume_status(clm_volume['state']),
151  'volume-id': clm_volume['storage_image_id'],
152  'tags': [{
153  'key': 'Name',
154  'value': clm_volume['name']
155  }]
156  }
157  volumes.append(volume)
158 
159 
160  if filters.get('size'):
161  for size in filters['size']:
162  size = str(int(size) / SIZE_RATIO)
163 
164  if filters.get('status'):
165  for state in filters['status']:
166  state = [k for k, v in STATE.iteritems() if v == STATE.get(state) ] # ?? wymaga testu
167 
168 
169 # attachment.attach-time - sprawdź
170 # attachment.delete-on-termination - ?
171 # attachment.device - nie
172 # attachment.instance-id - tak
173 # attachment.status - tak
174 # availability-zone- ?
175 # create-time - chyba tak
176 # size - TEST
177 # snapshot-id - nie
178 # status - tak TEST
179 
180 # creating | available | in-use | deleting | deleted | error
181 
182 # tag-key- ?
183 # volume-id - tak TEST
184 # volume-type - nie
185 
186  volumes = applyEc2Filters(volumes, filters)
187 
188  return {
189  'volumes': volumes,
190  }
191 
192 
193 class AttachVolume(Action):
194  def _execute(self):
195  try:
196  try:
197  volume_id_ec2 = self.parameters['VolumeId']
198  volume_id = parseID(volume_id_ec2, Entity.volume)
199  if not volume_id:
200  raise InvalidParameterValue
201  volume_id = int(volume_id)
202  except KeyError, error:
203  raise InvalidVolumeID.Malformed
204 
205  try:
206  instance_id_ec2 = self.parameters['InstanceId']
207  instance_id = parseID(instance_id_ec2, Entity.instance)
208  if not instance_id:
209  raise InvalidParameterValue
210  instance_id = int(instance_id)
211  except KeyError, error:
212  raise MissingParameter(parameter=error.args[0])
213 
214  except ValueError, error:
215  raise InvalidVolumeID.Malformed
216 
217  try:
218  self.cluster_manager.user.storage_image.attach({'storage_image_id' : volume_id ,
219  'vm_id' : instance_id})
220  except CLMException, error:
221  if error.status == 'image_attached':
222  raise VolumeInUse
223  if error.status == 'storage_image_get':
224  raise InvalidVolume.NotFound
225  if error.status == 'user_permission' or error.status == 'vm_get':
226  raise InvalidInstanceID.NotFound(image_id=instance_id_ec2)
227 
228  if error.status == 'storage_image_attach':
229  raise UndefinedError
230  raise UndefinedError
231 
232  return {'volume_id' : volume_id_ec2,
233  'instance_id' : instance_id_ec2,
234  'device' : 'not implemented',
235  'status' : 'attaching'}
236 
237 
238 
239 class DetachVolume(Action):
240  def _execute(self):
241  try:
242  volume_id = self.parameters['VolumeId']
243  volume_id = parseID(volume_id, Entity.volume)
244  if not volume_id:
245  raise InvalidParameterValue
246  volume_id = int(volume_id)
247  except KeyError, error:
248  raise MissingParameter(parameter=error.args[0])
249  except ValueError, error:
250  raise InvalidParameterValue
251 
252 
253  instance_id_ec2 = self.parameters.get('InstanceId')
254  instance_id = instance_id_ec2
255  if instance_id_ec2:
256  instance_id = parseID(instance_id_ec2, Entity.instance)
257  if not instance_id:
258  raise InvalidParameterValue
259  instance_id = int(instance_id)
260  try:
261  self.cluster_manager.user.storage_image.detach({ 'storage_image_id' : volume_id, 'vm_id' : instance_id })
262  except CLMException, error:
263  if error.status == 'storage_image_detach':
264  raise UndefinedError
265  if error.status == 'image_attached':
266  raise VolumeInUse
267  if error.status == 'storage_image_get':
268  raise InvalidVolume.NotFound
269  if error.status == 'user_permission' or error.status == 'vm_get':
270  raise InvalidInstanceID.NotFound(image_id=instance_id)
271 
272  else:
273  try:
274  volume_dict = self.cluster_manager.user.storage_image.get_by_id({'storage_image_id':volume_id})
275  if volume_dict['vm_id'] is None:
276  raise InvalidAttachment.NotFound
277  self.cluster_manager.user.storage_image.detach({ 'storage_image_id' : volume_id, 'vm_id' : volume_dict['vm_id'] })
278  except CLMException, error:
279  if error.status == 'storage_image_detach':
280  raise UndefinedError
281  if error.status == 'image_attached':
282  raise VolumeInUse
283  if error.status == 'storage_image_get':
284  raise InvalidVolume.NotFound
285  if error.status == 'user_permission' or error.status == 'vm_get':
286  raise InvalidInstanceID.NotFound(image_id=instance_id)
287  raise UndefinedError
288 
289  return {'volume_id':volume_id,
290  'instance_id' : instance_id}
291 
292 class DeleteVolume(Action):
293  def _execute(self):
294  try:
295  volume_id = self.parameters['VolumeId']
296  volume_id = parseID(volume_id, Entity.volume)
297  if not volume_id:
298  raise InvalidParameterValue
299  volume_id = int(volume_id)
300  except KeyError:
301  raise MissingParameter(parameter='VolumeId')
302  except ValueError:
303  raise InvalidVolumeID.Malformed(image_id=volume_id)
304 
305  try:
306  self.cluster_manager.user.storage_image.delete({'storage_image_id' : volume_id})
307  except CLMException, error:
308  if error.status == 'storage_image_get' or error.status == 'image_permission':
309  raise InvalidVolume.NotFound
310  if error.status == 'image_attached':
311  raise VolumeInUse
312  raise UndefinedError
313 
314  return None
315 
316 
317