2392da498a
Added a new action to create new volume from a volume snapshot. The snapshot_id is passed to nova volume API. The 'Create Volume' form is reused, with the 'size' value being checked against the snapshot size, as it cannot be less than the snapshot size. Fixes bug 1023740 PATCH SET 4: Added dropdown to select snapshot source in 'Create Volume' form, when loaded from /nova/volumes/ When a snapshot is selected from the dropdown, the name and size are pre-filled in the form (I've removed the description copy as it is probably specific for the reason a snapshot is being created). PATCH SET 6: rebased with master, fixed a bug with get_context in nova/volume/views Change-Id: I20dd8d698f5d8481938417557e09dcdc9b891e82
129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
import re
|
|
import netaddr
|
|
from django.core.exceptions import ValidationError
|
|
from django.forms import forms, widgets
|
|
from django.utils.translation import ugettext as _
|
|
from django.utils.encoding import force_unicode
|
|
from django.utils.html import escape, conditional_escape
|
|
from django.utils.functional import Promise
|
|
|
|
ip_allowed_symbols_re = re.compile(r'^[a-fA-F0-9:/\.]+$')
|
|
IPv4 = 1
|
|
IPv6 = 2
|
|
|
|
|
|
class IPField(forms.Field):
|
|
"""
|
|
Form field for entering IP/range values, with validation.
|
|
Supports IPv4/IPv6 in the format:
|
|
.. xxx.xxx.xxx.xxx
|
|
.. xxx.xxx.xxx.xxx/zz
|
|
.. ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
|
.. ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/zz
|
|
and all compressed forms. Also the short forms
|
|
are supported:
|
|
xxx/yy
|
|
xxx.xxx/yy
|
|
|
|
.. attribute:: version
|
|
|
|
Specifies which IP version to validate,
|
|
valid values are 1 (fields.IPv4), 2 (fields.IPv6) or
|
|
both - 3 (fields.IPv4 | fields.IPv6).
|
|
Defaults to IPv4 (1)
|
|
|
|
.. attribute:: mask
|
|
|
|
Boolean flag to validate subnet masks along with IP address.
|
|
E.g: 10.0.0.1/32
|
|
|
|
.. attribute:: mask_range_from
|
|
Subnet range limitation, e.g. 16
|
|
That means the input mask will be checked to be in the range
|
|
16:max_value. Useful to limit the subnet ranges
|
|
to A/B/C-class networks.
|
|
"""
|
|
invalid_format_message = _("Incorrect format for IP address")
|
|
invalid_version_message = _("Invalid version for IP address")
|
|
invalid_mask_message = _("Invalid subnet mask")
|
|
max_v4_mask = 32
|
|
max_v6_mask = 128
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.mask = kwargs.pop("mask", None)
|
|
self.min_mask = kwargs.pop("mask_range_from", 0)
|
|
self.version = kwargs.pop('version', IPv4)
|
|
|
|
super(IPField, self).__init__(*args, **kwargs)
|
|
|
|
def validate(self, value):
|
|
super(IPField, self).validate(value)
|
|
if not value and not self.required:
|
|
return
|
|
|
|
try:
|
|
if self.mask:
|
|
self.ip = netaddr.IPNetwork(value)
|
|
else:
|
|
self.ip = netaddr.IPAddress(value)
|
|
except:
|
|
raise ValidationError(self.invalid_format_message)
|
|
|
|
if not any([self.version & IPv4 > 0 and self.ip.version == 4,
|
|
self.version & IPv6 > 0 and self.ip.version == 6]):
|
|
raise ValidationError(self.invalid_version_message)
|
|
|
|
if self.mask:
|
|
if self.ip.version == 4 and \
|
|
not self.min_mask <= self.ip.prefixlen <= self.max_v4_mask:
|
|
raise ValidationError(self.invalid_mask_message)
|
|
|
|
if self.ip.version == 6 and \
|
|
not self.min_mask <= self.ip.prefixlen <= self.max_v6_mask:
|
|
raise ValidationError(self.invalid_mask_message)
|
|
|
|
def clean(self, value):
|
|
super(IPField, self).clean(value)
|
|
return str(getattr(self, "ip", ""))
|
|
|
|
|
|
class SelectWidget(widgets.Select):
|
|
"""
|
|
Customizable select widget, that allows to render
|
|
data-xxx attributes from choices.
|
|
|
|
.. attribute:: data_attrs
|
|
|
|
Specifies object properties to serialize as
|
|
data-xxx attribute. If passed ('id', ),
|
|
this will be rendered as:
|
|
<option data-id="123">option_value</option>
|
|
where 123 is the value of choice_value.id
|
|
|
|
.. attribute:: transform
|
|
|
|
A callable used to render the display value
|
|
from the option object.
|
|
"""
|
|
def __init__(self, attrs=None, choices=(), data_attrs=(), transform=None):
|
|
self.data_attrs = data_attrs
|
|
self.transform = transform
|
|
super(SelectWidget, self).__init__(attrs, choices)
|
|
|
|
def render_option(self, selected_choices, option_value, option_label):
|
|
option_value = force_unicode(option_value)
|
|
other_html = (option_value in selected_choices) and \
|
|
u' selected="selected"' or ''
|
|
if not isinstance(option_label, (basestring, Promise)):
|
|
for data_attr in self.data_attrs:
|
|
data_value = conditional_escape(
|
|
force_unicode(getattr(option_label,
|
|
data_attr, "")))
|
|
other_html += ' data-%s="%s"' % (data_attr, data_value)
|
|
|
|
if self.transform:
|
|
option_label = self.transform(option_label)
|
|
return u'<option value="%s"%s>%s</option>' % (
|
|
escape(option_value), other_html,
|
|
conditional_escape(force_unicode(option_label)))
|