text: # Copyright 2012 Nebula, Inc.
  # All rights reserved.
  
  # Licensed under the Apache License, Version 2.0 (the "License"); you may
  # not use this file except in compliance with the License. You may obtain
  # a copy of the License at
  #
  #      http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  # License for the specific language governing permissions and limitations
  # under the License.
  
  """
  Views for managing volumes.
  """
  import re
  
  from django.conf import settings
  from django.core.urlresolvers import reverse
  from django.forms import ValidationError  # noqa
  from django.template.defaultfilters import filesizeformat  # noqa
  from django.utils.translation import pgettext_lazy
  from django.utils.translation import ugettext_lazy as _
  
  from horizon import exceptions
  from horizon import forms
  from horizon import messages
  from horizon.utils import functions
  from horizon.utils.memoized import memoized  # noqa
  
  from openstack_dashboard import api
  from openstack_dashboard.api import cinder
  from openstack_dashboard.api import glance
  from openstack_dashboard.dashboards.project.images import utils
  from openstack_dashboard.dashboards.admin.instances import tables
  from openstack_dashboard.usage import quotas
  import logging
  LOG = logging.getLogger(__name__)
  
  IMAGE_BACKEND_SETTINGS = getattr(settings, 'OPENSTACK_IMAGE_BACKEND', {})
  IMAGE_FORMAT_CHOICES = IMAGE_BACKEND_SETTINGS.get('image_formats', [])
  VALID_DISK_FORMATS = ('raw', 'vmdk', 'vdi', 'qcow2')
  DEFAULT_CONTAINER_FORMAT = 'bare'
  
  class CreateVolumeType(forms.SelfHandlingForm):
      name = forms.CharField(max_length=255, label=_("Name"))
  
      def handle(self, request, data):
          try:
              # Remove any new lines in the public key
              volume_type = cinder.volume_type_create(request,
                                                      data['name'])
              messages.success(request, _('Successfully created volume type: %s')
                                        % data['name'])
              return volume_type
          except Exception:
              exceptions.handle(request,
                                _('Unable to create volume type.'))
              return False
  
  class CreateQosSpec(forms.SelfHandlingForm):
      name = forms.CharField(max_length=255, label=_("Name"))
      consumer = forms.ChoiceField(label=_("Consumer"),
                                   choices=cinder.CONSUMER_CHOICES)
  
      def handle(self, request, data):
          try:
              qos_spec = cinder.qos_spec_create(request,
                                                data['name'],
                                                {'consumer': data['consumer']})
              messages.success(request, _('Successfully created QOS Spec: %s')
                                        % data['name'])
              return qos_spec
          except Exception:
              exceptions.handle(request,
                                _('Unable to create QOS Spec.'))
              return False
  
  
  class CreateForm(forms.SelfHandlingForm):
      name = forms.CharField(max_length=255, label=_("Volume Name"))
      description = forms.CharField(max_length=255, widget=forms.Textarea(
          attrs={'class': 'modal-body-fixed-width'}),
          label=_("Description"), required=False)
      volume_source_type = forms.ChoiceField(label=_("Volume Source"),
                                             required=False,
                                             widget=forms.Select(attrs={
                                                 'class': 'switchable',
                                                 'data-slug': 'source'}))
      snapshot_source = forms.ChoiceField(
          label=_("Use snapshot as a source"),
          widget=forms.SelectWidget(
              attrs={'class': 'snapshot-selector'},
              data_attrs=('size', 'name'),
              transform=lambda x: "%s (%sGB)" % (x.name, x.size)),
          required=False)
      image_source = forms.ChoiceField(
          label=_("Use image as a source"),
          widget=forms.SelectWidget(
              attrs={'class': 'image-selector'},
              data_attrs=('size', 'name', 'min_disk'),
              transform=lambda x: "%s (%s)" % (x.name, filesizeformat(x.bytes))),
          required=False)
      volume_source = forms.ChoiceField(
          label=_("Use a volume as source"),
          widget=forms.SelectWidget(
              attrs={'class': 'image-selector'},
              data_attrs=('size', 'name'),
              transform=lambda x: "%s (%s)" % (x.name,
                  filesizeformat(x.size * 1024 * 1024 * 1024))),
          required=False)
      type = forms.ChoiceField(
          label=_("Type"),
          required=False,
          widget=forms.Select(
              attrs={'class': 'switched',
                     'data-switch-on': 'source',
                     'data-source-no_source_type': _('Type'),
                     'data-source-image_source': _('Type')}))
      size = forms.IntegerField(min_value=1, initial=1, label=_("Size (GB)"))
      availability_zone = forms.ChoiceField(
          label=_("Availability Zone"),
          required=False,
          widget=forms.Select(
              attrs={'class': 'switched',
                     'data-switch-on': 'source',
                     'data-source-no_source_type': _('Availability Zone'),
                     'data-source-image_source': _('Availability Zone')}))
  
      def __init__(self, request, *args, **kwargs):
          super(CreateForm, self).__init__(request, *args, **kwargs)
          volume_types = cinder.volume_type_list(request)
          self.fields['type'].choices = [("", _("No volume type"))] + \
                                        [(type.name, type.name)
                                         for type in volume_types]
  
          if "snapshot_id" in request.GET:
              try:
                  snapshot = self.get_snapshot(request,
                                               request.GET["snapshot_id"])
                  self.fields['name'].initial = snapshot.name
                  self.fields['size'].initial = snapshot.size
                  self.fields['snapshot_source'].choices = ((snapshot.id,
                                                             snapshot),)
                  try:
                      # Set the volume type from the original volume
                      orig_volume = cinder.volume_get(request,
                                                      snapshot.volume_id)
                      self.fields['type'].initial = orig_volume.volume_type
                  except Exception:
                      pass
                  self.fields['size'].help_text = _('Volume size must be equal '
                              'to or greater than the snapshot size (%sGB)') \
                              % snapshot.size
                  del self.fields['image_source']
                  del self.fields['volume_source']
                  del self.fields['volume_source_type']
                  del self.fields['availability_zone']
              except Exception:
                  exceptions.handle(request,
                                    _('Unable to load the specified snapshot.'))
          elif 'image_id' in request.GET:
              self.fields['availability_zone'].choices = \
                  self.availability_zones(request)
              try:
                  image = self.get_image(request,
                                         request.GET["image_id"])
                  image.bytes = image.size
                  self.fields['name'].initial = image.name
                  min_vol_size = functions.bytes_to_gigabytes(
                      image.size)
                  size_help_text = _('Volume size must be equal to or greater '
                                     'than the image size (%s)') \
                                   % filesizeformat(image.size)
                  properties = getattr(image, 'properties', {})
                  min_disk_size = getattr(image, 'min_disk', 0) or \
                                  properties.get('min_disk', 0)
                  if (min_disk_size > min_vol_size):
                      min_vol_size = min_disk_size
                      size_help_text = _('Volume size must be equal to or '
                                         'greater than the image minimum '
                                         'disk size (%sGB)') \
                                       % min_disk_size
                  self.fields['size'].initial = min_vol_size
                  self.fields['size'].help_text = size_help_text
                  self.fields['image_source'].choices = ((image.id, image),)
                  del self.fields['snapshot_source']
                  del self.fields['volume_source']
                  del self.fields['volume_source_type']
              except Exception:
                  msg = _('Unable to load the specified image. %s')
                  exceptions.handle(request, msg % request.GET['image_id'])
          elif 'volume_id' in request.GET:
              self.fields['availability_zone'].choices = \
                  self.availability_zones(request)
              volume = None
              try:
                  volume = self.get_volume(request, request.GET["volume_id"])
              except Exception:
                  msg = _('Unable to load the specified volume. %s')
                  exceptions.handle(request, msg % request.GET['volume_id'])
  
              if volume is not None:
                  self.fields['name'].initial = volume.name
                  self.fields['description'].initial = volume.description
                  min_vol_size = volume.size
                  size_help_text = _('Volume size must be equal to or greater '
                                     'than the origin volume size (%s)') \
                                   % filesizeformat(volume.size)
                  self.fields['size'].initial = min_vol_size
                  self.fields['size'].help_text = size_help_text
                  self.fields['volume_source'].choices = ((volume.id, volume),)
                  self.fields['type'].initial = volume.type
                  del self.fields['snapshot_source']
                  del self.fields['image_source']
                  del self.fields['volume_source_type']
          else:
              source_type_choices = []
              self.fields['availability_zone'].choices = \
                  self.availability_zones(request)
  
              try:
                  snapshot_list = cinder.volume_snapshot_list(request)
                  snapshots = [s for s in snapshot_list
                                if s.status == 'available']
                  if snapshots:
                      source_type_choices.append(("snapshot_source",
                                                  _("Snapshot")))
                      choices = [('', _("Choose a snapshot"))] + \
                                [(s.id, s) for s in snapshots]
                      self.fields['snapshot_source'].choices = choices
                  else:
                      del self.fields['snapshot_source']
              except Exception:
                  exceptions.handle(request, _("Unable to retrieve "
                          "volume snapshots."))
  
              images = utils.get_available_images(request,
                                            request.user.tenant_id)
              if images:
                  source_type_choices.append(("image_source", _("Image")))
                  choices = [('', _("Choose an image"))]
                  for image in images:
                      image.bytes = image.size
                      image.size = functions.bytes_to_gigabytes(image.bytes)
                      choices.append((image.id, image))
                  self.fields['image_source'].choices = choices
              else:
                  del self.fields['image_source']
  
              volumes = self.get_volumes(request)
              if volumes:
                  source_type_choices.append(("volume_source", _("Volume")))
                  choices = [('', _("Choose a volume"))]
                  for volume in volumes:
                      choices.append((volume.id, volume))
                  self.fields['volume_source'].choices = choices
              else:
                  del self.fields['volume_source']
  
              if source_type_choices:
                  choices = ([('no_source_type',
                               _("No source, empty volume"))] +
                              source_type_choices)
                  self.fields['volume_source_type'].choices = choices
              else:
                  del self.fields['volume_source_type']
  
      def clean(self):
          cleaned_data = super(CreateForm, self).clean()
          source_type = self.cleaned_data.get('volume_source_type')
          if (source_type == 'image_source' and
                  not cleaned_data.get('image_source')):
              msg = _('Image source must be specified')
              self._errors['image_source'] = self.error_class([msg])
          elif (source_type == 'snapshot_source' and
                  not cleaned_data.get('snapshot_source')):
              msg = _('Snapshot source must be specified')
              self._errors['snapshot_source'] = self.error_class([msg])
          elif (source_type == 'volume_source' and
                  not cleaned_data.get('volume_source')):
              msg = _('Volume source must be specified')
              self._errors['volume_source'] = self.error_class([msg])
          return cleaned_data
  
      # Determine whether the extension for Cinder AZs is enabled
      def cinder_az_supported(self, request):
          try:
              return cinder.extension_supported(request, 'AvailabilityZones')
          except Exception:
              exceptions.handle(request, _('Unable to determine if '
                                           'availability zones extension '
                                           'is supported.'))
              return False
  
      def availability_zones(self, request):
          zone_list = []
          if self.cinder_az_supported(request):
              try:
                  zones = api.cinder.availability_zone_list(request)
                  zone_list = [(zone.zoneName, zone.zoneName)
                                for zone in zones if zone.zoneState['available']]
                  zone_list.sort()
              except Exception:
                  exceptions.handle(request, _('Unable to retrieve availability '
                                               'zones.'))
          if not zone_list:
              zone_list.insert(0, ("", _("No availability zones found")))
          elif len(zone_list) > 0:
              zone_list.insert(0, ("", _("Any Availability Zone")))
  
          return zone_list
  
      def get_volumes(self, request):
          volumes = []
          try:
              volume_list = cinder.volume_list(self.request)
              if volume_list is not None:
                  volumes = [v for v in volume_list
                             if v.status == api.cinder.VOLUME_STATE_AVAILABLE]
          except Exception:
              exceptions.handle(request,
                                _('Unable to retrieve list of volumes.'))
          return volumes
  
      def handle(self, request, data):
          try:
              usages = quotas.tenant_limit_usages(self.request)
              availableGB = usages['maxTotalVolumeGigabytes'] - \
                  usages['gigabytesUsed']
              availableVol = usages['maxTotalVolumes'] - usages['volumesUsed']
  
              snapshot_id = None
              image_id = None
              volume_id = None
              source_type = data.get('volume_source_type', None)
              az = data.get('availability_zone', None) or None
              if (data.get("snapshot_source", None) and
                    source_type in [None, 'snapshot_source']):
                  # Create from Snapshot
                  snapshot = self.get_snapshot(request,
                                               data["snapshot_source"])
                  snapshot_id = snapshot.id
                  if (data['size'] < snapshot.size):
                      error_message = _('The volume size cannot be less than '
                          'the snapshot size (%sGB)') % snapshot.size
                      raise ValidationError(error_message)
                  az = None
              elif (data.get("image_source", None) and
                    source_type in [None, 'image_source']):
                  # Create from Snapshot
                  image = self.get_image(request,
                                         data["image_source"])
                  image_id = image.id
                  image_size = functions.bytes_to_gigabytes(image.size)
                  if (data['size'] < image_size):
                      error_message = _('The volume size cannot be less than '
                          'the image size (%s)') % filesizeformat(image.size)
                      raise ValidationError(error_message)
                  properties = getattr(image, 'properties', {})
                  min_disk_size = getattr(image, 'min_disk', 0) or \
                                  properties.get('min_disk', 0)
                  if (min_disk_size > 0 and data['size'] < min_disk_size):
                      error_message = _('The volume size cannot be less than '
                          'the image minimum disk size (%sGB)') % min_disk_size
                      raise ValidationError(error_message)
              elif (data.get("volume_source", None) and
                    source_type in [None, 'volume_source']):
                  # Create from volume
                  volume = self.get_volume(request, data["volume_source"])
                  volume_id = volume.id
  
                  if data['size'] < volume.size:
                      error_message = _('The volume size cannot be less than '
                          'the source volume size (%sGB)') % volume.size
                      raise ValidationError(error_message)
              else:
                  if type(data['size']) is str:
                      data['size'] = int(data['size'])
  
              if availableGB < data['size']:
                  error_message = _('A volume of %(req)iGB cannot be created as '
                                    'you only have %(avail)iGB of your quota '
                                    'available.')
                  params = {'req': data['size'],
                            'avail': availableGB}
                  raise ValidationError(error_message % params)
              elif availableVol <= 0:
                  error_message = _('You are already using all of your available'
                                    ' volumes.')
                  raise ValidationError(error_message)
  
              metadata = {}
  
              volume = cinder.volume_create(request,
                                            data['size'],
                                            data['name'],
                                            data['description'],
                                            data['type'],
                                            snapshot_id=snapshot_id,
                                            image_id=image_id,
                                            metadata=metadata,
                                            availability_zone=az,
                                            source_volid=volume_id)
              message = _('Creating volume "%s"') % data['name']
              messages.info(request, message)
              return volume
          except ValidationError as e:
              self.api_error(e.messages[0])
              return False
          except Exception:
              exceptions.handle(request, ignore=True)
              self.api_error(_("Unable to create volume."))
              return False
  
      @memoized
      def get_snapshot(self, request, id):
          return cinder.volume_snapshot_get(request, id)
  
      @memoized
      def get_image(self, request, id):
          return glance.image_get(request, id)
  
      @memoized
      def get_volume(self, request, id):
          return cinder.volume_get(request, id)
  
  class IncreaseForm(forms.SelfHandlingForm):
      orig_size = forms.CharField(
          label=_("Current Size (GB)"),
          widget=forms.TextInput(attrs={'readonly': 'readonly'}),
          required=False,
      )
      new_size = forms.IntegerField(label=_("Add Size (GB)"),
                                              min_value=1,
                                              initial=1)
  
      def clean(self):
          cleaned_data = super(IncreaseForm, self).clean()
          orig_size = cleaned_data.get('orig_size')
          if not orig_size:
              msg = _("Fail to get the size of current volume storage, please check the cinder services or exclude other reasons.")
              raise forms.ValidationError(msg)
          return cleaned_data
  
      def handle(self, request, data):
          try:
              endpoints = api.base.url_for(request, 'volume')
              expression = r'https?://(.+?):.+?'
              host = re.match(expression, endpoints).groups()
              result= api.device.update_cloud_disk_size(request, host=host, volume_size=data['new_size']).text
              res = eval(result)
              if res.get("status", 'failed') is "success":
                  messages.success(request, _("Extending volume group successful!"))
              else:
                  messages.error(request, _("Fail to extend volume group."))
          except Exception:
              redirect = reverse("horizon:admin:volumes:index")
              exceptions.handle(request,
                                _('Unable to extend volume group.'),
                                redirect=redirect)
  
          return True
  
  class AttachForm(forms.SelfHandlingForm):
      instance = forms.ChoiceField(label=_("Attach to Instance"),
                                   help_text=_("Select an instance to attach to. If disk type of instance is IDE, "
                                               "you must shutdown the instance when you want to attach to. In addition,\
                                                the instances in vGPU cannot attach volume."))
  
      device = forms.CharField(label=_("Device Name"),
                               widget=forms.TextInput(attrs={'placeholder':
                                                             '/dev/vdc'}),
                               required=False,
                               help_text=_("Actual device name may differ due "
                                           "to hypervisor settings. If not "
                                           "specified, then hypervisor will "
                                           "select a device name."))
  
      def __init__(self, *args, **kwargs):
          super(AttachForm, self).__init__(*args, **kwargs)
  
          # Hide the device field if the hypervisor doesn't support it.
          hypervisor_features = getattr(settings,
                                        "OPENSTACK_HYPERVISOR_FEATURES",
                                        {})
          can_set_mount_point = hypervisor_features.get("can_set_mount_point",
                                                        False)
          if not can_set_mount_point:
              self.fields['device'].widget = forms.widgets.HiddenInput()
  
          # populate volume_id
          volume = kwargs.get('initial', {}).get("volume", None)
          if volume:
              volume_id = volume.id
          else:
              volume_id = None
          self.fields['volume_id'] = forms.CharField(widget=forms.HiddenInput(),
                                                     initial=volume_id)
  
          # Populate instance choices
          instance_list = kwargs.get('initial', {}).get('instances', [])
          instances = []
          for instance in instance_list:
              if instance.status in tables.VOLUME_ATTACH_READY_STATES and \
                      not any(instance.id == att["server_id"]
                              for att in volume.attachments):
                  try:
                      obj_info = api.nova.get_object_info(self.request, instance.id)
                  except Exception:
                      exceptions.handle(self.request, err_msg)
                  if instance.vm_hypervisor_type != 'xen':
                      if obj_info.get("disk_type") == "ide" and \
                          instance.status in tables.VOLUME_ATTACH_READY_STATE:
                          instances.append((instance.id, '%s' % (instance.name)))
                      elif obj_info.get("disk_type") != "ide":
                          instances.append((instance.id, '%s' % (instance.name)))
          if instances:
              instances.insert(0, ("", _("Select an instance")))
          else:
              instances = (("", _("No instances available")),)
          self.fields['instance'].choices = instances
  
      def handle(self, request, data):
          instance_choices = dict(self.fields['instance'].choices)
          instance_name = instance_choices.get(data['instance'],
                                               _("Unknown instance (None)"))
          # The name of the instance in the choices list has the ID appended to
          # it, so let's slice that off...
          instance_name = instance_name.rsplit(" (")[0]
          try:
              attach = api.nova.instance_volume_attach(request,
                                                       data['volume_id'],
                                                       data['instance'],
                                                       data.get('device', ''))
              volume = cinder.volume_get(request, data['volume_id'])
              message = _('Attaching volume %(vol)s to instance '
                           '%(inst)s on %(dev)s.') % {"vol": volume.name,
                                                      "inst": instance_name,
                                                      "dev": attach.device}
              messages.info(request, message)
              return True
          except Exception:
              redirect = reverse("horizon:admin:volumes:index")
              exceptions.handle(request,
                                _('Unable to attach volume.'),
                                redirect=redirect)
  
  
  class CreateSnapshotForm(forms.SelfHandlingForm):
      name = forms.CharField(max_length=255, label=_("Snapshot Name"))
      description = forms.CharField(max_length=255, widget=forms.Textarea,
              label=_("Description"), required=False)
  
      def __init__(self, request, *args, **kwargs):
          super(CreateSnapshotForm, self).__init__(request, *args, **kwargs)
  
          # populate volume_id
          volume_id = kwargs.get('initial', {}).get('volume_id', [])
          self.fields['volume_id'] = forms.CharField(widget=forms.HiddenInput(),
                                                     initial=volume_id)
  
      def handle(self, request, data):
          try:
              volume = cinder.volume_get(request,
                                         data['volume_id'])
              force = False
              message = _('Creating volume snapshot "%s".') % data['name']
              if volume.status == 'in-use':
                  force = True
                  message = _('Forcing to create snapshot "%s" '
                              'from attached volume.') % data['name']
              snapshot = cinder.volume_snapshot_create(request,
                                                       data['volume_id'],
                                                       data['name'],
                                                       data['description'],
                                                       force=force)
  
              messages.info(request, message)
              return snapshot
          except Exception:
              redirect = reverse("horizon:admin:volumes:index")
              exceptions.handle(request,
                                _('Unable to create volume snapshot.'),
                                redirect=redirect)
  
  
  class UpdateForm(forms.SelfHandlingForm):
      name = forms.CharField(max_length=255, label=_("Volume Name"))
      description = forms.CharField(max_length=255, widget=forms.Textarea,
              label=_("Description"), required=False)
  
      def handle(self, request, data):
          volume_id = self.initial['volume_id']
          try:
              cinder.volume_update(request, volume_id, data['name'],
                                   data['description'])
  
              message = _('Updating volume "%s"') % data['name']
              messages.info(request, message)
              return True
          except Exception:
              redirect = reverse("horizon:admin:volumes:index")
              exceptions.handle(request,
                                _('Unable to update volume.'),
                                redirect=redirect)
  
  
  class UploadToImageForm(forms.SelfHandlingForm):
      name = forms.CharField(label=_('Volume Name'),
                             widget=forms.TextInput(
                                 attrs={'readonly': 'readonly'}))
      image_name = forms.CharField(max_length=255, label=_('Image Name'))
      disk_format = forms.ChoiceField(label=_('Disk Format'),
                                      widget=forms.Select(),
                                      required=False)
      force = forms.BooleanField(
          label=pgettext_lazy("Force upload volume in in-use status to image",
                              u"Force"),
          widget=forms.CheckboxInput(),
          required=False)
  
      def __init__(self, request, *args, **kwargs):
          super(UploadToImageForm, self).__init__(request, *args, **kwargs)
  
          # 'vhd','iso','aki','ari' and 'ami' disk formats are supported by
          # glance, but not by qemu-img. qemu-img supports 'vpc', 'cloop', 'cow'
          # and 'qcow' which are not supported by glance.
          # I can only use 'raw', 'vmdk', 'vdi' or 'qcow2' so qemu-img will not
          # have issues when processes image request from cinder.
          disk_format_choices = [(value, name) for value, name
                                  in IMAGE_FORMAT_CHOICES
                                  if value in VALID_DISK_FORMATS]
          self.fields['disk_format'].choices = disk_format_choices
          self.fields['disk_format'].initial = 'raw'
          if self.initial['status'] != 'in-use':
              self.fields['force'].widget = forms.widgets.HiddenInput()
  
      def handle(self, request, data):
          volume_id = self.initial['id']
  
          try:
              # 'aki','ari','ami' container formats are supported by glance,
              # but they need matching disk format to use.
              # Glance usually uses 'bare' for other disk formats except
              # amazon's. Please check the comment in CreateImageForm class
              cinder.volume_upload_to_image(request,
                                            volume_id,
                                            data['force'],
                                            data['image_name'],
                                            DEFAULT_CONTAINER_FORMAT,
                                            data['disk_format'])
              message = _(
                  'Successfully sent the request to upload volume to image '
                  'for volume: "%s"') % data['name']
              messages.info(request, message)
  
              return True
          except Exception:
              error_message = _(
                  'Unable to upload volume to image for volume: "%s"') \
                  % data['name']
              exceptions.handle(request, error_message)
  
              return False
  
  
  class ExtendForm(forms.SelfHandlingForm):
      name = forms.CharField(
          label=_("Volume Group Name"),
          widget=forms.TextInput(attrs={'readonly': 'readonly'}),
          required=False,
      )
      orig_size = forms.IntegerField(
          label=_("Current Size (GB)"),
          widget=forms.TextInput(attrs={'readonly': 'readonly'}),
          required=False,
      )
      new_size = forms.IntegerField(label=_("New Size (GB)"))
  
      def clean(self):
          cleaned_data = super(ExtendForm, self).clean()
          new_size = cleaned_data.get('new_size')
          orig_size = self.initial['orig_size']
          if new_size <= orig_size:
              raise ValidationError(
                  _("New size must be greater than current size."))
  
          usages = quotas.tenant_limit_usages(self.request)
          availableGB = usages['maxTotalVolumeGigabytes'] - \
              usages['gigabytesUsed']
          if availableGB < (new_size - orig_size):
              message = _('Volume cannot be extended to %(req)GB as '
                          'you only have %(avail)GB of your quota '
                          'available.')
              params = {'req': new_size, 'avail': availableGB}
              self._errors["new_size"] = self.error_class([message % params])
          return cleaned_data
  
      def handle(self, request, data):
          volume_id = self.initial['id']
          try:
              volume = cinder.volume_extend(request,
                                            volume_id,
                                            data['new_size'])
  
              message = _('Extending volume: "%s"') % data['name']
              messages.info(request, message)
              return volume
          except Exception:
              redirect = reverse("horizon:admin:volumes:index")
              exceptions.handle(request,
                                _('Unable to extend volume.'),
                                redirect=redirect)
  
  
  class RetypeForm(forms.SelfHandlingForm):
      name = forms.CharField(label=_('Volume Name'),
                             widget=forms.TextInput(
                                 attrs={'readonly': 'readonly'}))
      volume_type = forms.ChoiceField(label=_('Type'))
      MIGRATION_POLICY_CHOICES = [('never', _('Never')),
                                  ('on-demand', _('On Demand'))]
      migration_policy = forms.ChoiceField(label=_('Migration Policy'),
                                           widget=forms.Select(),
                                           choices=(MIGRATION_POLICY_CHOICES),
                                           initial='never',
                                           required=False)
  
      def __init__(self, request, *args, **kwargs):
          super(RetypeForm, self).__init__(request, *args, **kwargs)
  
          try:
              volume_types = cinder.volume_type_list(request)
              self.fields['volume_type'].choices = [(t.name, t.name)
                                                     for t in volume_types]
              self.fields['volume_type'].initial = self.initial['volume_type']
  
          except Exception:
              redirect_url = reverse("horizon:admin:volumes:index")
              error_message = _('Unable to retrieve the volume type list.')
              exceptions.handle(request, error_message, redirect=redirect_url)
  
      def clean_volume_type(self):
          cleaned_volume_type = self.cleaned_data['volume_type']
          origin_type = self.initial['volume_type']
  
          if cleaned_volume_type == origin_type:
              error_message = _(
                  'New volume type must be different from '
                  'the original volume type "%s".') % cleaned_volume_type
              raise ValidationError(error_message)
  
          return cleaned_volume_type
  
      def handle(self, request, data):
          volume_id = self.initial['id']
  
          try:
              cinder.volume_retype(request,
                                   volume_id,
                                   data['volume_type'],
                                   data['migration_policy'])
  
              message = _(
                  'Successfully sent the request to change the volume '
                  'type to "%(vtype)s" for volume: "%(name)s"')
              params = {'name': data['name'],
                        'vtype': data['volume_type']}
              messages.info(request, message % params)
  
              return True
          except Exception:
              error_message = _(
                  'Unable to change the volume type for volume: "%s"') \
                  % data['name']
              exceptions.handle(request, error_message)
  
              return False
  
  class UpdateStatus(forms.SelfHandlingForm):
      status = forms.ChoiceField(label=_("Status"))
  
      def __init__(self, request, *args, **kwargs):
          super(forms.SelfHandlingForm, self).__init__(request, *args, **kwargs)
  
          # This set of states was culled from cinder's admin_actions.py
          self.fields['status'].choices = (
              ('attaching', _('Attaching')),
              ('available', _('Available')),
              ('creating', _('Creating')),
              ('deleting', _('Deleting')),
              ('detaching', _('Detaching')),
              ('error', _('Error')),
              ('error_deleting', _('Error Deleting')),
              ('in-use', _('In Use')),
          )
  
      def handle(self, request, data):
          # Obtain the localized status for including in the message
          for choice in self.fields['status'].choices:
              if choice[0] == data['status']:
                  new_status = choice[1]
                  break
          else:
              new_status = data['status']
  
          try:
              cinder.volume_reset_state(request,
                                        self.initial['volume_id'],
                                        data['status'])
              messages.success(request,
                               _('Successfully updated volume status to "%s".') %
                               new_status)
              return True
          except Exception:
              exceptions.handle(request,
                                _('Unable to update volume status to "%s".') %
                               new_status)
              return False
  
  