cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
system_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.models.system_image
22 #
23 
24 import os
25 import subprocess
26 
27 from django.db import models
28 
29 from cm.models.image import Image
30 from cm.utils import log
31 from cm.utils.exception import CMException
32 from common.hardware import video_devices, network_devices, \
33  video_devices_reversed, network_devices_reversed
34 from common.states import image_access, vm_states
35 from cm.utils import message
36 
37 
38 ##
39 #
40 # @model{SYSTEM_IMAGE} VM type image's class.
41 #
42 # System image is one of the Images type. SystemImage class extends
43 # src.cm.models.image.Image class.
44 # By default virtual machine boots from copy of the stored VM image.
45 # At the VM's creation moment it's required to choose one VM image
46 #
47 # We can distinguish:
48 # - public images
49 # - private images
50 # - group images
51 #
52 # VM images are mutable while VM is running. When closing VM, the copy
53 # of the image that virtual machine is running from may be saved to
54 # private images' pool.
55 #
56 # Despite the ability of VM images to store data it's not their purpose.
57 # It is far more convenient and recommended to store data on
58 # Storage Images (StorageImages).
59 #
60 class SystemImage(Image):
61  platform = models.IntegerField()
62 
63  # default values are taken from hardware.py
64  network_device = models.IntegerField(default=network_devices['virtio'])
65  video_device = models.IntegerField(default=video_devices['cirrus'])
66 
67  @classmethod
68  def create(cls, name, description, user, platform, disk_controller, network_device, video_device):
69  image = Image.create(cls, name=name, description=description, user=user, size=0, progress=0,
70  disk_dev=1, disk_controller=disk_controller)
71  image.platform = platform
72  image.access = image_access['private']
73  image.network_device = network_device
74  image.video_device = video_device
75  return image
76 
77  # TODO: is vms and user_group necessary too?
78  @property
79  ##
80  #
81  # @returns{dict} image's data
82  # \n fields:
83  # @dictkey{id}
84  # @dictkey{user_id,int}
85  # @dictkey{name}
86  # @dictkey{platform}
87  # @dictkey{description}
88  # @dictkey{creation_date}
89  #
90  def dict(self):
91  d = self.dictImg()
92 
93  if self.disk_dev is not None:
94  d['disk_dev'] = 'sd%s' % chr(self.disk_dev + 96)
95  else:
96  d['disk_dev'] = ''
97 
98  # fields for vm image:
99  d['user_id'] = self.user.id
100  d['access'] = self.access
101  d['disk_controller'] = self.disk_controller
102  d['platform'] = self.platform
103  d['system_image_id'] = self.id
104 
105  # TODO:
106  # set of vms with this image attached
107  d['vms'] = list(self.vm_set.filter(state__in=[vm_states['running'], vm_states['running']]).values_list('id', flat=True)) or []
108 
109  # groups image belongs (only one group for now)
110  grouplist = self.systemimagegroup_set.values_list('group_id', flat=True)
111  d['group_id'] = grouplist[0] if len(grouplist) > 0 else ''
112 
113  d['disk_controller'] = self.disk_controller
114  d['video_device'] = self.video_device
115  d['network_device'] = self.network_device
116 
117  return d
118 
119  # returns image id if it belongs to user user_id
120  # (and optionally to listed groups, if any given)
121  # also check if user has permissions
122  @staticmethod
123  ##
124  #
125  # Method returns requested VMImage, provided specified User actually
126  # has right to access it:
127  # - either as it's owner,
128  # - or as member of a Group VMImage belongs to.
129  #
130  # @parameter{user_id,int} User for whom VMImage should be obtained
131  # @parameter{sys_image_id,int} id of the requested VMImage
132  # @parameter{groups,list(int)} ids of the Groups that User belongs to @optional{None}
133  #
134  # @returns{Image} requested VMImage instance, provided specified User
135  # actually has right to access it
136  #
137  # @raises{image_get,CMException} no such VMImage
138  #
139  def get(user_id, sys_image_id, groups=None):
140 
141  try:
142  image = SystemImage.objects.get(pk=sys_image_id)
143  except:
144  raise CMException('system_image_get')
145 
146  # TODO:
147  # should check on state and storage? it was in get image old code
148  # if image.state != image_states['ok'] and image.storage.state != storage_states['ok']:
149  # raise CMException('image_unavailable')
150 
151  image.has_access(user_id, groups)
152  return image
153 
154  # @returns VMImage instance for admin user
155  @staticmethod
156  ##
157  #
158  # Getter, which should be called by admin. It doesn't check Image's ownership.
159  #
160  # @parameter{sys_img_id,int} primary index of the @type{VMImage}
161  #
162  # @returns{SystemImage} instance of @type{SystemImage} based on primary index provided
163  #
164  # @raises{image_get,CMException}
165  #
166  def admin_get(sys_image_id):
167 
168  try:
169  image = SystemImage.objects.get(pk=sys_image_id)
170  except:
171  raise CMException('system_image_get')
172 
173  return image
174 
175  ##
176  #
177  # @parameter{user_id,int} id of the User to check for right to access
178  # @parameter{groups,list(int)} id if the Groups User belongs to; required
179  # if Image's access type is 'group'
180  #
181  # @returns{bool}
182  # True, if specified User or listed Groups have right to access this
183  # Image. Otherwise exception is thrown.
184  #
185  # @raises{system_image_permission,CMException} neither User nor listed Groups
186  # have right to access this Image.
187  #
188  def has_access(self, user_id, groups=None):
189  if self.user.id != user_id:
190  if self.access == image_access['private']:
191  raise CMException('system_image_permission')
192  # if image has a groups access we need to check if image belongs to users groups or is group leader
193  # elif self.access == image_access['group'] and (groups is None or self.image2group.group_id not in groups):
194  elif self.access == image_access['group'] and ((groups is None) or (not(self.systemimagegroup_set.filter(group_id__in=groups).exists()))):
195  raise CMException('system_image_permission')
196  return True
197 
198  @property
199  ##
200  #
201  # Method filters VIDEO_DEVICES list to find video device name by
202  # the video_device id that is assigned to this Image.
203  # @returns{string} name of this Image's the video device, if such a
204  # video device exists. otherwise 'vga'
205  #
206  def video_device_name(self):
207  try:
208  return video_devices_reversed[self.video_device]
209  except Exception:
210  log.error(self.user.id, 'Cannot find video device')
211  return 'vga'
212 
213  @property
214  ##
215  #
216  # Method filters NETWORK_DEVICES list to find network device name by
217  # the network_device id with is assigned to this Image.
218  # @returns{string} name of this Image's network device, if such a
219  # network device exists.
220  #
222  try:
223  return network_devices_reversed[self.network_device]
224  except Exception:
225  log.error(self.user.id, 'Cannot find network device')
226 
227  ##
228  #
229  # Copy vm image from storage to node
230  #
231  # @raises{vm_create,CMException}
232  #
233  def copy_to_node(self, vm):
234  log.debug(vm.user.id, "Copy image by ssh")
235  log.debug(vm.user.id, str(['ssh', vm.node.ssh_string, 'cp %s /images/%d' % (vm.system_image.path, vm.id)]))
236 
237  if subprocess.call(['ssh', vm.node.ssh_string, 'cp %s /images/%d' % (vm.system_image.path, vm.id)]):
238  message.error(vm.user_id, 'vm_create', {'id': vm.id, 'name': vm.name})
239  raise CMException('vm_create')
240 
241  if subprocess.call(['ssh', vm.node.ssh_string, 'chmod a+rw /images/%d' % vm.id]):
242  message.error(vm.user_id, 'vm_create', {'id': vm.id, 'name': vm.name})
243  raise CMException('vm_create')
244  return
245 
246  ##
247  #
248  # Copy vm image from node images pool to selected image on storage. Image
249  # entity should be createt before calling this function
250  #
251  def copy_to_storage(self, vm, image):
252  log.debug(vm.user.id, "Preparing temporary storage pool")
253  if not os.path.exists(os.path.dirname(image.path)):
254  os.makedirs(os.path.dirname(image.path))
255  log.debug(vm.user.id, str(['ssh', vm.node.ssh_string, 'cp /images/%d %s' % (vm.id, image.path)]))
256 
257  if subprocess.call(['ssh', vm.node.ssh_string, 'cp /images/%d %s' % (vm.id, image.path)]):
258  raise CMException('image_copy_to_storage')
259