manila-ui/manila_ui/dashboards/project/shares/shares/forms.py

402 lines
17 KiB
Python

# Copyright (c) 2014 NetApp, 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.
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 ugettext_lazy as _
import six
from horizon import exceptions
from horizon import forms
from horizon import messages
from horizon.utils.memoized import memoized # noqa
from manila_ui.api import manila
from manila_ui.dashboards import utils
from openstack_dashboard.usage import quotas
class CreateForm(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Share Name"))
description = forms.CharField(
label=_("Description"), required=False,
widget=forms.Textarea(attrs={'rows': 3}))
share_proto = forms.ChoiceField(label=_("Share Protocol"), required=True)
size = forms.IntegerField(min_value=1, label=_("Size (GiB)"))
share_type = forms.ChoiceField(
label=_("Share Type"), required=True,
widget=forms.Select(
attrs={'class': 'switchable', 'data-slug': 'sharetype'}))
availability_zone = forms.ChoiceField(
label=_("Availability Zone"),
required=False)
def __init__(self, request, *args, **kwargs):
super(CreateForm, self).__init__(request, *args, **kwargs)
manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
self.enable_public_shares = manila_features.get(
'enable_public_shares', True)
share_protos = ('NFS', 'CIFS', 'GlusterFS', 'HDFS', 'CephFS')
share_networks = manila.share_network_list(request)
share_types = manila.share_type_list(request)
self.fields['share_type'].choices = (
[("", "")] + [(st.name, st.name) for st in share_types])
availability_zones = manila.availability_zone_list(request)
self.fields['availability_zone'].choices = (
[("", "")] + [(az.name, az.name) for az in availability_zones])
self.sn_field_name_prefix = 'share-network-choices-'
for st in share_types:
extra_specs = st.get_keys()
dhss = extra_specs.get('driver_handles_share_servers')
# NOTE(vponomaryov): Set and tie share-network field only for
# share types with enabled handling of share servers.
if (isinstance(dhss, six.string_types) and
dhss.lower() in ['true', '1']):
sn_choices = (
[('', '')] +
[(sn.id, sn.name or sn.id) for sn in share_networks])
sn_field_name = self.sn_field_name_prefix + st.name
sn_field = forms.ChoiceField(
label=_("Share Network"), required=True,
choices=sn_choices,
widget=forms.Select(attrs={
'class': 'switched',
'data-switch-on': 'sharetype',
'data-sharetype-%s' % st.name: _("Share Network"),
}))
self.fields[sn_field_name] = sn_field
self.fields['share_source_type'] = forms.ChoiceField(
label=_("Share Source"), required=False,
widget=forms.Select(
attrs={'class': 'switchable', 'data-slug': 'source'}))
self.fields['snapshot'] = forms.ChoiceField(
label=_("Use snapshot as a source"),
widget=forms.fields.SelectWidget(
attrs={'class': 'switched',
'data-switch-on': 'source',
'data-source-snapshot': _('Snapshot')},
data_attrs=('size', 'name'),
transform=lambda x: "%s (%sGiB)" % (x.name, x.size)),
required=False)
self.fields['metadata'] = forms.CharField(
label=_("Metadata"), required=False,
widget=forms.Textarea(attrs={'rows': 4}))
if self.enable_public_shares:
self.fields['is_public'] = forms.BooleanField(
label=_("Make visible for all"), required=False,
help_text=(
"If set then all tenants will be able to see this share."))
self.fields['share_proto'].choices = [(sp, sp) for sp in share_protos]
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'].choices = ((snapshot.id, snapshot),)
try:
# Set the share type from the original share
orig_share = manila.share_get(request, snapshot.share_id)
self.fields['share_type'].initial = orig_share.share_type
except Exception:
pass
self.fields['size'].help_text = _(
'Share size must be equal to or greater than the snapshot '
'size (%sGiB)') % snapshot.size
del self.fields['share_source_type']
except Exception:
exceptions.handle(request,
_('Unable to load the specified snapshot.'))
else:
source_type_choices = []
try:
snapshot_list = manila.share_snapshot_list(request)
snapshots = [s for s in snapshot_list
if s.status == 'available']
if snapshots:
source_type_choices.append(("snapshot",
_("Snapshot")))
choices = [('', _("Choose a snapshot"))] + \
[(s.id, s) for s in snapshots]
self.fields['snapshot'].choices = choices
else:
del self.fields['snapshot']
except Exception:
exceptions.handle(request, _("Unable to retrieve "
"share snapshots."))
if source_type_choices:
choices = ([('no_source_type',
_("No source, empty share"))] +
source_type_choices)
self.fields['share_source_type'].choices = choices
else:
del self.fields['share_source_type']
def clean(self):
cleaned_data = super(CreateForm, self).clean()
errors = [k for k in self.errors.viewkeys()]
# NOTE(vponomaryov): skip errors for share-network fields that are not
# related to chosen share type.
for k in errors:
st_name = k.split(self.sn_field_name_prefix)[-1]
chosen_st = cleaned_data.get('share_type')
if (k.startswith(self.sn_field_name_prefix) and
st_name != chosen_st):
cleaned_data[k] = 'Not set'
self.errors.pop(k, None)
share_type = cleaned_data.get('share_type')
if share_type:
cleaned_data['share_network'] = cleaned_data.get(
self.sn_field_name_prefix + share_type)
return cleaned_data
def handle(self, request, data):
try:
# usages = quotas.tenant_limit_usages(self.request)
# availableGB = usages['maxTotalShareGigabytes'] - \
# usages['gigabytesUsed']
# availableVol = usages['maxTotalShares'] - usages['sharesUsed']
snapshot_id = None
source_type = data.get('share_source_type', None)
share_network = data.get('share_network', None)
if (data.get("snapshot", None) and
source_type in [None, 'snapshot']):
# Create from Snapshot
snapshot = self.get_snapshot(request,
data["snapshot"])
snapshot_id = snapshot.id
if (data['size'] < snapshot.size):
error_message = _('The share size cannot be less than the '
'snapshot size (%sGiB)') % snapshot.size
raise ValidationError(error_message)
else:
if type(data['size']) is str:
data['size'] = int(data['size'])
#
# if availableGB < data['size']:
# error_message = _('A share 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'
# ' shares.')
# raise ValidationError(error_message)
metadata = {}
try:
set_dict, unset_list = utils.parse_str_meta(data['metadata'])
if unset_list:
msg = _("Expected only pairs of key=value.")
raise ValidationError(message=msg)
metadata = set_dict
except ValidationError as e:
self.api_error(e.messages[0])
return False
is_public = self.enable_public_shares and data['is_public']
share = manila.share_create(
request,
size=data['size'],
name=data['name'],
description=data['description'],
proto=data['share_proto'],
share_network=share_network,
snapshot_id=snapshot_id,
share_type=data['share_type'],
is_public=is_public,
metadata=metadata,
availability_zone=data['availability_zone'])
message = _('Creating share "%s"') % data['name']
messages.success(request, message)
return share
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 share."))
return False
@memoized
def get_snapshot(self, request, id):
return manila.share_snapshot_get(request, id)
class UpdateForm(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Share Name"))
description = forms.CharField(widget=forms.Textarea,
label=_("Description"), required=False)
is_public = forms.ChoiceField(
choices=((None, 'Do not change share visibility'),
(False, "Make it 'Private'"), (True, "Make it 'Public'")),
label=_("Visibility"), required=False,
widget=forms.Select(
attrs={'class': 'switched', 'data-slug': 'sharetype'}))
def __init__(self, request, *args, **kwargs):
super(UpdateForm, self).__init__(request, *args, **kwargs)
manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
self.enable_public_shares = manila_features.get(
'enable_public_shares', True)
if not self.enable_public_shares:
self.fields.pop('is_public')
def handle(self, request, data):
share_id = self.initial['share_id']
is_public = data['is_public'] if self.enable_public_shares else False
try:
share = manila.share_get(self.request, share_id)
manila.share_update(
request, share, data['name'], data['description'],
is_public=is_public)
message = _('Updating share "%s"') % data['name']
messages.success(request, message)
return True
except Exception:
redirect = reverse("horizon:project:shares:index")
exceptions.handle(request,
_('Unable to update share.'),
redirect=redirect)
class UpdateMetadataForm(forms.SelfHandlingForm):
metadata = forms.CharField(widget=forms.Textarea,
label=_("Metadata"), required=False)
def __init__(self, *args, **kwargs):
super(UpdateMetadataForm, self).__init__(*args, **kwargs)
meta_str = ""
for k, v in self.initial["metadata"].iteritems():
meta_str += "%s=%s\r\n" % (k, v)
self.initial["metadata"] = meta_str
def handle(self, request, data):
share_id = self.initial['share_id']
try:
share = manila.share_get(self.request, share_id)
set_dict, unset_list = utils.parse_str_meta(data['metadata'])
if set_dict:
manila.share_set_metadata(request, share, set_dict)
if unset_list:
manila.share_delete_metadata(request, share, unset_list)
message = _('Updating share metadata "%s"') % share.name
messages.success(request, message)
return True
except ValidationError as e:
self.api_error(e.messages[0])
return False
except Exception:
redirect = reverse("horizon:project:shares:index")
exceptions.handle(request,
_('Unable to update share metadata.'),
redirect=redirect)
class AddRule(forms.SelfHandlingForm):
access_type = forms.ChoiceField(
label=_("Access Type"), required=True,
choices=(('ip', 'ip'), ('user', 'user'), ('cert', 'cert'),
('cephx', 'cephx')))
access_level = forms.ChoiceField(
label=_("Access Level"), required=True,
choices=(('rw', 'read-write'), ('ro', 'read-only'),))
access_to = forms.CharField(
label=_("Access To"), max_length="255", required=True)
def handle(self, request, data):
share_id = self.initial['share_id']
try:
manila.share_allow(
request, share_id,
access_to=data['access_to'],
access_type=data['access_type'],
access_level=data['access_level'])
message = _('Creating rule for "%s"') % data['access_to']
messages.success(request, message)
return True
except Exception:
redirect = reverse("horizon:project:shares:manage_rules",
args=[self.initial['share_id']])
exceptions.handle(
request, _('Unable to add rule.'), redirect=redirect)
class ExtendForm(forms.SelfHandlingForm):
name = forms.CharField(
max_length="255", label=_("Share Name"),
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
required=False,
)
orig_size = forms.IntegerField(
label=_("Current Size (GiB)"),
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
required=False,
)
new_size = forms.IntegerField(
label=_("New Size (GiB)"),
)
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:
message = _("New size must be greater than current size.")
self._errors["new_size"] = self.error_class([message])
return cleaned_data
usages = quotas.tenant_limit_usages(self.request)
availableGB = (usages['maxTotalShareGigabytes'] -
usages['totalShareGigabytesUsed'])
if availableGB < (new_size - orig_size):
message = _('Share cannot be extended to %(req)iGiB as '
'you only have %(avail)iGiB of your quota '
'available.')
params = {'req': new_size, 'avail': availableGB + orig_size}
self._errors["new_size"] = self.error_class([message % params])
return cleaned_data
def handle(self, request, data):
share_id = self.initial['share_id']
try:
manila.share_extend(request, share_id, data['new_size'])
message = _('Extend share "%s"') % data['name']
messages.success(request, message)
return True
except Exception:
redirect = reverse("horizon:project:shares:index")
exceptions.handle(request,
_('Unable to extend share.'),
redirect=redirect)