cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
iso_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.iso_image
22 #
23 
24 from cm.models.image import Image
25 from cm.utils import log
26 from cm.utils.exception import CMException
27 from common.hardware import disk_controllers_reversed
28 from common.states import image_states, storage_states, image_access, vm_states
29 
30 
31 ##
32 #
33 # @model{image} Class for CD Images
34 #
35 # CD type Image is one of the Images type. CDImage class extends
36 # src.cm.models.image.Image class.
37 # CD Images are meant to be downloaded by user from some external source and
38 # attached to VMs so that VMs may boot from them. Such Images are immutable.
39 #
40 class IsoImage(Image):
41 
42  @property
43  ##
44  #
45  # @returns{dict} image's data
46  # \n fields:
47  # @dictkey{id}
48  # @dictkey{user_id,int}
49  # @dictkey{name}
50  # @dictkey{platform}
51  # @dictkey{description}
52  # @dictkey{creation_date}
53  #
54  def dict(self):
55 
56  d = self.dictImg()
57 
58  if self.disk_dev is not None:
59  d['disk_dev'] = 'cdrom%d' % self.disk_dev
60  else:
61  d['disk_dev'] = ''
62 
63  d['iso_image_id'] = self.id
64 
65  # TODO: to be tested
66  # set of vms which is using the cd volume
67  #d['vms'] = self.vm_set.values_list('id', flat=True) or ''
68  vms = self.vm_set.filter(state__in=[vm_states['running'], vm_states['running']])
69  vm_ids = []
70  vm_names = []
71  for vm in vms:
72  vm_ids.append(vm.id)
73  vm_names.append(vm.name)
74 
75  d['vm_ids'] = vm_ids
76  d['vm_names'] = vm_names
77 
78  return d
79 
80  @staticmethod
81  ##
82  #
83  # Method returns image \c id if it belongs to user \c user_id
84  # (and optionally to listed \c groups, if any given)
85  #
86  # @parameter{user_id,int}
87  # @parameter{iso_image_id,int}
88  #
89  # @returns{Image} image with id given
90  #
91  # @raises{image_get,CMException}
92  #
93  def get(user_id, iso_image_id):
94 
95  try:
96  image = IsoImage.objects.get(pk=iso_image_id)
97  except:
98  raise CMException('iso_image_get')
99 
100  # check on state and storage?
101  if image.state != image_states['ok'] and image.storage.state != storage_states['ok']:
102  raise CMException('image_unavailable')
103 
104  image.has_access(user_id)
105  return image
106 
107  # @returns CDmage instance for admin user
108  @staticmethod
109  ##
110  #
111  # Getter, which should be called by admin. It doesn't check Image's ownership.
112  #
113  # @parameter{iso_image_id,int} primary index of the @type{cdImage}
114  #
115  # @returns{StorageImage} instance of @type{StorageImage} based on primary index provided
116  #
117  # @raises{image_get,CMException}
118  #
119  def admin_get(iso_image_id):
120 
121  try:
122  image = IsoImage.objects.get(pk=iso_image_id)
123  except:
124  raise CMException('iso_image_get')
125 
126  return image
127 
128  # returns True, if user \c user_id (and optionally listed \c groups)
129  # has access to this image. Otherwise exception is thrown.
130  ##
131  #
132  # @parameter{user_id,int}
133  #
134  # @returns{bool}
135  # True, if user \c user_id (and optionally listed \c groups)
136  # has access to this image. Otherwise exception is thrown.
137  #
138  # @raises{iso_image_permission,CMException}
139  #
140  def has_access(self, user_id):
141  if self.user.id != user_id:
142  if self.access == image_access['private']:
143  raise CMException('iso_image_permission')
144  return True
145 
146  def check_attached(self):
147  if self.vm_set.exclude(state__in=[vm_states['closed'], vm_states['erased']]).exists():
148  raise CMException('image_attached')
149 
150  ##
151  #
152  # Attaches this StorageImage to specified VM. It searches for first free
153  # device and if there's any, tries to attach this to it via Libvirt.
154  # Further it updates DB information.
155  #
156  # @parameter{vm,VM} instance of the existing VM
157  #
158  # @raises{storage_image_attach,CMException} no free device found or cannot
159  # attach StorageImage
160  #
161  def attach(self, vm):
162  domain = vm.lv_domain()
163  log.debug(self.user.id, self.disk_controller)
164  disk_controller_name = disk_controllers_reversed[self.disk_controller]
165 
166  # Get all block devices and find first, unused sdX
167  attached_devices = [d.disk_dev for d in IsoImage.objects.filter(vm_id__exact=vm.id)]
168 
169  free_dev = 'sdz'
170 
171  if free_dev == attached_devices:
172  raise CMException('iso_image_attach')
173 
174  try:
175  device_desc = """<disk type='file' device='disk'>
176  <driver name='qemu' type='raw'/>
177  <source file='%(path)s'/>
178  <target dev='%(dev)s' bus='%(bus)s'/>
179  <alias name='%(bus)s-%(dev)s'/>
180  </disk>""" % {
181  'path': self.path,
182  # disk_dev name will be in format sd+letter corresponding to the number (e.g: 2->sdb)
183  'dev': 'sd%s' % free_dev,
184  'bus': disk_controller_name
185  }
186  log.debug(self.user.id, device_desc)
187  domain.attachDevice(device_desc)
188  except:
189  log.exception(self.user.id, 'iso attach')
190  raise CMException('iso_image_attach')
191 
192  # Update database information
193  self.disk_dev = free_dev
194  self.vm = vm
195  # self.vm_id = vm.id
196  # saved later by the view function which calls 'attach'
197  # self.save()
198 
199  ##
200  #
201  # Requests Libvirt to detach from given VM this StorageImage.
202  #
203  # @parameter{vm,VM} VM from which StorageImage should be detached.
204  #
205  # @raises{storage_image_detach,CMException} cannot detach StorageImage
206  #
207  def detach(self, vm):
208  domain = vm.lv_domain()
209  disk_controller_name = disk_controllers_reversed[self.disk_controller]
210 
211  try:
212  device_desc = """<disk type='file' device='disk'>
213  <driver name='qemu' type='raw'/>
214  <source file='%(path)s'/>
215  <target dev='%(dev)s' bus='%(bus)s'/>
216  <alias name='%(bus)s-%(dev)s'/>
217  </disk>""" % {
218  'path': self.path,
219  # 'dev': self.disk_dev,
220  'dev': 'sd%s' % chr(self.disk_dev + 96),
221  'bus': disk_controller_name
222  }
223  domain.detachDevice(device_desc)
224  except:
225  log.exception(self.user.id, 'iso detach')
226  raise CMException('iso_image_detach')
227 
228  self.vm = None
229  # saved later by the view function which calls 'detach'
230  # self.save()
231 
232  @classmethod
233  def create(cls, name, description, user, disk_dev, disk_controller):
234  image = Image.create(cls, name=name, description=description, user=user, disk_dev=disk_dev, disk_controller=disk_controller)
235 
236  return image
237