cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
instance.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.states import ec2names, vm_states, image_access
20 from ec2.base.action import Action, CLMException
21 from ec2.error import InvalidAMIID, InvalidKeyPair, MissingParameter, \
22  InvalidParameterValue, InvalidInstanceID, InvalidVolumeID, InternalError, \
23  UndefinedError, InvalidFilter, ResourceLimitExceeded, InvalidVolume
24 from ec2.helpers.entities import Entity
25 from ec2.helpers.filters import applyEc2Filters, validateEc2Filters
26 from ec2.helpers.parse import parseSequenceIntArguments, parseFilters, parseIDs, \
27  parseID, parseSequenceArguments, parseClmDate
28 
29 """@package src.ec2.instance
30 EC2 actions for instances
31 
32 @copyright Copyright (c) 2012 Institute of Nuclear Physics PAS <http://www.ifj.edu.pl/>
33 @author Oleksandr Gituliar <gituliar@gmail.com>
34 @author Rafał Grzymkowski
35 @author Miłosz Zdybał
36 @author Łukasz Chrząszcz <l.chrzaszcz@gmail.com>
37 """
38 
39 CLM_STATES = {
40  0: (0, 'pending'), # pending
41  1: (16, 'running'), # running
42  2: (32, 'shutting-down'), # closing
43  3: (48, 'terminated'), # closed
44  4: (64, 'stopping'), # saving
45  5: (0, 'pending'), # failed
46  6: (0, 'pending'), # saving failed
47  7: (16, 'running'), # running ctx
48  8: (0, 'pending') , # restart
49  10:(80, 'stopped'), # turned off
50  11:(0, 'pending'), # erased
51  12:(0, 'pending'), # erasing
52 }
53 
54 
55 class DescribeInstances(Action):
56  available_filters = ['image-id', 'instance-id', 'reservation-id']
57 
58  def _execute(self):
59  filters = parseFilters(self.parameters)
60  if not validateEc2Filters(filters, self.available_filters):
61  raise InvalidFilter
62 
63  clm_instances = []
64 
65  counter = 1
66  while True: # first we parse arguments provided in request
67  instance_id = self.parameters.get('InstanceId.' + str(counter), None)
68 
69  if instance_id is None: # if there no such argument then break
70  break
71 
72  instance_id = parseID(instance_id, Entity.instance)
73  if instance_id is None:
74  raise InvalidParameterValue
75 
76  counter += 1
77  try:
78  instance = int(instance_id)
79  except ValueError:
80  raise InvalidParameterValue
81 
82  clm_instances.append({'vm_id':instance })
83 
84  # if we didn't provide specific IDs then use a whole list of user's VMs
85  if not clm_instances:
86  clm_instances = self.cluster_manager.user.vm.get_list()
87 
88  ec2_instances = []
89  try:
90  for clm_instance in clm_instances:
91  clm_instance = self.cluster_manager.user.vm.get_by_id({'vm_id': clm_instance['vm_id']})
92  if clm_instance['state'] == vm_states['closed']:
93  raise InvalidInstanceID.NotFound(image_id=clm_instance['vm_id'])
94 
95  private_ip_address = None
96  if clm_instance['leases']:
97  private_ip_address = clm_instance['leases'][0].get('address')
98 
99  ec2_instance = {
100  'image-id': clm_instance['image_id'],
101  'instance-id': clm_instance['vm_id'],
102  'instanceState': {
103  'code': CLM_STATES[clm_instance['state']][0],
104  'name': CLM_STATES[clm_instance['state']][1]},
105  'launchTime': parseClmDate(clm_instance['start_time']),
106  'template_name': clm_instance['template_name'],
107  'ownerId': clm_instance['user_id'],
108  'placement': {
109  'availabilityZone': self.cluster_manager.name},
110  'privateIpAddress': private_ip_address,
111  'reservation-id': clm_instance['reservation_id'],
112  'tags': [{
113  'key': 'Name',
114  'value': clm_instance['name']
115  }]
116  }
117 
118  public_ip = clm_instance['leases'][0]['public_ip']
119  ec2_instance['ipAddress'] = public_ip.get('ip') if public_ip else None
120  ec2_instances.append(ec2_instance)
121  except CLMException, error:
122  if error.status == 'vm_get' or error.status == 'user_permission':
123  raise InvalidInstanceID.NotFound(image_id=clm_instance['vm_id'])
124 
125  owner_id = None
126  if ec2_instances:
127  owner_id = ec2_instances[0]['ownerId']
128 
129  reservation_filter = None
130  if filters and filters.get('reservation-id'):
131  reservation_filter = {'reservation-id': filters['reservation-id'] }
132  del filters['reservation-id']
133 
134  ec2_instances = applyEc2Filters(ec2_instances, filters)
135 
136  reservations_ids = []
137  for ec2_instance in ec2_instances:
138  reservation_ids = int(ec2_instance['reservation-id'])
139  if reservation_ids not in reservations_ids:
140  reservations_ids.append(reservation_ids)
141 
142  reservations = []
143  for reservation in reservations_ids:
144  reservations.append({'reservation-id' : reservation,
145  'ownerId' : owner_id,
146  'instances': [instance for instance in ec2_instances if instance['reservation-id'] == reservation]
147  })
148 #
149  if reservation_filter:
150  reservations = applyEc2Filters(reservations , reservation_filter)
151 
152  return {'reservations' : reservations}
153 
154 
155 class RunInstances(Action):
156  def _execute(self):
157  try:
158  image_id = self.parameters['ImageId']
159  image_id = parseID(image_id, Entity.image)
160  if not image_id:
161  raise InvalidAMIID.Malformed
162 
163  image_id = int(image_id)
164  except KeyError:
165  raise MissingParameter(parameter='ImageId')
166  except ValueError:
167  raise InvalidAMIID.Malformed
168  # raise InvalidAMIID.NotFound(image_id=image_id)
169 
170  instance_type = self.parameters.get('InstanceType', 'm1.small')
171  key_name = self.parameters.get('KeyName')
172  user_data = self.parameters.get('UserData', None)
173 
174  template_id = None
175  for template in self.cluster_manager.user.template.get_list():
176  if template.get('ec2name') == ec2names.get(instance_type):
177  dir(template)
178  template_id = int(template['template_id'])
179  break
180 
181  machine = {
182  'count': int(self.parameters.get('MinCount', 1)),
183  'description': 'created by EC2 API',
184  'image_id': image_id,
185  'name': 'noname',
186  'template_id': template_id if template_id is not None else 1,
187  'public_ip_id':None,
188  'iso_list': None,
189  'disk_list':None,
190  'vnc':None,
191  'user_data' : user_data
192  }
193 
194  if key_name:
195  try:
196  key = self.cluster_manager.user.key.get({'name':key_name})
197  except CLMException, error:
198  if error.status == 'ssh_key_get':
199  raise InvalidKeyPair.NotFound(key_name=key_name)
200  machine['ssh_key'] = key['data']
201  machine['ssh_username'] = 'root'
202 
203 
204  device_mapping_counter = 1
205  volumes = []
206 
207  # EC2 passes data about volumes by BlockDeviceMapping.X.VirtualName parameter
208  # where X starts with 1 and step is also 1
209  while True:
210  volume = self.parameters.get('BlockDeviceMapping.' +
211  str(device_mapping_counter) +
212  '.VirtualName')
213 
214  if volume is None:
215  break
216 
217  volume = parseID(volume, Entity.volume)
218  if volume is None:
219  raise InvalidParameterValue
220 
221  device_mapping_counter += 1
222  try:
223  volumes.append(int(volume))
224  except ValueError:
225  raise InvalidVolumeID.Malformed
226 
227  if volumes:
228  machine['disk_list'] = volumes
229 
230  instances = []
231 
232 
233 
234  # as far as I know here we have problem detecting if image_get regard volume or AMI
235  # TODO load volumes from CLM and check on EC2 server if specified volume exists
236  try:
237  instances = self.cluster_manager.user.vm.create(machine)
238 # instances = self.cluster_manager.vm.user.create(machine)
239  except CLMException, error:
240  if error.status == 'vm_create':
241  raise InternalError # we have not enough information to determine what happened
242  if error.status == 'system_image_get' or error.status == 'image_permission':
243  raise InvalidAMIID.NotFound(image_id=image_id)
244  if error.status == 'storage_image_get':
245  raise InvalidVolume.NotFound
246  if error.status == 'user_cpu_limit':
247  raise ResourceLimitExceeded(resource="CPU")
248  if error.status == 'user_memory_limit':
249  raise ResourceLimitExceeded(resource="RAM")
250  if error.status == 'user_storage_limit':
251  raise ResourceLimitExceeded(resource="Storage")
252  if error.status == 'image_unavailable':
253  raise InvalidAMIID.Unavailable
254  raise UndefinedError
255 
256 
257 
258  reservation_id = instances[0]['vm_id']
259  instance_ids = [instance['vm_id'] for instance in instances]
260  print 'instance_ids:', instance_ids
261 
262  for instance_id in instance_ids:
263 # vm_desc = {
264 # 'name' : 'i-' + str(instance_id),
265 # 'description' : 'created by EC2 API'
266 # }
267  print 'editing'
268  edit_response = self.cluster_manager.user.vm.edit({'vm_id': instance_id,
269  'name':'i-' + str(instance_id),
270  'description': 'created by EC2 API' })
271 
272  print edit_response
273  return {
274  'reservationId' : reservation_id,
275  'instances': [{
276  'imageId': image_id,
277  'instanceId': instance_id,
278  'instance_type': instance_type,
279  } for instance_id in instance_ids]
280  }
281 
282 # create <-- [{'id':5},{'id':6},{'id':7}]
283 
284 
285 class TerminateInstances(Action):
286  def _execute(self):
287  instance_ids = []
288  for param, value in self.parameters.iteritems():
289  if param.startswith('InstanceId'):
290  try:
291  value = parseID(value, Entity.instance)
292  if not value:
293  raise InvalidParameterValue
294  instance_ids.append(int(value))
295  except ValueError:
296  raise InvalidInstanceID.Malformed(image_id=value)
297  if not instance_ids:
298  raise MissingParameter(parameter='InstanceId')
299 
300  try:
301  none = self.cluster_manager.user.vm.destroy({'vm_ids': instance_ids})
302  except CLMException, error:
303  if error.status == 'vm_get' or error.status == 'user_permission':
304  raise InvalidInstanceID.NotFound(image_id=0) # TODO wykrywanie który instance jest zly
305  print error.status
306  raise InternalError # destroy should throw no exception, however, we check and raise InternalError just in case
307 
308  return {
309  'instances': [{
310  'id': instance_id,
311  } for instance_id in instance_ids],
312  }
313 
314 class RebootInstances(Action):
315  def _execute(self):
316  try:
317  instances = parseSequenceArguments(self.parameters, 'InstanceId.')
318  if not instances:
319  raise MissingParameter(parameter='InstanceId')
320  instances = parseIDs(instances, Entity.instance)
321  if not instances:
322  raise InvalidParameterValue
323  # nie powinno się tutaj rzutować na inta?
324  except ValueError:
325  raise InvalidInstanceID.Malformed(image_id=0) # TODO wykrywanie który instance jest zly
326 
327  try:
328  none = self.cluster_manager.user.vm.reset({'vm_ids':instances })
329  except CLMException, error:
330  if error.status == 'user_permission' or error.status == 'vm_get':
331  raise InvalidInstanceID.NotFound(image_id=0) # TODO wykrywanie która dokładnie to instancja
332  print error.status
333  raise InternalError
334