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 # @package src.cm.models.disk_volume
21 #
22 
23 from django.db import models
24 
25 from cm.models.image import Image
26 from cm.models.vm import VM
27 from cm.utils import log
28 from cm.utils.exception import CMException
29 
30 
31 ##
32 #
33 # @model{DISK_VOLUME}
34 #
35 # Storage type image's class.
36 #
37 # Disk Volume is one of the Images type. StorageImage class extends
38 # src.cm.models.image.Image class.
39 #
40 # Storage images are meant to collect data produced by VM it's attached to.
41 # It should be considered as storage disk. Data may be saved on it.
42 #
43 # There are several disk controllers to choose from. Only storage image
44 # with USB disk controller may be plugged to and unplugged from running
45 # VM on the fly. Other ones ought to be plugged while starting VM and are
46 # automatically unplugged when VM is closed or destroyed.
47 #
48 class StorageImage(Image):
49  vm = models.ForeignKey(VM, null=True, blank=True)
50 
51  @classmethod
52  def create(cls, name, description, user, disk_controller, size=0):
53  image = Image.create(cls, name=name, description=description, user=user, size=size, progress=0, disk_dev=1, disk_controller=disk_controller)
54 
55  return image
56 
57  @property
58  ##
59  #
60  # @returns{dict} image's data
61  # \n fields:
62  # @dictkey{id}
63  # @dictkey{user_id,int}
64  # @dictkey{name}
65  # @dictkey{platform}
66  # @dictkey{description}
67  # @dictkey{creation_date}
68  #
69  def dict(self):
70  d = self.dictImg()
71 
72  d['storage_image_id'] = self.id
73 
74  # fields for disk volume:
75  d['disk_dev'] = 'sd%s' % chr(self.disk_dev + 98) if self.disk_dev else ''
76 
77  # the vm id and name to which the disk volume is attached (or empty if no vm uses it)
78  d['vm_id'] = self.vm.id if self.vm else ''
79  d['vm_name'] = self.vm.name if self.vm else ''
80 
81  return d
82 
83  @staticmethod
84  ##
85  #
86  # @parameter{user_id,int} declared owner of the requested StorageImage
87  # @parameter{disk_image_id,int} id of the requested StorageImage
88  #
89  # @returns{StorageImage} requested StorageImage instance, provided it actually
90  # belongs to user_id.
91  #
92  # @raises{image_get,CMException} no such Image
93  #
94  def get(user_id, disk_image_id):
95 
96  try:
97  image = StorageImage.objects.get(pk=disk_image_id)
98  except:
99  raise CMException('storage_image_get')
100 
101  image.has_access(user_id)
102 
103  # check on state and storage?
104  # if image.state != image_states['ok'] and image.storage.state != storage_states['ok']:
105  # raise CMException('image_unavailable')
106 
107  return image
108 
109  # returns True, if user_id is the owner of the storage image
110  # (disk volumes are private). Otherwise exception is thrown.
111  ##
112  #
113  # @parameter{user_id,int}
114  #
115  # @returns{bool}
116  # True, if user \c user_id is the owner of the image.
117  # Otherwise exception is thrown.
118  #
119  # @raises{storage_image_permission,CMException}
120  #
121  def has_access(self, user_id):
122  if self.user.id != user_id:
123  raise CMException('storage_image_permission')
124  return True
125 
126  # @returns VMImage instance for admin user
127  @staticmethod
128  ##
129  #
130  # Getter, which should be called by admin. It doesn't check Image's ownership.
131  #
132  # @parameter{img_id,int} id of the requested Image
133  #
134  # @returns{Image} instance of the requested Image
135  # @raises{image_get,CMException} requested Image doesn't exist
136  #
137  def admin_get(disk_image_id):
138 
139  try:
140  image = StorageImage.objects.get(pk=disk_image_id)
141  except:
142  raise CMException('storage_image_get')
143 
144  return image
145 
146  # Note: disk_dev is now an integer
147  ##
148  #
149  # Attaches this StorageImage to specified VM. It searches for first free
150  # device and if there's any, tries to attach this to it via Libvirt.
151  # Further it updates DB information.
152  #
153  # @parameter{vm,VM} instance of the existing VM
154  #
155  # @raises{storage_image_attach,CMException} no free device found or cannot
156  # attach StorageImage
157  #
158  def attach(self, vm):
159  domain = vm.lv_domain()
160  log.debug(self.user.id, self.disk_controller)
161 
162  # Get all block devices and find first, unused sdX
163  attached_devices = [d.disk_dev for d in StorageImage.objects.filter(vm_id__exact=vm.id)]
164 
165  free_dev = None
166  # for i in range(98, 122):
167  # find the first free numbers to be given to disk volume (sda is now integer)
168  for i in range(2, 12):
169  if not i in attached_devices:
170  free_dev = i
171  break
172 
173  if free_dev == None:
174  raise CMException('storage_image_attach')
175 
176  try:
177  device_desc = """<disk type='file' device='disk'>
178  <driver name='qemu' type='raw'/>
179  <source file='%(path)s'/>
180  <target dev='%(dev)s' bus='%(bus)s'/>
181  <alias name='%(bus)s-%(dev)s'/>
182  </disk>""" % {
183  'path': self.path,
184  # disk_dev name will be in format sd+letter corresponding to the number (e.g: 2->sdb)
185  'dev': 'sd%s' % chr(free_dev + 98),
186  'bus': self.disk_controller_name
187  }
188  log.debug(self.user.id, device_desc)
189  domain.attachDevice(device_desc)
190  except:
191  log.exception(self.user.id, 'storage attach')
192  raise CMException('storage_image_attach')
193 
194  # Update database information
195  self.disk_dev = free_dev
196  self.vm = vm
197  # self.vm_id = vm.id
198  # saved later by the view function which calls 'attach'
199  # self.save()
200 
201  ##
202  #
203  # Requests Libvirt to detach from given VM this StorageImage.
204  #
205  # @parameter{vm,VM} VM from which StorageImage should be detached.
206  #
207  # @raises{storage_image_detach,CMException} cannot detach StorageImage
208  #
209  def detach(self, vm):
210  domain = vm.lv_domain()
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 + 98),
221  'bus': self.disk_controller_name
222  }
223  domain.detachDevice(device_desc)
224  except:
225  log.exception(self.user.id, 'storage detach')
226  raise CMException('storage_image_detach')
227 
228  self.vm = None
229  # saved later by the view function which calls 'detach'
230  # self.save()
231 
232  def check_attached(self):
233  if self.vm:
234  raise CMException('image_attached')
235 
236  def revoke(self):
237  domain = self.vm.domain()
238  try:
239  device_desc = """<disk type='file' device='disk'>
240  <driver name='qemu' type='raw'/>
241  <source file='%(path)s'/>
242  <target dev='%(dev)s' bus='%(bus)s'/>
243  <alias name='%(bus)s-%(dev)s'/>
244  </disk>""" % {
245  'path': self.path,
246  # 'dev': self.disk_dev,
247  'dev': 'sd%s' % chr(self.disk_dev + 98),
248  'bus': self.disk_controller_name
249  }
250  domain.detachDevice(device_desc)
251  except:
252  log.exception(self.user.id, 'storage detach')
253  #raise CMException('storage_image_detach') # do not raise exception - there is possibility, that domain doesn't exist, we want to mark disk as not atached anyway
254 
255  self.vm = None
256