示例#1
0
class ImportKeypair(forms.SelfHandlingForm):
    name = forms.RegexField(max_length=255,
                            label=_("Key Pair Name"),
                            regex=KEYPAIR_NAME_REGEX,
                            error_messages=KEYPAIR_ERROR_MESSAGES)
    key_type = forms.ChoiceField(label=_("Key Type"),
                                 widget=forms.SelectWidget(),
                                 choices=[('ssh', _("SSH Key")),
                                          ('x509', _("X509 Certificate"))],
                                 initial='ssh')
    public_key = forms.CharField(label=_("Public Key"),
                                 widget=forms.Textarea())

    def handle(self, request, data):
        try:
            # Remove any new lines in the ssh public key
            if data['key_type'] == 'ssh':
                data['public_key'] = NEW_LINES.sub("", data['public_key'])
            keypair = api.nova.keypair_import(request,
                                              data['name'],
                                              data['public_key'],
                                              data['key_type'])
            messages.success(request,
                             _('Successfully imported public key: %s')
                             % data['name'])
            return keypair
        except Exception:
            exceptions.handle(request, ignore=True)
            self.api_error(_('Unable to import key pair.'))
            return False
class EnableForm(forms.SelfHandlingForm):
    volume_id = forms.ChoiceField(
        label=_("Select a volume to enable"),
        widget=forms.SelectWidget(attrs={'class': 'image-selector'},
                                  data_attrs=('size', 'name'),
                                  transform=lambda x: "%s (%s)" %
                                  (x.name, x.id)),
        required=True)
    name = forms.CharField(max_length=255, label=_("Name"), required=False)
    description = forms.CharField(max_length=255,
                                  widget=forms.Textarea(attrs={'rows': 4}),
                                  label=_("Description"),
                                  required=False)

    def __init__(self, request, *args, **kwargs):
        super(EnableForm, self).__init__(request, *args, **kwargs)
        cinder_volumes = self.get_cinder_volumes(request)
        sg_volumes = []
        choices = [('', _("Choose a volume"))]
        for vol in sg_api.volume_list(request):
            sg_volumes.append(vol.id)
        if cinder_volumes:
            choices = [('', _("Choose a volume"))]
            for volume in cinder_volumes:
                if volume.status == "available":
                    choices.append((volume.id, volume))
        self.fields['volume_id'].choices = choices

    def handle(self, request, data):
        try:
            result = None
            volume = cinder.volume_get(request, data['volume_id'])
            if not volume:
                message = _('Volume not exist,id:"%s".') % data['volume_id']
            else:
                message = _('Enabling volume "%s".') % data['name']
                result = sg_api.volume_enable(request, data['volume_id'],
                                              data['name'],
                                              data['description'])
            messages.info(request, message)
            return result
        except Exception:
            redirect = reverse("horizon:storage-gateway:volumes:index")
            msg = _('Unable to enable volume:%s.') % data['volume_id']
            exceptions.handle(request, msg, redirect=redirect)

    def get_cinder_volumes(self, request):
        volumes = []
        try:
            available = api.cinder.VOLUME_STATE_AVAILABLE
            volumes = cinder.volume_list(self.request,
                                         search_opts=dict(status=available))
        except Exception:
            exceptions.handle(request,
                              _('Unable to retrieve list of volumes.'))
        return volumes
示例#3
0
class CloneCGroupForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255, label=_("Consistency Group Name"))
    description = forms.CharField(max_length=255,
                                  widget=forms.Textarea(attrs={'rows': 4}),
                                  label=_("Description"),
                                  required=False)
    cgroup_source = forms.ChoiceField(
        label=_("Use a consistency group as source"),
        widget=forms.SelectWidget(attrs={'class': 'image-selector'},
                                  data_attrs=('name'),
                                  transform=lambda x: "%s" % (x.name)),
        required=False)

    def prepare_cgroup_source_field(self, request, cgroup_id):
        try:
            cgroup = cinder.volume_cgroup_get(request, cgroup_id)
            self.fields['cgroup_source'].choices = ((cgroup_id, cgroup), )
        except Exception:
            exceptions.handle(
                request, _('Unable to load the specified '
                           'consistency group.'))

    def __init__(self, request, *args, **kwargs):
        super(CloneCGroupForm, self).__init__(request, *args, **kwargs)

        # populate cgroup_id
        cgroup_id = kwargs.get('initial', {}).get('cgroup_id', [])
        self.fields['cgroup_id'] = forms.CharField(widget=forms.HiddenInput(),
                                                   initial=cgroup_id)
        self.prepare_cgroup_source_field(request, cgroup_id)

    def handle(self, request, data):
        try:
            message = _('Creating consistency group "%s".') % data['name']
            cgroup = cinder.volume_cgroup_create_from_source(
                request,
                data['name'],
                source_cgroup_id=data['cgroup_id'],
                description=data['description'])

            messages.info(request, message)
            return cgroup
        except Exception:
            redirect = reverse("horizon:project:volumes:index")
            msg = _('Unable to clone consistency group.')

            search_opts = {'consistentcygroup_id': data['cgroup_id']}
            volumes = cinder.volume_list(request, search_opts=search_opts)
            if len(volumes) == 0:
                msg = _('Unable to clone empty consistency group.')

            exceptions.handle(request, msg, redirect=redirect)
示例#4
0
class ImageChoiceField(ChoiceField):
    widget = hz_forms.SelectWidget(transform=_get_title,
                                   transform_html_attrs=_disable_non_ready)

    def __init__(self, *args, **kwargs):
        self.image_type = kwargs.pop('image_type', None)
        super(ImageChoiceField, self).__init__(*args, **kwargs)

    @with_request
    def update(self, request, form=None, **kwargs):
        image_map, image_choices = {}, []
        murano_images = get_murano_images(request,
                                          getattr(form, 'region', None))
        for image in murano_images:
            murano_data = image.murano_property
            title = murano_data.get('title', image.name)

            if image.status == 'active':
                title = Choice(title, enabled=True)
            else:
                title = Choice("{} ({})".format(title, image.status),
                               enabled=False)
            if self.image_type is not None:
                itype = murano_data.get('type')

                if not self.image_type and itype is None:
                    continue

                prefix = '{type}.'.format(type=self.image_type)
                if (not itype.startswith(prefix)
                        and not self.image_type == itype):
                    continue
            image_map[image.id] = title

        for id_, title in sorted(six.iteritems(image_map),
                                 key=lambda e: e[1].title):
            image_choices.append((id_, title))
        if image_choices:
            image_choices.insert(0, ("", _("Select Image")))
        else:
            image_choices.insert(0, ("", _("No images available")))

        self.choices = image_choices
示例#5
0
class SelectPortAction(workflows.Action):
    port = forms.ChoiceField(
        label=_("Port"),
        help_text=_("Create tap flow with"
                    " this port"),
        widget=forms.SelectWidget(transform=lambda x: ("%s (%s) %s" % (
            x.name, x.id, x["fixed_ips"][0]["ip_address"]))))

    def __init__(self, request, *args, **kwargs):
        super(SelectPortAction, self).__init__(request, *args, **kwargs)
        port_list = self.fields["port"].choices
        if len(port_list) == 1:
            self.fields['port'].initial = [port_list[0][0]]

    class Meta(object):
        name = _("Port")
        permissions = ('openstack.services.network', )
        help_text = _("Select port for your tap flow.")

    def populate_port_choices(self, request, context):
        return ts_utils.port_field_data(request, )
示例#6
0
class RebuildForm(forms.SelfHandlingForm):
    router_id = forms.CharField(label=_("ID"),
                                widget=forms.HiddenInput(),
                                required=True)
    router_name = forms.CharField(label=_("Router Name"),
                                  widget=forms.HiddenInput(),
                                  required=False)
    attrs = {'class': 'image-selector'}
    image = forms.ChoiceField(label=_("Select Image"),
                              widget=forms.SelectWidget(
                                  attrs=attrs,
                                  data_attrs=('size', 'display-name'),
                                  transform=_image_choice_title),
                              required=False)

    def __init__(self, request, *args, **kwargs):
        super(RebuildForm, self).__init__(request, *args, **kwargs)
        images = utils.get_available_images(request, request.user.tenant_id)
        choices = [(image.id, image) for image in images]
        if choices:
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        self.fields['image'].choices = choices

    def handle(self, request, data):
        try:
            if data['image']:
                rc.router_rebuild(request, data['router_id'], data['image'])
            else:
                rc.router_rebuild(request, data['router_id'])
            messages.success(request,
                             _('Rebuilt Router: %s.') % data['router_name'])
        except Exception:
            exceptions.handle(
                request,
                _('Unable to rebuild router %s.' % data['router_name']))
        return True
示例#7
0
class CreateSubnetInfoAction(workflows.Action):
    subnet_name = forms.CharField(max_length=255,
                                  widget=forms.TextInput(attrs={}),
                                  label=_("Subnet Name"),
                                  required=False)

    address_source = forms.ChoiceField(
        required=False,
        label=_('Network Address Source'),
        choices=[('manual', _('Enter Network Address manually')),
                 ('subnetpool', _('Allocate Network Address from a pool'))],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'source',
        }))

    subnetpool = forms.ChoiceField(
        label=_("Address pool"),
        widget=forms.SelectWidget(attrs={
            'class': 'switched switchable',
            'data-slug': 'subnetpool',
            'data-switch-on': 'source',
            'data-source-subnetpool': _('Address pool')
        },
                                  data_attrs=('name', 'prefixes', 'ip_version',
                                              'min_prefixlen', 'max_prefixlen',
                                              'default_prefixlen'),
                                  transform=lambda x: "%s (%s)" %
                                  (x.name, ", ".join(x.prefixes))
                                  if 'prefixes' in x else "%s" % (x.name)),
        required=False)

    prefixlen = forms.ChoiceField(
        widget=forms.Select(attrs={
            'class': 'switched',
            'data-switch-on': 'subnetpool',
        }),
        label=_('Network Mask'),
        required=False)

    cidr = forms.IPField(label=_("Network Address"),
                         required=False,
                         initial="",
                         widget=forms.TextInput(
                             attrs={
                                 'class': 'switched',
                                 'data-switch-on': 'source',
                                 'data-source-manual': _("Network Address"),
                             }),
                         help_text=_("Network address in CIDR format "
                                     "(e.g. 192.168.0.0/24, 2001:DB8::/48)"),
                         version=forms.IPv4 | forms.IPv6,
                         mask=True)
    ip_version = forms.ChoiceField(
        choices=[(4, 'IPv4'), (6, 'IPv6')],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'ipversion',
        }),
        label=_("IP Version"),
        required=False)
    gateway_ip = forms.IPField(
        label=_("Gateway IP"),
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'gateway_ip',
                'data-source-manual': _("Gateway IP")
            }),
        required=False,
        initial="",
        help_text=_("IP address of Gateway (e.g. 192.168.0.254) "
                    "The default value is the first IP of the "
                    "network address "
                    "(e.g. 192.168.0.1 for 192.168.0.0/24, "
                    "2001:DB8::1 for 2001:DB8::/48). "
                    "If you use the default, leave blank. "
                    "If you do not want to use a gateway, "
                    "check 'Disable Gateway' below."),
        version=forms.IPv4 | forms.IPv6,
        mask=False)
    no_gateway = forms.BooleanField(label=_("Disable Gateway"),
                                    widget=forms.CheckboxInput(
                                        attrs={
                                            'class': 'switchable',
                                            'data-slug': 'gateway_ip',
                                            'data-hide-on-checked': 'true'
                                        }),
                                    initial=False,
                                    required=False)

    check_subnet_range = True

    class Meta(object):
        name = _("Subnet")
        help_text = _('Creates a subnet associated with the network.'
                      ' You need to enter a valid "Network Address"'
                      ' and "Gateway IP". If you did not enter the'
                      ' "Gateway IP", the first value of a network'
                      ' will be assigned by default. If you do not want'
                      ' gateway please check the "Disable Gateway" checkbox.'
                      ' Advanced configuration is available by clicking on'
                      ' the "Subnet Details" tab.')

    def __init__(self, request, context, *args, **kwargs):
        super(CreateSubnetInfoAction, self).__init__(request, context, *args,
                                                     **kwargs)
        if 'with_subnet' in context:
            self.fields['with_subnet'] = forms.BooleanField(
                initial=context['with_subnet'],
                required=False,
                widget=forms.HiddenInput())

        if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get(
                'enable_ipv6', True):
            self.fields['ip_version'].widget = forms.HiddenInput()
            self.fields['ip_version'].initial = 4

        try:
            if api.neutron.is_extension_supported(request,
                                                  'subnet_allocation'):
                self.fields['subnetpool'].choices = \
                    self.get_subnetpool_choices(request)
            else:
                self.hide_subnetpool_choices()
        except Exception:
            self.hide_subnetpool_choices()
            msg = _('Unable to initialize subnetpools')
            exceptions.handle(request, msg)
        if len(self.fields['subnetpool'].choices) > 1:
            # Pre-populate prefixlen choices to satisfy Django
            # ChoiceField Validation. This is overridden w/data from
            # subnetpool on select.
            self.fields['prefixlen'].choices = \
                zip(list(range(0, 128 + 1)),
                    list(range(0, 128 + 1)))
            # Populate data-fields for switching the prefixlen field
            # when user selects a subnetpool other than
            # "Provider default pool"
            for (id, name) in self.fields['subnetpool'].choices:
                if not len(id):
                    continue
                key = 'data-subnetpool-' + id
                self.fields['prefixlen'].widget.attrs[key] = \
                    _('Network Mask')
        else:
            self.hide_subnetpool_choices()

    def get_subnetpool_choices(self, request):
        subnetpool_choices = [('', _('Select a pool'))]

        for subnetpool in api.neutron.subnetpool_list(request):
            subnetpool_choices.append((subnetpool.id, subnetpool))
        return subnetpool_choices

    def hide_subnetpool_choices(self):
        self.fields['address_source'].widget = forms.HiddenInput()
        self.fields['subnetpool'].choices = []
        self.fields['subnetpool'].widget = forms.HiddenInput()
        self.fields['prefixlen'].widget = forms.HiddenInput()

    def _check_subnet_range(self, subnet, allow_cidr):
        allowed_net = netaddr.IPNetwork(allow_cidr)
        return subnet in allowed_net

    def _check_cidr_allowed(self, ip_version, subnet):
        if not self.check_subnet_range:
            return

        allowed_cidr = getattr(settings, "ALLOWED_PRIVATE_SUBNET_CIDR", {})
        version_str = 'ipv%s' % ip_version
        allowed_ranges = allowed_cidr.get(version_str, [])
        if allowed_ranges:
            under_range = any(
                self._check_subnet_range(subnet, allowed_range)
                for allowed_range in allowed_ranges)
            if not under_range:
                range_str = ', '.join(allowed_ranges)
                msg = (_("CIDRs allowed for user private %(ip_ver)s "
                         "networks are %(allowed)s.") % {
                             'ip_ver': '%s' % version_str,
                             'allowed': range_str
                         })
                raise forms.ValidationError(msg)

    def _check_subnet_data(self, cleaned_data, is_create=True):
        cidr = cleaned_data.get('cidr')
        ip_version = int(cleaned_data.get('ip_version'))
        gateway_ip = cleaned_data.get('gateway_ip')
        no_gateway = cleaned_data.get('no_gateway')
        address_source = cleaned_data.get('address_source')
        subnetpool = cleaned_data.get('subnetpool')

        if not subnetpool and address_source == 'subnetpool':
            msg = _('Specify "Address pool" or select '
                    '"Enter Network Address manually" and specify '
                    '"Network Address".')
            raise forms.ValidationError(msg)
        if not cidr and address_source != 'subnetpool':
            msg = _('Specify "Network Address" or '
                    'clear "Create Subnet" checkbox in previous step.')
            raise forms.ValidationError(msg)
        if cidr:
            subnet = netaddr.IPNetwork(cidr)
            if subnet.version != ip_version:
                msg = _('Network Address and IP version are inconsistent.')
                raise forms.ValidationError(msg)
            if (ip_version == 4 and subnet.prefixlen == 32) or \
                    (ip_version == 6 and subnet.prefixlen == 128):
                msg = _("The subnet in the Network Address is "
                        "too small (/%s).") % subnet.prefixlen
                self._errors['cidr'] = self.error_class([msg])
            self._check_cidr_allowed(ip_version, subnet)

        if not no_gateway and gateway_ip:
            if netaddr.IPAddress(gateway_ip).version is not ip_version:
                msg = _('Gateway IP and IP version are inconsistent.')
                raise forms.ValidationError(msg)
        if not is_create and not no_gateway and not gateway_ip:
            msg = _('Specify IP address of gateway or '
                    'check "Disable Gateway" checkbox.')
            raise forms.ValidationError(msg)

    def clean(self):
        cleaned_data = super(CreateSubnetInfoAction, self).clean()
        with_subnet = cleaned_data.get('with_subnet')
        if not with_subnet:
            return cleaned_data
        self._check_subnet_data(cleaned_data)
        return cleaned_data
示例#8
0
文件: forms.py 项目: toha10/horizon
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255,
                           label=_("Volume Name"),
                           required=False)
    description = forms.CharField(max_length=255,
                                  widget=forms.Textarea(attrs={'rows': 4}),
                                  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 * units.Gi))),
        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 prepare_source_fields_if_snapshot_specified(self, request):
        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.'))

    def prepare_source_fields_if_image_specified(self, request):
        self.fields['availability_zone'].choices = \
            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'])

    def prepare_source_fields_if_volume_specified(self, request):
        self.fields['availability_zone'].choices = \
            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']

    def prepare_source_fields_default(self, request):
        source_type_choices = []
        self.fields['availability_zone'].choices = \
            availability_zones(request)

        try:
            available = api.cinder.VOLUME_STATE_AVAILABLE
            snapshots = cinder.volume_snapshot_list(
                request, search_opts=dict(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 __init__(self, request, *args, **kwargs):
        super(CreateForm, self).__init__(request, *args, **kwargs)
        volume_types = cinder.volume_type_list(request)
        self.fields['type'].choices = [("no_type", _("No volume type"))] + \
                                      [(type.name, type.name)
                                       for type in volume_types]

        if "snapshot_id" in request.GET:
            self.prepare_source_fields_if_snapshot_specified(request)
        elif 'image_id' in request.GET:
            self.prepare_source_fields_if_image_specified(request)
        elif 'volume_id' in request.GET:
            self.prepare_source_fields_if_volume_specified(request)
        else:
            self.prepare_source_fields_default(request)

    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

    def get_volumes(self, request):
        volumes = []
        try:
            available = api.cinder.VOLUME_STATE_AVAILABLE
            volumes = cinder.volume_list(self.request,
                                         search_opts=dict(status=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 = {}

            if data['type'] == 'no_type':
                data['type'] = ''

            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:
            redirect = reverse("horizon:project:volumes:index")
            exceptions.handle(request,
                              _("Unable to create volume."),
                              redirect=redirect)

    @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)
示例#9
0
class UpdateQuotaForm(forms.SelfHandlingForm):
    project_id = forms.CharField(label=_("Project ID"), widget=forms.HiddenInput, required=False)
    user_id = forms.CharField(label=_("User ID"), widget=forms.HiddenInput, required=False)

    # user_type = forms.ChoiceField(
    #     label=_("User Type"),
    #     required=True,
    #     choices=[('normal', _("Normal User")), ('credit', _("Credit User"))],
    #     widget=forms.Select(
    #         attrs={
    #             'class': 'switchable',
    #             'data-slug': 'user_type'
    #         }
    #     )
    # )
    #
    # credit_line = forms.FloatField(
    #     label=_("Credit Line"),
    #     required=False,
    #     min_value=0.0,
    #     widget=forms.TextInput(
    #         attrs={
    #             'class': 'switched',
    #             'data-switch-on': 'user_type',
    #             'data-user_type-credit': _('Credit'),
    #         }))
    # adjust_quota = forms.BooleanField(
    #     label=_("Adjust Quota"),
    #     required=False,
    #     widget=forms.CheckboxInput(
    #         attrs={
    #             'class': 'switchable',
    #             'data-slug': 'adjust_quota',
    #             'data-hide-on-checked': 'false'
    #         }
    #     )
    # )
    region_id = forms.ChoiceField(label=_("Regions"), required=True,
                                  widget=forms.SelectWidget(attrs={
                                       # 'class': 'switched',
                                       # 'data-switch-on': 'adjust_quota',
                                       # 'data-is-required': 'true',
                                       'style': 'width 10%'
                                   }))
    instances = forms.IntegerField(min_value=-1, label=_("Instances"),
                                   required=True,
                                   widget=forms.TextInput(attrs={
                                       # 'class': 'switched',
                                       # 'data-switch-on': 'adjust_quota',
                                       # 'data-is-required': 'true',
                                       'style': 'width 10%'
                                   }))

    cores = forms.IntegerField(min_value=2, label=_("VCPUs"),
                               required=True,
                               widget=forms.TextInput(attrs={
                                   # 'class': 'switched',
                                   # 'data-switch-on': 'adjust_quota',
                                   # 'data-is-required': 'true'
                               }))
    ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)"),
                             required=True,
                             widget=forms.TextInput(attrs={
                                 # 'class': 'switched',
                                 # 'data-switch-on': 'adjust_quota',
                                 # 'data-is-required': 'true'
                             }))
    volumes = forms.IntegerField(min_value=-1, label=_("Volumes"),
                                 required=True,
                                 widget=forms.TextInput(attrs={
                                     # 'class': 'switched',
                                     # 'data-switch-on': 'adjust_quota',
                                     # 'data-is-required': 'true'
                                 }))
    snapshots = forms.IntegerField(min_value=-1, label=_("Volume Snapshots"),
                                   required=True,
                                   widget=forms.TextInput(attrs={
                                       # 'class': 'switched',
                                       # 'data-switch-on': 'adjust_quota',
                                       # 'data-is-required': 'true'
                                   }))
    volume_gigabytes = forms.IntegerField(
        min_value=-1, label=_("Size of Volumes(GB)"),
        required=True,
        widget=forms.TextInput(attrs={
            # 'class': 'switched',
            # 'data-switch-on': 'adjust_quota',
            # 'data-is-required': 'true'
        }))
    snapshot_gigabytes = forms.IntegerField(
        min_value=-1, label=_("Size of Snapshots (GB)"),
        required=True,
        widget=forms.TextInput(attrs={
            # 'class': 'switched',
            # 'data-switch-on': 'adjust_quota',
            # 'data-is-required': 'true'
        }))
    floatingip = forms.IntegerField(min_value=-1, label=_("Floating IPs"),
                                    required=True,
                                    widget=forms.TextInput(attrs={
                                        # 'class': 'switched',
                                        # 'data-switch-on': 'adjust_quota',
                                        # 'data-is-required': 'true'
                                    }))
    network = forms.IntegerField(min_value=-1, label=_("Networks"),
                                 required=True,
                                 widget=forms.TextInput(attrs={
                                     # 'class': 'switched',
                                     # 'data-switch-on': 'adjust_quota',
                                     # 'data-is-required': 'true'
                                 }))
    router = forms.IntegerField(min_value=-1, label=_("Routers"),
                                required=True,
                                widget=forms.TextInput(attrs={
                                    # 'class': 'switched',
                                    # 'data-switch-on': 'adjust_quota',
                                    # 'data-is-required': 'true'
                                }))
    subnet = forms.IntegerField(min_value=-1, label=_("Subnets"),
                                required=True,
                                widget=forms.TextInput(attrs={
                                    # 'class': 'switched',
                                    # 'data-switch-on': 'adjust_quota',
                                    # 'data-is-required': 'true'
                                }))
    pool = forms.IntegerField(min_value=-1, label=_("Loadbalancers"),
                              required=True,
                              widget=forms.TextInput(attrs={
                                  # 'class': 'switched',
                                  # 'data-switch-on': 'adjust_quota',
                                  # 'data-is-required': 'true'
                              }))
    bandwidth = forms.IntegerField(required=False, widget=forms.HiddenInput)

    def __init__(self, request, *args, **kwargs):
        super(UpdateQuotaForm, self).__init__(request, *args, **kwargs)
        region_choices = []
        user_id = kwargs['initial'].get('user_id', None)
        regions = api.keystone.list_regions_for_user(self.request, user_id)
        for region in regions:
            region_choices.append((region['id'], region['description']))
        self.fields['region_id'].choices = region_choices
        # if kwargs['initial']['user_type'] == 'credit':
        #     self.fields['user_type'].choices = [('credit', _("Credit User"))]
        #
        # if policy.check((("identity", "project_admin_required"),), self.request):
        #     self.fields['credit_line'].validators.append(MaxValueValidator(settings.UPPER_CREDIT_LINE_FOR_PROJECT_ADMIN))
        #     self.fields['credit_line'].help_text = _('credit line is between 0~%s') % settings.UPPER_CREDIT_LINE_FOR_PROJECT_ADMIN


    def clean(self):
        cleaned_data = super(UpdateQuotaForm, self).clean()
        usages = quotas.tenant_quota_usages(
            self.request, tenant_id=self.initial['project_id'], region=cleaned_data['region_id'])
        # Validate the quota values before updating quotas.
        # add key based on cleaned data, do not update tenant_quota_usages because
        # do not know if hava affect on others
        need_trans = {'network': 'networks', 'subnet': 'subnets', 'router': 'routers', 'floatingip': 'floating_ips'}
        bad_values = []
        for key, value in cleaned_data.items():
            if key in need_trans:
                key = need_trans[key]
            used = usages[key].get('used', 0)
            if value is not None and value >= 0 and used > value:
                bad_values.append(_('%(used)s %(key)s used') %
                                  {'used': used,
                                   'key': quotas.QUOTA_NAMES.get(key, key)})
        if bad_values:
            value_str = ", ".join(bad_values)
            msg = (_('Quota value(s) cannot be less than the current usage '
                     'value(s): %s.') %
                   value_str)
            raise forms.ValidationError(msg)
        return cleaned_data

    # def _update_billing_accout(self, request, project_id, data):
    #     client = api.billing.RequestClient(request)
    #     account = client.get_account(project_id)
    #     if data['user_type'] == 'credit' and account['type'] != 'credit':
    #         ret = client.api_request('/account/change2credit/' + account['account_id'] + '?credit_line=' + str(data['credit_line']), method='GET')
    #         user = json.loads(ret.read())
    #         if user['success'] != 'success':
    #             raise
    #         return True
    #     else:
    #         # change credit line
    #         if data['user_type'] == 'credit':
    #             ret = client.api_request(
    #                 '/account/changecreditline/' + account['account_id'] + '?credit_line=' + str(data['credit_line']),
    #                 method='GET')
    #             user = json.loads(ret.read())
    #             if user['success'] != 'success':
    #                 raise
    #         return True

    # def _update_user(self, request, data):
    #     try:
    #         response = self._update_billing_accout(request, data['project_id'], data)
    #     except Exception:
    #         response = exceptions.handle(request, ignore=True)
    #         messages.error(request, _('Unable to update user account.'))
    #
    #     if isinstance(response, http.HttpResponse):
    #         return response
    #     else:
    #         return response

    def _update_project_quota(self, request, data, project_id):
        # Update the project quota.
        nova_data = dict(
            [(key, data[key]) for key in NOVA_QUOTA_FIELDS])
        try:
            api.nova.tenant_quota_update(request, project_id, region=data['region_id'], **nova_data)

            if api.base.is_service_enabled(request, 'volume'):
                cinder_data = dict([(key, data[key]) for key in
                                    quotas.CINDER_QUOTA_FIELDS])
                api.cinder.tenant_quota_update(request,
                                           project_id,
                                           region=data['region_id'],
                                           **cinder_data)
            if api.base.is_service_enabled(request, 'network') and \
                    api.neutron.is_quotas_extension_supported(request):
                neutron_data = {}
                for key in NEUTRON_QUOTA_FIELDS:
                    neutron_data[key] = data[key]
                api.neutron.tenant_quota_update(request,
                                                project_id,
                                                region=data['region_id'],
                                                **neutron_data)
        except Exception:
            exceptions.handle(request, _('Unable to set project quotas.'))
            raise

    def handle(self, request, data):
        # set primary project for user
        try:
            # user = self._update_user(request, data)
            # if not user:
            #     raise
            project_id = data['project_id']
            self._update_project_quota(request, data, project_id)
            messages.success(request,
                     _('User has been updated successfully.'))
        except Exception:
             # operation log
            config = _('Project ID: %s') % data['project_id']
            api.logger.Logger(request).create(resource_type='account', action_name='Modify Quotas',
                                              resource_name='Account', config=config,
                                              status='Error')
            return False
        
        # operation log
        config = _('Project ID: %s') % data['project_id']
        api.logger.Logger(request).create(resource_type='account', action_name='Modify Quotas',
                                          resource_name='Account', config=config,
                                          status='Success')
        return True
示例#10
0
class SetInstanceDetailsAction(workflows.Action):
    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
                                          required=False)

    name = forms.CharField(label=_("Instance Name"), max_length=255)

    flavor = forms.ChoiceField(label=_("Flavor"),
                               help_text=_("Size of image to launch."))

    count = forms.IntegerField(label=_("Instance Count"),
                               min_value=1,
                               initial=1,
                               help_text=_("Number of instances to launch."))

    source_type = forms.ChoiceField(label=_("Instance Boot Source"),
                                    required=True,
                                    help_text=_("Choose Your Boot Source "
                                                "Type."))

    instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
                                             required=False)

    volume_id = forms.ChoiceField(label=_("Volume"), required=False)

    volume_snapshot_id = forms.ChoiceField(label=_("Volume Snapshot"),
                                           required=False)

    image_id = forms.ChoiceField(
        label=_("Image Name"),
        required=False,
        widget=forms.SelectWidget(data_attrs=('volume_size', ),
                                  transform=lambda x:
                                  ("%s (%s)" %
                                   (x.name, filesizeformat(x.bytes)))))

    volume_size = forms.IntegerField(label=_("Device size (GB)"),
                                     required=False,
                                     help_text=_("Volume size in gigabytes "
                                                 "(integer value)."))

    device_name = forms.CharField(label=_("Device Name"),
                                  required=False,
                                  initial="vda",
                                  help_text=_("Volume mount point (e.g. 'vda' "
                                              "mounts at '/dev/vda')."))

    delete_on_terminate = forms.BooleanField(label=_("Delete on Terminate"),
                                             initial=False,
                                             required=False,
                                             help_text=_("Delete volume on "
                                                         "instance terminate"))

    class Meta:
        name = _("Details")
        help_text_template = ("project/instances/" "_launch_details_help.html")

    def __init__(self, request, context, *args, **kwargs):
        self._init_images_cache()
        self.request = request
        self.context = context
        super(SetInstanceDetailsAction, self).__init__(request, context, *args,
                                                       **kwargs)
        source_type_choices = [
            ('', _("Select source")),
            ("image_id", _("Boot from image")),
            ("instance_snapshot_id", _("Boot from snapshot")),
        ]
        if base.is_service_enabled(request, 'volume'):
            source_type_choices.append(("volume_id", _("Boot from volume")))

            try:
                if api.nova.extension_supported("BlockDeviceMappingV2Boot",
                                                request):
                    source_type_choices.append(
                        ("volume_image_id",
                         _("Boot from image (creates a new volume)")))
            except Exception:
                exceptions.handle(
                    request, _('Unable to retrieve extensions '
                               'information.'))

            source_type_choices.append(
                ("volume_snapshot_id",
                 _("Boot from volume snapshot (creates a new volume)")))
        self.fields['source_type'].choices = source_type_choices

    def clean(self):
        cleaned_data = super(SetInstanceDetailsAction, self).clean()

        count = cleaned_data.get('count', 1)
        # Prevent launching more instances than the quota allows
        usages = quotas.tenant_quota_usages(self.request)
        available_count = usages['instances']['available']
        if available_count < count:
            error_message = ungettext_lazy(
                'The requested instance '
                'cannot be launched as you only '
                'have %(avail)i of your quota '
                'available. ', 'The requested %(req)i instances '
                'cannot be launched as you only '
                'have %(avail)i of your quota '
                'available.', count)
            params = {'req': count, 'avail': available_count}
            raise forms.ValidationError(error_message % params)

        # Validate our instance source.
        source_type = self.data.get('source_type', None)

        if source_type in ('image_id', 'volume_image_id'):
            if source_type == 'volume_image_id':
                if not self.data.get('volume_size', None):
                    msg = _("You must set volume size")
                    self._errors['volume_size'] = self.error_class([msg])
                if not cleaned_data.get('device_name'):
                    msg = _("You must set device name")
                    self._errors['device_name'] = self.error_class([msg])
            if not cleaned_data.get('image_id'):
                msg = _("You must select an image.")
                self._errors['image_id'] = self.error_class([msg])
            else:
                # Prevents trying to launch an image needing more resources.
                try:
                    image_id = cleaned_data.get('image_id')
                    # We want to retrieve details for a given image,
                    # however get_available_images uses a cache of image list,
                    # so it is used instead of image_get to reduce the number
                    # of API calls.
                    images = image_utils.get_available_images(
                        self.request, self.context.get('project_id'),
                        self._images_cache)
                    image = [x for x in images if x.id == image_id][0]
                except IndexError:
                    image = None

                try:
                    flavor_id = cleaned_data.get('flavor')
                    # We want to retrieve details for a given flavor,
                    # however flavor_list uses a memoized decorator
                    # so it is used instead of flavor_get to reduce the number
                    # of API calls.
                    flavors = instance_utils.flavor_list(self.request)
                    flavor = [x for x in flavors if x.id == flavor_id][0]
                except IndexError:
                    flavor = None

                if image and flavor:
                    props_mapping = (("min_ram", "ram"), ("min_disk", "disk"))
                    for iprop, fprop in props_mapping:
                        if getattr(image, iprop) > 0 and \
                                getattr(image, iprop) > getattr(flavor, fprop):
                            msg = _(
                                "The flavor '%(flavor)s' is too small for "
                                "requested image.\n"
                                "Minimum requirements: "
                                "%(min_ram)s MB of RAM and "
                                "%(min_disk)s GB of Root Disk." % {
                                    'flavor': flavor.name,
                                    'min_ram': image.min_ram,
                                    'min_disk': image.min_disk
                                })
                            self._errors['image_id'] = self.error_class([msg])
                            break  # Not necessary to continue the tests.

                    volume_size = cleaned_data.get('volume_size')
                    if volume_size and source_type == 'volume_image_id':
                        volume_size = int(volume_size)
                        img_gigs = functions.bytes_to_gigabytes(image.size)
                        smallest_size = max(img_gigs, image.min_disk)
                        if volume_size < smallest_size:
                            msg = _(
                                "The Volume size is too small for the"
                                " '%(image_name)s' image and has to be"
                                " greater than or equal to "
                                "'%(smallest_size)d' GB." % {
                                    'image_name': image.name,
                                    'smallest_size': smallest_size
                                })
                            self._errors['volume_size'] = self.error_class(
                                [msg])

        elif source_type == 'instance_snapshot_id':
            if not cleaned_data['instance_snapshot_id']:
                msg = _("You must select a snapshot.")
                self._errors['instance_snapshot_id'] = self.error_class([msg])

        elif source_type == 'volume_id':
            if not cleaned_data.get('volume_id'):
                msg = _("You must select a volume.")
                self._errors['volume_id'] = self.error_class([msg])
            # Prevent launching multiple instances with the same volume.
            # TODO(gabriel): is it safe to launch multiple instances with
            # a snapshot since it should be cloned to new volumes?
            if count > 1:
                msg = _('Launching multiple instances is only supported for '
                        'images and instance snapshots.')
                raise forms.ValidationError(msg)

        elif source_type == 'volume_snapshot_id':
            if not cleaned_data.get('volume_snapshot_id'):
                msg = _("You must select a snapshot.")
                self._errors['volume_snapshot_id'] = self.error_class([msg])
            if not cleaned_data.get('device_name'):
                msg = _("You must set device name")
                self._errors['device_name'] = self.error_class([msg])

        return cleaned_data

    def populate_flavor_choices(self, request, context):
        flavors = instance_utils.flavor_list(request)
        if flavors:
            return instance_utils.sort_flavor_list(request, flavors)
        return []

    def populate_availability_zone_choices(self, request, context):
        try:
            zones = api.nova.availability_zone_list(request)
        except Exception:
            zones = []
            exceptions.handle(request,
                              _('Unable to retrieve availability zones.'))

        zone_list = [(zone.zoneName, zone.zoneName) for zone in zones
                     if zone.zoneState['available']]
        zone_list.sort()
        if not zone_list:
            zone_list.insert(0, ("", _("No availability zones found")))
        elif len(zone_list) > 1:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        return zone_list

    def get_help_text(self):
        extra = {}
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.dumps(
                [f._info for f in instance_utils.flavor_list(self.request)])
            extra['flavors'] = flavors
            images = image_utils.get_available_images(
                self.request, self.initial['project_id'], self._images_cache)
            if images is not None:
                attrs = [{
                    'id': i.id,
                    'min_disk': getattr(i, 'min_disk', 0),
                    'min_ram': getattr(i, 'min_ram', 0)
                } for i in images]
                extra['images'] = json.dumps(attrs)

        except Exception:
            exceptions.handle(self.request,
                              _("Unable to retrieve quota information."))
        return super(SetInstanceDetailsAction, self).get_help_text(extra)

    def _init_images_cache(self):
        if not hasattr(self, '_images_cache'):
            self._images_cache = {}

    def _get_volume_display_name(self, volume):
        if hasattr(volume, "volume_id"):
            vol_type = "snap"
            visible_label = _("Snapshot")
        else:
            vol_type = "vol"
            visible_label = _("Volume")
        return (("%s:%s" % (volume.id, vol_type)),
                (_("%(name)s - %(size)s GB (%(label)s)") % {
                    'name': volume.name,
                    'size': volume.size,
                    'label': visible_label
                }))

    def populate_image_id_choices(self, request, context):
        choices = []
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        for image in images:
            image.bytes = image.size
            image.volume_size = max(image.min_disk,
                                    functions.bytes_to_gigabytes(image.bytes))
            choices.append((image.id, image))
            if context.get('image_id') == image.id and \
                    'volume_size' not in context:
                context['volume_size'] = image.volume_size
        if choices:
            choices.sort(key=lambda c: c[1].name)
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        return choices

    def populate_instance_snapshot_id_choices(self, request, context):
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        choices = [(image.id, image.name) for image in images
                   if image.properties.get("image_type", '') == "snapshot"]
        if choices:
            choices.insert(0, ("", _("Select Instance Snapshot")))
        else:
            choices.insert(0, ("", _("No snapshots available")))
        return choices

    def populate_volume_id_choices(self, request, context):
        try:
            volumes = [
                self._get_volume_display_name(v)
                for v in cinder.volume_list(self.request)
                if v.status == api.cinder.VOLUME_STATE_AVAILABLE
                and v.bootable == 'true'
            ]
        except Exception:
            volumes = []
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volumes.'))
        if volumes:
            volumes.insert(0, ("", _("Select Volume")))
        else:
            volumes.insert(0, ("", _("No volumes available")))
        return volumes

    def populate_volume_snapshot_id_choices(self, request, context):
        try:
            snapshots = cinder.volume_snapshot_list(self.request)
            snapshots = [
                self._get_volume_display_name(s) for s in snapshots
                if s.status == api.cinder.VOLUME_STATE_AVAILABLE
            ]
        except Exception:
            snapshots = []
            exceptions.handle(
                self.request,
                _('Unable to retrieve list of volume '
                  'snapshots.'))
        if snapshots:
            snapshots.insert(0, ("", _("Select Volume Snapshot")))
        else:
            snapshots.insert(0, ("", _("No volume snapshots available")))
        return snapshots
示例#11
0
class LaunchInstance(workflows.Action):
    availability_zone = forms.ChoiceField(
        label=_("Availability Zone"), required=False)
    name = forms.CharField(label=_("Instance Name"), max_length=255)
    flavor = forms.ChoiceField(
        label=_("Flavor"), help_text=_("Size of image to launch."))
    count = forms.IntegerField(label=_(
        "Instance Count"),
        min_value=1,
        initial=1,
        help_text=_("Number of instances to launch."))
    image = forms.ChoiceField(label=_("Select Image"),
                              widget=forms.SelectWidget(
                                  attrs={'class': 'image-selector'},
                                  data_attrs=('size', 'display-name'),
                                  transform=_image_choice_title))

    def __init__(self, request, *args, **kwargs):
        super(LaunchInstance, self).__init__(request, *args, **kwargs)
        images = imageutils.get_available_images(
            request, request.user.tenant_id)
        choices = [(image.id, image) for image in images]
        if choices:
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        zones = self._availability_zone_choices(request)
        self.fields['image'].choices = choices
        self.fields['availability_zone'].choices = zones
        self.fields['flavor'].choices = self._flavor_choices(request)

    def _flavor_choices(self, request):
        flavors = utils.flavor_list(request)
        if flavors:
            return utils.sort_flavor_list(request, flavors)
        return []

    def _availability_zone_choices(self, request):
        try:
            zones = api.nova.availability_zone_list(request)
        except Exception:
            zones = []
            exceptions.handle(
                request, _('Unable to retrieve availability zones.'))
        zone_list = [(zone.zoneName, zone.zoneName)
                     for zone in zones if zone.zoneState['available']]
        zone_list.sort()
        if not zone_list:
            zone_list.insert(0, ("", _("No availability zones found")))
        elif len(zone_list) > 1:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        return zone_list

    def handle(self, request, context):
        policy_target_id = self.request.path.split("/")[-2]
        try:
            msg = _('Member was successfully created.')
            ep = client.pt_create(
                request, policy_target_group_id=policy_target_id)
            api.nova.server_create(request,
                                   context['name'],
                                   context['image'],
                                   context['flavor'],
                                   key_name=None,
                                   user_data=None,
                                   security_groups=None,
                                   instance_count=context['count'],
                                   nics=[{'port-id': ep.port_id}])
            LOG.debug(msg)
            messages.success(request, msg)
        except Exception:
            msg = _('Failed to launch VM')
            LOG.error(msg)
            u = "horizon:project:policytargets:policy_targetdetails"
            redirect = reverse(u, kwargs={'policy_target_id':
                policy_target_id})
            exceptions.handle(request, msg, redirect=redirect)
示例#12
0
class CreateImageForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255, label=_("Name"))
    description = forms.CharField(widget=forms.widgets.Textarea(attrs={
        'class': 'modal-body-fixed-width',
        'rows': 4
    }),
                                  label=_("Description"),
                                  required=False)
    source_type = forms.ChoiceField(
        label=_('Image Source'),
        required=False,
        choices=[('url', _('Image Location')), ('file', _('Image File'))],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'source'
        }))
    image_url_attrs = {
        'class': 'switched',
        'data-switch-on': 'source',
        'data-source-url': _('Image Location'),
        'ng-model': 'ctrl.copyFrom',
        'ng-change': 'ctrl.selectImageFormat(ctrl.copyFrom)'
    }
    image_url = ImageURLField(label=_("Image Location"),
                              help_text=_("An external (HTTP/HTTPS) URL to "
                                          "load the image from."),
                              widget=forms.TextInput(attrs=image_url_attrs),
                              required=False)
    image_attrs = {
        'class': 'switched',
        'data-switch-on': 'source',
        'data-source-file': _('Image File'),
        'ng-model': 'ctrl.imageFile',
        'ng-change': 'ctrl.selectImageFormat(ctrl.imageFile.name)',
        'image-file-on-change': None
    }
    image_file = forms.FileField(label=_("Image File"),
                                 help_text=_("A local image to upload."),
                                 widget=forms.FileInput(attrs=image_attrs),
                                 required=False)
    kernel = forms.ChoiceField(
        label=_('Kernel'),
        required=False,
        widget=forms.SelectWidget(transform=lambda x: "%s (%s)" % (
            x.name, defaultfilters.filesizeformat(x.size))))
    ramdisk = forms.ChoiceField(
        label=_('Ramdisk'),
        required=False,
        widget=forms.SelectWidget(transform=lambda x: "%s (%s)" % (
            x.name, defaultfilters.filesizeformat(x.size))))
    disk_format = forms.ChoiceField(
        label=_('Format'),
        choices=[],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'ng-model': 'ctrl.diskFormat'
        }))
    architecture = forms.CharField(max_length=255,
                                   label=_("Architecture"),
                                   required=False)
    minimum_disk = forms.IntegerField(
        label=_("Minimum Disk (GB)"),
        min_value=0,
        help_text=_('The minimum disk size required to boot the image. '
                    'If unspecified, this value defaults to 0 (no minimum).'),
        required=False)
    minimum_ram = forms.IntegerField(
        label=_("Minimum RAM (MB)"),
        min_value=0,
        help_text=_('The minimum memory size required to boot the image. '
                    'If unspecified, this value defaults to 0 (no minimum).'),
        required=False)
    is_copying = forms.BooleanField(
        label=_("Copy Data"),
        initial=True,
        required=False,
        help_text=_('Specify this option to copy image data to the image '
                    'service. If unspecified, image data will be used in its '
                    'current location.'),
        widget=forms.CheckboxInput(
            attrs={
                'class': 'switched',
                'data-source-url': _('Image Location'),
                'data-switch-on': 'source'
            }))
    is_public = forms.BooleanField(label=_("Public"), required=False)
    protected = forms.BooleanField(label=_("Protected"), required=False)

    def __init__(self, request, *args, **kwargs):
        super(CreateImageForm, self).__init__(request, *args, **kwargs)

        if (not settings.HORIZON_IMAGES_ALLOW_UPLOAD or not policy.check(
            (("image", "upload_image"), ), request)):
            self._hide_file_source_type()
        if not policy.check((("image", "set_image_location"), ), request):
            self._hide_url_source_type()
        if not policy.check((("image", "publicize_image"), ), request):
            self._hide_is_public()

        self.fields['disk_format'].choices = IMAGE_FORMAT_CHOICES

        try:
            kernel_images = api.glance.image_list_detailed(
                request, filters={'disk_format': 'aki'})[0]
        except Exception:
            kernel_images = []
            msg = _('Unable to retrieve image list.')
            messages.error(request, msg)

        if kernel_images:
            choices = [('', _("Choose an image"))]
            for image in kernel_images:
                choices.append((image.id, image))
            self.fields['kernel'].choices = choices
        else:
            del self.fields['kernel']

        try:
            ramdisk_images = api.glance.image_list_detailed(
                request, filters={'disk_format': 'ari'})[0]
        except Exception:
            ramdisk_images = []
            msg = _('Unable to retrieve image list.')
            messages.error(request, msg)

        if ramdisk_images:
            choices = [('', _("Choose an image"))]
            for image in ramdisk_images:
                choices.append((image.id, image))
            self.fields['ramdisk'].choices = choices
        else:
            del self.fields['ramdisk']

    def _hide_file_source_type(self):
        self.fields['image_file'].widget = HiddenInput()
        source_type = self.fields['source_type']
        source_type.choices = [
            choice for choice in source_type.choices if choice[0] != 'file'
        ]
        if len(source_type.choices) == 1:
            source_type.widget = HiddenInput()

    def _hide_url_source_type(self):
        self.fields['image_url'].widget = HiddenInput()
        source_type = self.fields['source_type']
        source_type.choices = [
            choice for choice in source_type.choices if choice[0] != 'url'
        ]
        if len(source_type.choices) == 1:
            source_type.widget = HiddenInput()

    def _hide_is_public(self):
        self.fields['is_public'].widget = HiddenInput()
        self.fields['is_public'].initial = False

    def clean(self):
        data = super(CreateImageForm, self).clean()

        # The image_file key can be missing based on particular upload
        # conditions. Code defensively for it here...
        image_file = data.get('image_file', None)
        image_url = data.get('image_url', None)

        if not image_url and not image_file:
            raise ValidationError(
                _("A image or external image location must be specified."))
        elif image_url and image_file:
            raise ValidationError(
                _("Can not specify both image and external image location."))
        else:
            return data

    def handle(self, request, data):
        meta = create_image_metadata(data, False)

        # Add image source file or URL to metadata
        if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and policy.check(
            (("image", "upload_image"), ), request)
                and data.get('image_file', None)):
            meta['data'] = self.files['image_file']
        elif data['is_copying']:
            meta['copy_from'] = data['image_url']
        else:
            meta['location'] = data['image_url']

        try:
            image = api.glance.image_create(request, **meta)
            messages.success(
                request,
                _('Your image %s has been queued for creation.') %
                meta['name'])
            return image
        except Exception as e:
            msg = _('Unable to create new image')
            # TODO(nikunj2512): Fix this once it is fixed in glance client
            if hasattr(e, 'code') and e.code == 400:
                if "Invalid disk format" in e.details:
                    msg = _('Unable to create new image: Invalid disk format '
                            '%s for image.') % meta['disk_format']
                elif "Image name too long" in e.details:
                    msg = _('Unable to create new image: Image name too long.')
                elif "not supported" in e.details:
                    msg = _('Unable to create new image: URL scheme not '
                            'supported.')

            exceptions.handle(request, msg)

            return False
示例#13
0
class CreateSiteDetailAction(workflows.Action):
    with_gw = forms.BooleanField(label=_("Add your Enterprise Branch Gateway"),
                                 initial=True,
                                 required=False)
    #LGCHOICES = (('Provider_AZ1_GW1', 'Cloud Provider Avail. Zone 1 - Gateway 1'), ('Provider_AZ2_GW1', 'Cloud Provider Avail. Zone 2 - Gateway 1'), ('Provider_AZ2_GW2', 'Cloud Provider Avail. Zone 2 - Gateway 2'), ('new', 'Add a New Gateway'))
    #lgwchoice = forms.ChoiceField(required=True, label='Add a Provider Gateway', choices=LGCHOICES)
    '''RGCHOICES = (('new', 'Add a New Gateway'), ('GW1_Data_Center_Berlin', 'Enterprise Data Center in Berlin - Gateway 1'))
    rgwchoice = forms.ChoiceField(required=True, label='New Gateway VM or Pre-Configured Enterprise Gateway', choices=RGCHOICES)'''
    name = forms.CharField(max_length=255,
                           label=_("Name your Enterprise Branch Gateway"),
                           help_text=_(
                               "Enterprise Gateway Name. This field is "
                               "optional."),
                           required=False)
    '''gw_endpoint = fields.IPField(label=_("Enterprise Gateway Public Management IP address"),
                          required=False,
                          initial="",
                          help_text=_("Enterprise Gateway Network address in IPv4 or IPv6 address format "
                                      "(e.g., 50.59.22.182)"),
                          version=fields.IPv4 | fields.IPv6,
                          mask=False)
    '''
    tenant_name = forms.CharField(
        max_length=255,
        required=True,
        label='Tenant Name',
        help_text=_("Enter a Tenant Name - it takes a string"))
    branch_name = forms.CharField(
        max_length=255,
        required=True,
        label='Branch Name',
        help_text=_("Enter a Branch Name - it takes a string"))

    KEYPAIR_IMPORT_URL = "horizon:project:access_and_security:keypairs:import"
    keypair = forms.DynamicChoiceField(
        label=_("Key Pair"),
        required=False,
        help_text=_("Key pair to use for authentication."),
        add_item_link=KEYPAIR_IMPORT_URL)

    image_id = forms.ChoiceField(
        label=_("Image Name"),
        required=False,
        widget=forms.SelectWidget(data_attrs=('volume_size', ),
                                  transform=lambda x:
                                  ("%s (%s)" %
                                   (x.name, filesizeformat(x.bytes)))))

    flavor = forms.ChoiceField(label=_("Flavor"),
                               help_text=_("Size of image to launch."))
    '''groups = forms.MultipleChoiceField(label=_("Security Groups"),
                                       required=False,
                                       initial=["default"],
                                       widget=forms.CheckboxSelectMultiple(),
                                       #widget=forms.RadioSelect(),
                                       help_text=_("Launch instance in these ""security groups."))'''

    groups = forms.ChoiceField(label=_("Security Groups"),
                               required=True,
                               initial=["default"],
                               widget=forms.RadioSelect,
                               help_text=_("Launch instance in these "
                                           "security groups."))

    volume_size = forms.IntegerField(label=_("Device size (GB)"),
                                     initial=1,
                                     min_value=0,
                                     required=False,
                                     help_text=_("Volume size in gigabytes "
                                                 "(integer value)."))

    class Meta:
        name = ("Local and Remote Gateways")
        help_text = _(
            'You can specify the Local (Provider) and Remote (Enterprise) Gateways in Local and Remote Sites resp. to add to this VPN.'
        )

    def __init__(self, request, context, *args, **kwargs):
        self._init_images_cache()
        self.request = request
        self.context = context
        super(CreateSiteDetailAction, self).__init__(request, context, *args,
                                                     **kwargs)

    def clean(self):
        cleaned_data = super(CreateSiteDetailAction, self).clean()
        return cleaned_data

    def _init_images_cache(self):
        if not hasattr(self, '_images_cache'):
            self._images_cache = {}

    @memoized.memoized_method
    def _get_keypair(self, keypair):
        try:
            # We want to retrieve details for a given keypair,
            # however keypair_list uses a memoized decorator
            # so it is used instead of keypair_get to reduce the number
            # of API calls.
            keypairs = instance_utils.keypair_list(self.request)
            keypair = [x for x in keypairs if x.id == keypair][0]
        except IndexError:
            keypair = None
        return keypair

    @memoized.memoized_method
    def _get_flavor(self, flavor_id):
        try:
            # We want to retrieve details for a given flavor,
            # however flavor_list uses a memoized decorator
            # so it is used instead of flavor_get to reduce the number
            # of API calls.
            flavors = instance_utils.flavor_list(self.request)
            flavor = [x for x in flavors if x.id == flavor_id][0]
        except IndexError:
            flavor = None
        return flavor

    @memoized.memoized_method
    def _get_image(self, image_id):
        try:
            # We want to retrieve details for a given image,
            # however get_available_images uses a cache of image list,
            # so it is used instead of image_get to reduce the number
            # of API calls.
            images = image_utils.get_available_images(
                self.request, self.context.get('project_id'),
                self._images_cache)
            image = [x for x in images if x.id == image_id][0]
        except IndexError:
            image = None
        return image

    def populate_keypair_choices(self, request, context):
        keypairs = instance_utils.keypair_field_data(request, False)
        if len(keypairs) == 2:
            self.fields['keypair'].initial = keypairs[1][0]
        return keypairs

    def populate_groups_choices(self, request, context):
        try:
            groups = api.network.security_group_list(request)
            security_group_list = [(sg.name, sg.name) for sg in groups]
        except Exception:
            exceptions.handle(request,
                              _('Unable to retrieve list of security groups'))
            security_group_list = []
        return security_group_list

    def populate_flavor_choices(self, request, context):
        return instance_utils.flavor_field_data(request, False)

    def populate_image_id_choices(self, request, context):
        choices = []
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        for image in images:
            image.bytes = image.virtual_size or image.size
            image.volume_size = max(image.min_disk,
                                    functions.bytes_to_gigabytes(image.bytes))
            choices.append((image.id, image))
            if context.get('image_id') == image.id and \
                    'volume_size' not in context:
                context['volume_size'] = image.volume_size
        if choices:
            choices.sort(key=lambda c: c[1].name)
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        return choices

    def get_help_text(self, extra_context=None):
        extra = {} if extra_context is None else dict(extra_context)
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.dumps(
                [f._info for f in instance_utils.flavor_list(self.request)])
            extra['flavors'] = flavors
            images = image_utils.get_available_images(
                self.request, images_cache=self._images_cache)
            if images is not None:
                attrs = [{
                    'id': i.id,
                    'min_disk': getattr(i, 'min_disk', 0),
                    'min_ram': getattr(i, 'min_ram', 0),
                    'size': functions.bytes_to_gigabytes(i.size)
                } for i in images]
                extra['images'] = json.dumps(attrs)

        except Exception:
            exceptions.handle(self.request,
                              _("Unable to retrieve quota information."))
        return super(CreateSiteDetailAction, self).get_help_text(extra)
示例#14
0
class CreateSubnetInfoAction(workflows.Action):
    subnet_name = forms.CharField(max_length=255,
                                  widget=forms.TextInput(attrs={}),
                                  label=_("Subnet Name"),
                                  required=False)

    address_source = forms.ChoiceField(
        required=False,
        label=_('Network Address Source'),
        choices=[('manual', _('Enter Network Address manually')),
                 ('subnetpool', _('Allocate Network Address from a pool'))],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'source',
        }))

    subnetpool = forms.ChoiceField(
        label=_("Address pool"),
        widget=forms.SelectWidget(attrs={
            'class': 'switched switchable',
            'data-slug': 'subnetpool',
            'data-switch-on': 'source',
            'data-source-subnetpool': _('Address pool')
        },
                                  data_attrs=('name', 'prefixes', 'ip_version',
                                              'min_prefixlen', 'max_prefixlen',
                                              'default_prefixlen'),
                                  transform=lambda x: "%s (%s)" %
                                  (x.name, ", ".join(x.prefixes))
                                  if 'prefixes' in x else "%s" % (x.name)),
        required=False)

    prefixlen = forms.ChoiceField(
        widget=forms.Select(attrs={
            'class': 'switched',
            'data-switch-on': 'subnetpool',
        }),
        label=_('Network Mask'),
        required=False)

    cidr = forms.IPField(label=_("Network Address"),
                         required=False,
                         initial="",
                         widget=forms.TextInput(
                             attrs={
                                 'class': 'switched',
                                 'data-switch-on': 'source',
                                 'data-source-manual': _("Network Address"),
                             }),
                         help_text=_("Network address in CIDR format "
                                     "(e.g. 192.168.0.0/24, 2001:DB8::/48)"),
                         version=forms.IPv4 | forms.IPv6,
                         mask=True)
    ip_version = forms.ChoiceField(
        choices=[(4, 'IPv4'), (6, 'IPv6')],
        widget=forms.Select(attrs={
            'class': 'switchable',
            'data-slug': 'ipversion',
        }),
        label=_("IP Version"),
        required=False)
    gateway_ip = forms.IPField(
        label=_("Gateway IP"),
        widget=forms.TextInput(
            attrs={
                'class': 'switched',
                'data-switch-on': 'source gateway_ip',
                'data-source-manual': _("Gateway IP")
            }),
        required=False,
        initial="",
        help_text=_("IP address of Gateway (e.g. 192.168.0.254) "
                    "The default value is the first IP of the "
                    "network address "
                    "(e.g. 192.168.0.1 for 192.168.0.0/24, "
                    "2001:DB8::1 for 2001:DB8::/48). "
                    "If you use the default, leave blank. "
                    "If you do not want to use a gateway, "
                    "check 'Disable Gateway' below."),
        version=forms.IPv4 | forms.IPv6,
        mask=False)
    no_gateway = forms.BooleanField(label=_("Disable Gateway"),
                                    widget=forms.CheckboxInput(
                                        attrs={
                                            'class': 'switchable',
                                            'data-slug': 'gateway_ip',
                                            'data-hide-on-checked': 'true'
                                        }),
                                    initial=False,
                                    required=False)
    msg = _('Specify "Network Address", "Address pool" or '
            'clear "Create Subnet" checkbox.')

    class Meta(object):
        name = _("Subnet")
        help_text = _('Create a subnet associated with the network. '
                      'Advanced configuration is available by clicking on the '
                      '"Subnet Details" tab.')

    def __init__(self, request, context, *args, **kwargs):
        super(CreateSubnetInfoAction, self).__init__(request, context, *args,
                                                     **kwargs)
        if 'with_subnet' in context:
            self.fields['with_subnet'] = forms.BooleanField(
                initial=context['with_subnet'],
                required=False,
                widget=forms.HiddenInput())

        if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get(
                'enable_ipv6', True):
            self.fields['ip_version'].widget = forms.HiddenInput()
            self.fields['ip_version'].initial = 4

        try:
            if api.neutron.is_extension_supported(request,
                                                  'subnet_allocation'):
                self.fields['subnetpool'].choices = \
                    self.get_subnetpool_choices(request)
            else:
                self.hide_subnetpool_choices()
        except Exception:
            self.hide_subnetpool_choices()
            msg = _('Unable to initialize subnetpools')
            exceptions.handle(request, msg)
        if len(self.fields['subnetpool'].choices) > 1:
            # Pre-populate prefixlen choices to satisfy Django
            # ChoiceField Validation. This is overridden w/data from
            # subnetpool on select.
            self.fields['prefixlen'].choices = \
                zip(list(range(0, 128 + 1)),
                    list(range(0, 128 + 1)))
            # Populate data-fields for switching the prefixlen field
            # when user selects a subnetpool other than
            # "Provider default pool"
            for (id, name) in self.fields['subnetpool'].choices:
                if not len(id):
                    continue
                key = 'data-subnetpool-' + id
                self.fields['prefixlen'].widget.attrs[key] = \
                    _('Network Mask')
        else:
            self.hide_subnetpool_choices()

    def get_subnetpool_choices(self, request):
        subnetpool_choices = [('', _('Select a pool'))]
        default_ipv6_subnet_pool_label = \
            getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get(
                'default_ipv6_subnet_pool_label', None)
        default_ipv4_subnet_pool_label = \
            getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}).get(
                'default_ipv4_subnet_pool_label', None)

        if default_ipv6_subnet_pool_label:
            subnetpool_dict = {
                'ip_version': 6,
                'name': default_ipv6_subnet_pool_label
            }
            subnetpool = api.neutron.SubnetPool(subnetpool_dict)
            subnetpool_choices.append(('', subnetpool))

        if default_ipv4_subnet_pool_label:
            subnetpool_dict = {
                'ip_version': 4,
                'name': default_ipv4_subnet_pool_label
            }
            subnetpool = api.neutron.SubnetPool(subnetpool_dict)
            subnetpool_choices.append(('', subnetpool))

        for subnetpool in api.neutron.subnetpool_list(request):
            subnetpool_choices.append((subnetpool.id, subnetpool))
        return subnetpool_choices

    def hide_subnetpool_choices(self):
        self.fields['address_source'].widget = forms.HiddenInput()
        self.fields['subnetpool'].choices = []
        self.fields['subnetpool'].widget = forms.HiddenInput()
        self.fields['prefixlen'].widget = forms.HiddenInput()

    def _check_subnet_data(self, cleaned_data, is_create=True):
        cidr = cleaned_data.get('cidr')
        ip_version = int(cleaned_data.get('ip_version'))
        gateway_ip = cleaned_data.get('gateway_ip')
        no_gateway = cleaned_data.get('no_gateway')
        address_source = cleaned_data.get('address_source')

        # When creating network from a pool it is allowed to supply empty
        # subnetpool_id signaling that Neutron should choose the default
        # pool configured by the operator. This is also part of the IPv6
        # Prefix Delegation Workflow.
        if not cidr and address_source != 'subnetpool':
            raise forms.ValidationError(self.msg)
        if cidr:
            subnet = netaddr.IPNetwork(cidr)
            if subnet.version != ip_version:
                msg = _('Network Address and IP version are inconsistent.')
                raise forms.ValidationError(msg)
            if (ip_version == 4 and subnet.prefixlen == 32) or \
                    (ip_version == 6 and subnet.prefixlen == 128):
                msg = _("The subnet in the Network Address is "
                        "too small (/%s).") % subnet.prefixlen
                raise forms.ValidationError(msg)
        if not no_gateway and gateway_ip:
            if netaddr.IPAddress(gateway_ip).version is not ip_version:
                msg = _('Gateway IP and IP version are inconsistent.')
                raise forms.ValidationError(msg)
        if not is_create and not no_gateway and not gateway_ip:
            msg = _('Specify IP address of gateway or '
                    'check "Disable Gateway".')
            raise forms.ValidationError(msg)

    def clean(self):
        cleaned_data = super(CreateSubnetInfoAction, self).clean()
        with_subnet = cleaned_data.get('with_subnet')
        if not with_subnet:
            return cleaned_data
        self._check_subnet_data(cleaned_data)
        return cleaned_data
示例#15
0
文件: forms.py 项目: zhaosod/horizon
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length="255", label=_("Volume Name"))
    description = forms.CharField(
        widget=forms.Textarea(attrs={'class': 'modal-body-fixed-width'}),
        label=_("Description"),
        required=False)
    type = forms.ChoiceField(label=_("Type"), required=False)
    size = forms.IntegerField(min_value=1, label=_("Size (GB)"))
    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)
    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 = [("", "")] + \
                                      [(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)
                min_disk_size = getattr(image, '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']

    # 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)
                min_disk_size = getattr(image, 'min_disk', 0)
                if (min_disk_size > 0 and data['size'] < image.min_disk):
                    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)
示例#16
0
class RebuildInstanceForm(forms.SelfHandlingForm):
    instance_id = forms.CharField(widget=forms.HiddenInput())

    image = forms.ChoiceField(
        label=_("New Image"),
        widget=forms.SelectWidget(attrs={'class': 'image-selector'},
                                  data_attrs=('size', 'display-name'),
                                  transform=_image_choice_title))
    username = forms.CharField(max_length=64, label=_("User Name"))
    sync_set_root = forms.BooleanField(label=_("Sync set root/Administrator password"), initial=True, required=False)
    password = forms.RegexField(
        label=_("Rebuild Password"),
        required=False,
        widget=forms.PasswordInput(render_value=False, attrs={'placeholder': _('begin letter,8-64 bits,with number,letter and symbol')}),
        regex=validators.password_validator(),
        error_messages={'invalid': validators.password_validator_msg()})
    confirm_password = forms.CharField(
        label=_("Confirm Rebuild Password"),
        required=False,
        widget=forms.PasswordInput(render_value=False))
    # disk_config = forms.ChoiceField(label=_("Disk Partition"),
    #                                 required=False)

    def __init__(self, request, *args, **kwargs):
        super(RebuildInstanceForm, self).__init__(request, *args, **kwargs)
        instance_id = kwargs.get('initial', {}).get('instance_id')
        self.fields['instance_id'].initial = instance_id
        self.fields["username"].initial = 'syscloud'

        images = utils.get_available_images(request, request.user.tenant_id)
        choices = [(image.id, image) for image in images]
        if choices:
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        self.fields['image'].choices = choices

        if not api.nova.can_set_server_password():
            del self.fields['password']
            del self.fields['confirm_password']

        # try:
        #     if not api.nova.extension_supported("DiskConfig", request):
        #         del self.fields['disk_config']
        #     else:
        #         # Set our disk_config choices
        #         config_choices = [("AUTO", _("Automatic")),
        #                           ("MANUAL", _("Manual"))]
        #         self.fields['disk_config'].choices = config_choices
        # except Exception:
        #     exceptions.handle(request, _('Unable to retrieve extensions '
        #                                  'information.'))

    def clean(self):
        cleaned_data = super(RebuildInstanceForm, self).clean()
        is_allow_inject_passwd = False
        select_image_id = cleaned_data.get('image', None)
        if select_image_id != None:
            image = api.glance.image_get(self.request, select_image_id)
            is_allow_inject_passwd = getattr(image, 'is_allow_inject_passwd', False)
            cleaned_data['is_allow_inject_passwd'] = is_allow_inject_passwd
            cleaned_data['image_name'] = image.name
        if 'password' in cleaned_data:
            passwd = cleaned_data.get('password')
            confirm = cleaned_data.get('confirm_password')
            if passwd is not None and confirm is not None:
                if passwd != confirm:
                    raise forms.ValidationError(_("Passwords do not match."))
                if is_allow_inject_passwd:
                    if not validators.validate_password(passwd):
                        raise forms.ValidationError(_("The password must begin with a letter, "
                                                  "and the length is between the 8-64 bits, "
                                                  "and the number, the letter and the symbol are the same."))
        return cleaned_data

    # We have to protect the entire "data" dict because it contains the
    # password and confirm_password strings.
    @sensitive_variables('data', 'password')
    def handle(self, request, data):
        instance = data.get('instance_id')
        image = data.get('image')
        image_name = data.get('image_name', None)
        is_allow_inject_passwd = data.get('is_allow_inject_passwd', None)
        username = data.get('username', 'syscloud')
        password = data.get('password') or None
        disk_config = data.get('disk_config', None)
        is_sync_set_root = data.get('sync_set_root', False)

        if is_allow_inject_passwd:
            user_data_script_helper = userdata.UserDataScriptHelper(image_name, is_sync_set_root=is_sync_set_root, is_rebuild=True)
            password = user_data_script_helper.get_user_data(username, password)

        try:
            api.nova.server_rebuild(request, instance, image, password,
                                    disk_config)
            messages.success(request, _('Rebuilding instance %s.') % instance)

            # operation log
            config = _('Instance ID: %s') %instance
            api.logger.Logger(request).create(resource_type='instance', action_name='Rebuild Instance',
                                                       resource_name='Instance', config=config,
                                                       status='Success')
        except Exception:
            redirect = reverse('horizon:instances:instances:index')
            exceptions.handle(request, _("Unable to rebuild instance."),
                              redirect=redirect)

            # operation log
            config = _('Instance ID: %s') %instance
            api.logger.Logger(request).create(resource_type='instance', action_name='Rebuild Instance',
                                                       resource_name='Instance', config=config,
                                                       status='Error')
        return True
示例#17
0
class FirewallForm(forms.SelfHandlingForm):
    #required_css_class = 'required form-float'
    required_css_class = 'form-horizontal'

    gateway_ip = forms.ChoiceField(
        label=_("Select Gateway"),
        widget=forms.SelectWidget(attrs={'style': 'width: 500px'}))
    gateway_port = forms.IntegerField(
        label=_("Gateway Port (10000-65000"),
        validators=[limit_port_range],
        widget=forms.TextInput(attrs={'style': 'width: 510px'}))
    service_port = forms.IntegerField(
        label=_("Service Port"),
        min_value=1,
        validators=[validators.validate_port_range],
        widget=forms.TextInput(attrs={'style': 'width: 510px'}))

    def __init__(self, request, *args, **kwargs):
        super(FirewallForm, self).__init__(request, *args, **kwargs)
        choices = []
        tmp_choices = []

        gateways = kwargs['initial']['gateways']
        for gateway in gateways:
            item = (gateway.hostname, gateway.vext_ip)
            if gateway.int_dev != gateway.ext_dev or \
                    gateway.vext_ip != gateway.ext_ip:
                choices.append(item)
            else:
                tmp_choices.append(item)

        if not choices:
            choices = tmp_choices

        if choices:
            choices.insert(0, ("", _("Select Gateway")))
        else:
            choices.insert(0, ("", _("No Gateway available")))
        self.fields['gateway_ip'].choices = choices

    def clean(self):
        data = super(forms.Form, self).clean()
        hostname = data.get("gateway_ip")
        gateway_port = data.get("gateway_port")
        service_port = data.get("service_port")
        if hostname and gateway_port and service_port:
            instance = self.initial['instance']
            try:
                api.daolicloud.firewall_exist(self.request,
                                              instance.id,
                                              hostname=hostname,
                                              gateway_port=gateway_port)
            except Exception as e:
                #msg = _("This gateway port already be used.")
                #raise forms.ValidationError(msg)
                raise forms.ValidationError(e.message)

        return data

    def handle(self, request, data):
        hostname = data['gateway_ip']
        gport = data['gateway_port']
        sport = data['service_port']
        instance = self.initial['instance']
        try:
            firewall = api.daolicloud.firewall_create(request, instance.id,
                                                      hostname, gport, sport)
            message = _('Create gateway %(gport)s, service %(sport)s to '
                        ' instance %(inst)s.') % {
                            "gport": gport,
                            "sport": sport,
                            "inst": instance.name
                        }
            messages.success(request, message)
            return firewall
        except Exception:
            redirect = reverse("horizon:project:edges:index")
            exceptions.handle(request,
                              _('Unable to add firewall.'),
                              redirect=redirect)
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255,
                           label=_("Volume Name"),
                           required=False)
    description = forms.CharField(max_length=255,
                                  widget=forms.Textarea(attrs={'rows': 4}),
                                  label=_("Description"),
                                  required=False)
    volume_source_type = forms.ChoiceField(
        label=_("Volume Source Type"),
        required=False,
        widget=forms.SelectWidget(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=('name', 'id'),
                                  transform=lambda x: "%s (%s GiB)" %
                                  (x.name, x.id)),
        required=False)
    checkpoint_source = forms.ChoiceField(
        label=_("Use checkpoint as a source"),
        widget=forms.SelectWidget(attrs={'class': 'snapshot-selector'},
                                  data_attrs=('name', 'id'),
                                  transform=lambda x: "%s (%s GiB)" %
                                  (x.name, x.id)),
        required=False)
    type = forms.ChoiceField(label=_("Type"),
                             required=False,
                             widget=forms.SelectWidget(
                                 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 (GiB)"))
    availability_zone = forms.ChoiceField(
        label=_("Availability Zone"),
        required=False,
        widget=forms.SelectWidget(
            attrs={
                'class': 'switched',
                'data-switch-on': 'source',
                'data-source-no_source_type': _('Availability Zone'),
                'data-source-image_source': _('Availability Zone')
            }))

    def prepare_source_fields_if_checkpoint_specified(self, request):
        try:
            checkpoint = self.get_checkpoint(request,
                                             request.GET["checkpoint_id"])
            replication = self.get_replication(request,
                                               checkpoint.replication_id)
            master_volume = self.get_volume(request, replication.master_volume)
            master_az = master_volume.availability_zone
            slave_volume = self.get_volume(request, replication.slave_volume)
            slave_az = slave_volume.availability_zone
            self.fields['name'].initial = checkpoint.name
            self.fields['size'].initial = master_volume.size
            self.fields['checkpoint_source'].choices = ((checkpoint.id,
                                                         checkpoint), )
            self.fields['availability_zone'].choices = \
                [(master_az, master_az), (slave_az, slave_az)]
            try:
                # Set the volume type from the original volume
                orig_volume = cinder.volume_get(request,
                                                replication.master_volume)
                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 '
                  'master_volume size (%sGiB)') % master_volume.size)
            del self.fields['volume_source_type']
            del self.fields['snapshot_source']

        except Exception:
            exceptions.handle(request,
                              _('Unable to load the specified snapshot.'))

    def prepare_source_fields_if_snapshot_specified(self, request):
        try:
            snapshot = self.get_snapshot(request, request.GET["snapshot_id"])
            self.fields['name'].initial = snapshot.name
            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
                self.fields['size'].initial = orig_volume.size
            except Exception:
                pass
            self.fields['size'].help_text = (
                _('Volume size must be equal to or greater than the '
                  'snapshot size (%sGiB)') % orig_volume.size)
            self.fields['type'].widget = forms.widgets.HiddenInput()
            del self.fields['volume_source_type']
            del self.fields['availability_zone']
            del self.fields['checkpoint_source']

        except Exception:
            exceptions.handle(request,
                              _('Unable to load the specified snapshot.'))

    def prepare_source_fields_default(self, request):
        source_type_choices = []
        self.fields['availability_zone'].choices = \
            availability_zones(request)

        try:
            available = sg_api.VOLUME_STATE_AVAILABLE
            snapshots = sg_api.volume_snapshot_list(
                request, search_opts=dict(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."))

        try:
            checkpoints = sg_api.volume_checkpoint_list(
                request, search_opts=dict(status=available))
            if checkpoints:
                source_type_choices.append(
                    ("checkpoint_source", _("Checkpoint")))
                choices = [('', _("Choose a checkpoint"))] + \
                          [(s.id, s) for s in checkpoints]
                self.fields['checkpoint_source'].choices = choices
            else:
                del self.fields['checkpoint_source']
        except Exception:
            exceptions.handle(request,
                              _("Unable to retrieve volume checkpoints."))

        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 __init__(self, request, *args, **kwargs):
        super(CreateForm, self).__init__(request, *args, **kwargs)
        volume_types = []
        try:
            volume_types = cinder.volume_type_list(request)
        except Exception:
            redirect_url = reverse("horizon:storage-gateway:volumes:index")
            error_message = _('Unable to retrieve the volume type list.')
            exceptions.handle(request, error_message, redirect=redirect_url)
        self.fields['type'].choices = [("", _("No volume type"))] + \
                                      [(type.name, type.name)
                                       for type in volume_types]
        if 'initial' in kwargs and 'type' in kwargs['initial']:
            # if there is a default volume type to select, then remove
            # the first ""No volume type" entry
            self.fields['type'].choices.pop(0)

        if "snapshot_id" in request.GET:
            self.prepare_source_fields_if_snapshot_specified(request)
        elif 'checkpoint_id' in request.GET:
            self.prepare_source_fields_if_checkpoint_specified(request)
        else:
            self.prepare_source_fields_default(request)

    def clean(self):
        cleaned_data = super(CreateForm, self).clean()
        source_type = self.cleaned_data.get('volume_source_type')
        if (source_type == 'checkpoint_source'
                and not cleaned_data.get('checkpoint_source')):
            msg = _('Checkpoint source must be specified')
            self._errors['checkpoint_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])
        return cleaned_data

    def get_volumes(self, request):
        volumes = []
        try:
            available = sg_api.VOLUME_STATE_ENABLED
            volumes = sg_api.volume_list(self.request,
                                         search_opts=dict(status=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
            checkpoint_id = None
            source_type = data.get('volume_source_type', None)
            az = data.get('availability_zone', None) or None
            volume_type = data.get('type')

            if (data.get("snapshot_source", None)
                    and source_type in ['', None, 'snapshot_source']):
                # Create from Snapshot
                snapshot = self.get_snapshot(request, data["snapshot_source"])
                orig_volume = cinder.volume_get(request, snapshot.volume_id)
                snapshot_id = snapshot.id
                if data['size'] < orig_volume.size:
                    error_message = (_('The volume size cannot be less than '
                                       'the snapshot size (%sGiB)') %
                                     orig_volume.size)
                    raise ValidationError(error_message)
                az = None
                volume_type = ""
            if (data.get("checkpoint_source", None)
                    and source_type in ['', None, 'checkpoint_source']):
                # Create from Checkpoint
                checkpoint = self.get_checkpoint(request,
                                                 data["checkpoint_source"])
                checkpoint_id = checkpoint.id
                check_size = self.fields['size'].initial
                if data['size'] < check_size:
                    error_message = (_('The volume size cannot be less than '
                                       'the master_volume size (%sGiB)') %
                                     check_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)iGiB cannot be created '
                                  'as you only have %(avail)iGiB 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)

            volume = sg_api.volume_create(request,
                                          size=data['size'],
                                          name=data['name'],
                                          description=data['description'],
                                          volume_type=volume_type,
                                          snapshot_id=snapshot_id,
                                          availability_zone=az,
                                          checkpoint_id=checkpoint_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:
            redirect = reverse("horizon:storage-gateway:volumes:index")
            exceptions.handle(request,
                              _("Unable to create volume."),
                              redirect=redirect)

    @memoized
    def get_snapshot(self, request, id):
        return sg_api.volume_snapshot_get(request, id)

    @memoized
    def get_volume(self, request, id):
        return sg_api.volume_get(request, id)

    @memoized
    def get_checkpoint(self, request, id):
        return sg_api.volume_checkpoint_get(request, id)

    @memoized
    def get_replication(self, request, id):
        return sg_api.volume_replication_get(request, id)
示例#19
0
    def __init__(self, request, *args, **kwargs):
        super(CreateShareGroupForm, self).__init__(request, *args, **kwargs)
        self.st_field_name_prefix = "share-type-choices-"
        self.fields["source_type"] = forms.ChoiceField(
            label=_("Source Type"),
            widget=forms.Select(attrs={
                "class": "switchable",
                "data-slug": "source",
            }),
            required=False)

        self.fields["snapshot"] = forms.ChoiceField(
            label=_("Use share group snapshot as a source"),
            widget=forms.SelectWidget(
                attrs={
                    "class": "switched",
                    "data-switch-on": "source",
                    "data-source-snapshot": _("Share Group Snapshot"),
                }),
            required=True)

        if ("snapshot_id" in request.GET
                or kwargs.get("data", {}).get("snapshot")):
            try:
                snapshot = self.get_share_group_snapshot(
                    request,
                    request.GET.get("snapshot_id",
                                    kwargs.get("data", {}).get("snapshot")))
                self.fields["name"].initial = snapshot.name
                self.fields["snapshot"].choices = ((snapshot.id, snapshot.name
                                                    or snapshot.id), )
                try:
                    # Set the share group type from the original share group
                    orig_sg = manila.share_group_get(request,
                                                     snapshot.share_group_id)
                    self.fields["sgt"].initial = orig_sg.share_group_type_id
                except Exception:
                    pass
                del self.fields["source_type"]
            except Exception:
                exceptions.handle(
                    request,
                    _("Unable to load the specified share group snapshot."))
        else:
            source_type_choices = []
            try:
                snapshot_list = manila.share_group_snapshot_list(request)
                snapshots = [
                    s for s in snapshot_list if s.status == "available"
                ]
                if snapshots:
                    source_type_choices.append(("snapshot", _("Snapshot")))
                    self.fields["snapshot"].choices = (
                        [("", _("Choose a snapshot"))] +
                        [(s.id, s.name or s.id) for s in snapshots])
                else:
                    del self.fields["snapshot"]
            except Exception:
                exceptions.handle(
                    request, _("Unable to retrieve share group snapshots."))

            if source_type_choices:
                choices = ([('none', _("No source, empty share group"))] +
                           source_type_choices)
                self.fields["source_type"].choices = choices
            else:
                del self.fields["source_type"]

            self.fields["az"] = forms.ChoiceField(
                label=_("Availability Zone"),
                widget=forms.SelectWidget(
                    attrs={
                        "class": "switched",
                        "data-switch-on": "source",
                        "data-source-none": _("Availability Zone"),
                    }),
                required=False)

            availability_zones = manila.availability_zone_list(request)
            self.fields["az"].choices = ([("", "")] +
                                         [(az.name, az.name)
                                          for az in availability_zones])

            share_group_types = manila.share_group_type_list(request)
            self.fields["sgt"] = forms.ChoiceField(
                label=_("Share Group Type"),
                widget=forms.fields.SelectWidget(
                    attrs={
                        "class": "switched switchable",
                        "data-switch-on": "source",
                        "data-source-none": _("Share Group Type"),
                        "data-slug": "sgt",
                    }),
                required=True)
            self.fields["sgt"].choices = (
                [("", "")] + [(utils.transform_dashed_name(sgt.id), sgt.name)
                              for sgt in share_group_types])

            # NOTE(vponomaryov): create separate set of available share types
            # for each of share group types.
            share_types = manila.share_type_list(request)
            for sgt in share_group_types:
                st_choices = ([(st.id, st.name) for st in share_types
                               if st.id in sgt.share_types])
                amount_of_choices = len(st_choices)
                st_field_name = (self.st_field_name_prefix +
                                 utils.transform_dashed_name(sgt.id))
                if amount_of_choices < 2:
                    st_field = forms.ChoiceField(
                        label=_("Share Types"),
                        choices=st_choices,
                        widget=forms.fields.SelectWidget(
                            attrs={
                                "class":
                                "switched",
                                "data-switch-on":
                                "sgt",
                                "data-sgt-%s" % utils.transform_dashed_name(sgt.id):
                                _("Share Types (one available)"),
                            }),
                        required=True)
                else:
                    height = min(30 * amount_of_choices, 155)
                    st_field = forms.MultipleChoiceField(
                        label=_("Share Types"),
                        choices=st_choices,
                        widget=forms.fields.widgets.SelectMultiple(
                            attrs={
                                "style":
                                "max-height: %spx;" % height,
                                "class":
                                "switched",
                                "data-switch-on":
                                "sgt",
                                "data-sgt-%s" % utils.transform_dashed_name(sgt.id):
                                _("Share Types (multiple available)"),
                            }),
                        required=False)
                    st_field.initial = st_choices[0]
                self.fields[st_field_name] = st_field

            self.fields["share_network"] = forms.ChoiceField(
                label=_("Share Network"),
                widget=forms.fields.SelectWidget(
                    attrs={
                        "class": "switched",
                        "data-switch-on": "source",
                        "data-source-none": _("Share Network"),
                    }),
                required=False)
            share_networks = manila.share_network_list(request)
            self.fields["share_network"].choices = ([("", "")] +
                                                    [(sn.id, sn.name or sn.id)
                                                     for sn in share_networks])
示例#20
0
class RebuildInstanceForm(forms.SelfHandlingForm):
    instance_id = forms.CharField(widget=forms.HiddenInput())

    image = forms.ChoiceField(label=_("Select Image"),
                              widget=forms.SelectWidget(
                                  attrs={'class': 'image-selector'},
                                  data_attrs=('size', 'display-name'),
                                  transform=_image_choice_title))
    password = forms.RegexField(
        label=_("Rebuild Password"),
        required=False,
        widget=forms.PasswordInput(render_value=False),
        regex=validators.password_validator(),
        error_messages={'invalid': validators.password_validator_msg()})
    confirm_password = forms.CharField(
        label=_("Confirm Rebuild Password"),
        required=False,
        widget=forms.PasswordInput(render_value=False))
    disk_config = forms.ChoiceField(label=_("Disk Partition"), required=False)

    def __init__(self, request, *args, **kwargs):
        super(RebuildInstanceForm, self).__init__(request, *args, **kwargs)
        instance_id = kwargs.get('initial', {}).get('instance_id')
        self.fields['instance_id'].initial = instance_id

        images = image_utils.get_available_images(request,
                                                  request.user.tenant_id)
        choices = [(image.id, image) for image in images]
        if choices:
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        self.fields['image'].choices = choices

        if not api.nova.can_set_server_password():
            del self.fields['password']
            del self.fields['confirm_password']

        try:
            if not api.nova.extension_supported("DiskConfig", request):
                del self.fields['disk_config']
            else:
                # Set our disk_config choices
                config_choices = [("AUTO", _("Automatic")),
                                  ("MANUAL", _("Manual"))]
                self.fields['disk_config'].choices = config_choices
        except Exception:
            exceptions.handle(
                request, _('Unable to retrieve extensions '
                           'information.'))

    def clean(self):
        cleaned_data = super(RebuildInstanceForm, self).clean()
        if 'password' in cleaned_data:
            passwd = cleaned_data.get('password')
            confirm = cleaned_data.get('confirm_password')
            if passwd is not None and confirm is not None:
                if passwd != confirm:
                    raise forms.ValidationError(_("Passwords do not match."))
        return cleaned_data

    # We have to protect the entire "data" dict because it contains the
    # password and confirm_password strings.
    @sensitive_variables('data', 'password')
    def handle(self, request, data):
        instance = data.get('instance_id')
        image = data.get('image')
        password = data.get('password') or None
        disk_config = data.get('disk_config', None)
        try:
            api.nova.server_rebuild(request, instance, image, password,
                                    disk_config)
            messages.success(request, _('Rebuilding instance %s.') % instance)
        except Exception:
            redirect = reverse('horizon:project:instances:index')
            exceptions.handle(request,
                              _("Unable to rebuild instance."),
                              redirect=redirect)
        return True
示例#21
0
class SetInstanceDetailsAction(workflows.Action):
    flavor_vcpus = forms.CharField(widget=forms.HiddenInput, required=False)
    flavor_ram = forms.CharField(widget=forms.HiddenInput, required=False)

    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
                                          required=False)

    availability_zone_info = forms.CharField(widget=forms.HiddenInput,
                                             required=False)

    cluster_id = forms.ChoiceField(label=_("Cluster"), required=False)

    template_id = forms.ChoiceField(label=_("Template"), required=False)

    name = forms.CharField(label=_("Instance Name"), max_length=255)

    flavor = forms.ChoiceField(label=_("Flavor"),
                               help_text=_("Size of image to launch."))

    count = forms.IntegerField(label=_("Instance Count"),
                               min_value=1,
                               initial=1,
                               help_text=_("Number of instances to launch."))

    source_type = forms.ChoiceField(label=_("Instance Boot Source"),
                                    help_text=_("Choose Your Boot Source "
                                                "Type."))

    instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
                                             required=False)

    volume_id = forms.ChoiceField(label=_("Volume"), required=False)

    volume_snapshot_id = forms.ChoiceField(label=_("Volume Snapshot"),
                                           required=False)

    image_id = forms.ChoiceField(
        label=_("Image Name"),
        required=False,
        widget=forms.SelectWidget(data_attrs=('volume_size', ),
                                  transform=lambda x:
                                  ("%s (%s)" %
                                   (x.name, filesizeformat(x.bytes)))))

    volume_size = forms.IntegerField(label=_("Device size (GB)"),
                                     initial=1,
                                     min_value=0,
                                     required=False,
                                     help_text=_("Volume size in gigabytes "
                                                 "(integer value)."))

    device_name = forms.CharField(label=_("Device Name"),
                                  required=False,
                                  initial="vda",
                                  help_text=_("Volume mount point (e.g. 'vda' "
                                              "mounts at '/dev/vda')."))

    delete_on_terminate = forms.BooleanField(label=_("Delete on Terminate"),
                                             initial=False,
                                             required=False,
                                             help_text=_("Delete volume on "
                                                         "instance terminate"))

    class Meta:
        name = _("Details")
        help_text_template = ("project/applyhost/" "_launch_details_help.html")

    def __init__(self, request, context, *args, **kwargs):
        self._init_images_cache()
        self.request = request
        self.context = context
        super(SetInstanceDetailsAction, self).__init__(request, context, *args,
                                                       **kwargs)
        source_type_choices = [
            ('', _("Select source")),
            ("image_id", _("Boot from image")),
            ("instance_snapshot_id", _("Boot from snapshot")),
        ]
        if base.is_service_enabled(request, 'volume'):
            source_type_choices.append(("volume_id", _("Boot from volume")))

            try:
                if api.nova.extension_supported("BlockDeviceMappingV2Boot",
                                                request):
                    source_type_choices.append(
                        ("volume_image_id",
                         _("Boot from image (creates a new volume)")))
            except Exception:
                exceptions.handle(
                    request, _('Unable to retrieve extensions '
                               'information.'))

            source_type_choices.append(
                ("volume_snapshot_id",
                 _("Boot from volume snapshot (creates a new volume)")))
        self.fields['source_type'].choices = source_type_choices

        availability_zone_info = self.get_availability_zone_info()

        if availability_zone_info and type(availability_zone_info) == type({}):
            self.fields[
                'availability_zone_info'].initial = availability_zone_info.get(
                    'res')
            self.fields['template_id'].choices = availability_zone_info.get(
                'all_templates')
            self.fields['cluster_id'].choices = availability_zone_info.get(
                'all_clusters')

    def get_availability_zone_info(self):
        res = {}
        def_res = {}
        try:
            source_domains = api.nova.availability_zone_list(self.request)
            if not source_domains:
                return def_res
            plats = []
            try:
                request_api = RequestApi()
                plats = request_api.getRequestInfo(
                    'api/heterogeneous/platforms')
            except Exception:
                plats = []
            if not plats:
                return def_res
            all_clusters = []
            all_templates = []
            for sd in source_domains:
                for pl in plats:
                    if pl.get('domain_name') == sd.zoneName:
                        vtype = pl.get('virtualplatformtype')
                        templates = request_api.getRequestInfo(
                            'api/heterogeneous/platforms/' + vtype +
                            '/templates', {'name': pl.get('name')}) or []
                        templates = [
                            t for t in templates if t.get('name') not in
                            CSERVER_TEMPLATES_BLACK_LIST
                        ]
                        _clusters = request_api.getRequestInfo(
                            'api/heterogeneous/platforms/' + vtype +
                            '/clusters', {'name': pl.get('name')}) or []
                        all_clusters += [(c.get('id'), c.get('name'))
                                         for c in _clusters]
                        all_templates += [(c.get('id'), c.get('name'))
                                          for c in templates]

                        clusters = [{
                            'id': c.get('id'),
                            'name': c.get('name')
                        } for c in _clusters]
                        res[sd.zoneName] = {
                            'vtype': vtype,
                            'clusters': clusters,
                            'templates': templates
                        }
                        break

            return {
                'res': json.dumps(res),
                'all_clusters': all_clusters,
                'all_templates': all_templates
            }
        except Exception:
            res = _(
                'Unable to get vtype|clusters|templates info of source domain.'
            )
            exceptions.handle(self.request, ignore=True)
            return def_res

    def get_image_id(self, image_name, request, context):
        images_list = self.get_images_list(request, context)

        if images_list:
            for image in images_list:
                if image_name == getattr(image[1], 'name', ''):
                    return image[0]
        return ''

    def get_exists_name(self):
        try:
            instances, _more = api.nova.server_list(self.request,
                                                    all_tenants=True)
            exists_name = [getattr(i, 'name', '') for i in instances]
        except Exception:
            self._more = False
            exceptions.handle(self.request,
                              _('Unable to retrieve instance list.'))
            exists_name = []

        return exists_name

    def set_meta_data(self, cleaned_data):
        availability_zone_info_str = cleaned_data.get('availability_zone_info')
        if availability_zone_info_str:
            cleaned_data['metadata'] = {}
            availability_zone = cleaned_data.get('availability_zone')
            availability_zone_info = json.loads(availability_zone_info_str)
            platformtype = 'Openstack'
            if availability_zone_info and availability_zone_info.get(availability_zone) \
                    and availability_zone_info.get(availability_zone).get('vtype'):
                platformtype = availability_zone_info.get(
                    availability_zone).get('vtype')
            cleaned_data['metadata']['platformtype'] = platformtype
        return cleaned_data

    def clean(self):
        cleaned_data = super(SetInstanceDetailsAction, self).clean()
        cleaned_data = self.set_meta_data(cleaned_data)

        exists_name = self.get_exists_name()
        if cleaned_data.get('name') in exists_name:
            raise forms.ValidationError(
                _('The name is already used by another vm.'))

        flavor_vcpus = cleaned_data.get('flavor_vcpus')
        flavor_ram = cleaned_data.get('flavor_ram')

        if cleaned_data.get(
                'cluster_id') and not cleaned_data.get('template_id'):
            raise forms.ValidationError(_('Please select one template.'))

        if cleaned_data.get('template_id') and cleaned_data.get('cluster_id') \
                and cleaned_data.get('flavor_vcpus') and cleaned_data.get('flavor_ram'):

            _flavors = []
            try:
                _flavors = api.nova.flavor_list(self.request, None) or []
            except Exception as e:
                pass
            finded = False

            for f in _flavors:
                if getattr(f, 'vcpus', '0') and getattr(f, 'ram', '0'):
                    if int(flavor_vcpus) == int(
                            f.vcpus) and int(flavor_ram) == int(f.ram):
                        cleaned_data['flavor'] = getattr(f, 'id', '')
                        finded = True
            if not finded:
                new_flavor = None
                try:
                    new_flavor = api.nova.flavor_create(self.request,
                                                        name='vflavor-' +
                                                        str(uuid.uuid4()),
                                                        memory=flavor_ram,
                                                        vcpu=flavor_vcpus,
                                                        disk='10',
                                                        ephemeral='0',
                                                        swap='0',
                                                        is_public=True)
                except Exception:
                    exceptions.handle(self.request,
                                      _('Unable to create flavor.'))
                new_flavor_id = getattr(new_flavor, 'id',
                                        '') if new_flavor else ''
                cleaned_data['flavor'] = new_flavor_id

            cleaned_data['source_type'] = 'image_id'
            cleaned_data['image_id'] = self.get_image_id(
                'cserver_template_image', self.request, self.context)

        count = cleaned_data.get('count', 1)
        usages = quotas.tenant_quota_usages(self.request)
        available_count = usages['instances']['available']
        if available_count < count:
            error_message = ungettext_lazy(
                'The requested instance '
                'cannot be launched as you only '
                'have %(avail)i of your quota '
                'available. ', 'The requested %(req)i instances '
                'cannot be launched as you only '
                'have %(avail)i of your quota '
                'available.', count)
            params = {'req': count, 'avail': available_count}
            raise forms.ValidationError(error_message % params)
        try:
            flavor_id = cleaned_data.get('flavor')
            # We want to retrieve details for a given flavor,
            # however flavor_list uses a memoized decorator
            # so it is used instead of flavor_get to reduce the number
            # of API calls.
            flavors = instance_utils.flavor_list(self.request)
            flavor = [x for x in flavors if x.id == flavor_id][0]
        except IndexError:
            flavor = None

        count_error = []
        # Validate cores and ram.
        available_cores = usages['cores']['available']
        if flavor and available_cores < count * flavor.vcpus:
            count_error.append(
                _("Cores(Available: %(avail)s, "
                  "Requested: %(req)s)") % {
                      'avail': available_cores,
                      'req': count * flavor.vcpus
                  })

        available_ram = usages['ram']['available']
        if flavor and available_ram < count * flavor.ram:
            count_error.append(
                _("RAM(Available: %(avail)s, "
                  "Requested: %(req)s)") % {
                      'avail': available_ram,
                      'req': count * flavor.ram
                  })

        if count_error:
            value_str = ", ".join(count_error)
            msg = (_('The requested instance cannot be launched. '
                     'The following requested resource(s) exceed '
                     'quota(s): %s.') % value_str)
            if count == 1:
                self._errors['flavor'] = self.error_class([msg])
            else:
                self._errors['count'] = self.error_class([msg])

        # Validate our instance source.
        source_type = self.data.get('source_type', None)

        if source_type in ('image_id', 'volume_image_id'):
            if source_type == 'volume_image_id':
                volume_size = self.data.get('volume_size', None)
                if not volume_size:
                    msg = _("You must set volume size")
                    self._errors['volume_size'] = self.error_class([msg])
                if float(volume_size) <= 0:
                    msg = _("Volume size must be greater than 0")
                    self._errors['volume_size'] = self.error_class([msg])
                if not cleaned_data.get('device_name'):
                    msg = _("You must set device name")
                    self._errors['device_name'] = self.error_class([msg])
            if not cleaned_data.get('image_id'):
                msg = _("You must select an image.")
                self._errors['image_id'] = self.error_class([msg])
            else:
                # Prevents trying to launch an image needing more resources.
                try:
                    image_id = cleaned_data.get('image_id')
                    # We want to retrieve details for a given image,
                    # however get_available_images uses a cache of image list,
                    # so it is used instead of image_get to reduce the number
                    # of API calls.
                    images = image_utils.get_available_images(
                        self.request, self.context.get('project_id'),
                        self._images_cache)
                    image = [x for x in images if x.id == image_id][0]
                except IndexError:
                    image = None

                if image and flavor:
                    props_mapping = (("min_ram", "ram"), ("min_disk", "disk"))
                    for iprop, fprop in props_mapping:
                        if getattr(image, iprop) > 0 and \
                                getattr(image, iprop) > getattr(flavor, fprop):
                            msg = (_("The flavor '%(flavor)s' is too small "
                                     "for requested image.\n"
                                     "Minimum requirements: "
                                     "%(min_ram)s MB of RAM and "
                                     "%(min_disk)s GB of Root Disk.") % {
                                         'flavor': flavor.name,
                                         'min_ram': image.min_ram,
                                         'min_disk': image.min_disk
                                     })
                            self._errors['image_id'] = self.error_class([msg])
                            break  # Not necessary to continue the tests.

                    volume_size = cleaned_data.get('volume_size')
                    if volume_size and source_type == 'volume_image_id':
                        volume_size = int(volume_size)
                        img_gigs = functions.bytes_to_gigabytes(image.size)
                        smallest_size = max(img_gigs, image.min_disk)
                        if volume_size < smallest_size:
                            msg = (_("The Volume size is too small for the"
                                     " '%(image_name)s' image and has to be"
                                     " greater than or equal to "
                                     "'%(smallest_size)d' GB.") % {
                                         'image_name': image.name,
                                         'smallest_size': smallest_size
                                     })
                            self._errors['volume_size'] = self.error_class(
                                [msg])

        elif source_type == 'instance_snapshot_id':
            if not cleaned_data['instance_snapshot_id']:
                msg = _("You must select a snapshot.")
                self._errors['instance_snapshot_id'] = self.error_class([msg])

        elif source_type == 'volume_id':
            if not cleaned_data.get('volume_id'):
                msg = _("You must select a volume.")
                self._errors['volume_id'] = self.error_class([msg])
            # Prevent launching multiple instances with the same volume.
            # TODO(gabriel): is it safe to launch multiple instances with
            # a snapshot since it should be cloned to new volumes?
            if count > 1:
                msg = _('Launching multiple instances is only supported for '
                        'images and instance snapshots.')
                raise forms.ValidationError(msg)

        elif source_type == 'volume_snapshot_id':
            if not cleaned_data.get('volume_snapshot_id'):
                msg = _("You must select a snapshot.")
                self._errors['volume_snapshot_id'] = self.error_class([msg])
            if not cleaned_data.get('device_name'):
                msg = _("You must set device name")
                self._errors['device_name'] = self.error_class([msg])

        return cleaned_data

    def populate_flavor_choices(self, request, context):
        flavors = instance_utils.flavor_list(request)
        res_flavors = []
        for i in flavors:
            if getattr(i, 'name',
                       '') not in ('m1.cserver_flavor', 'm1.vcenter_flavor'):
                res_flavors.append(i)

        if res_flavors:
            return instance_utils.sort_flavor_list(request, res_flavors)
        return []

    def populate_availability_zone_choices(self, request, context):
        try:
            zones = api.nova.availability_zone_list(request)
        except Exception:
            zones = []
            exceptions.handle(request,
                              _('Unable to retrieve availability zones.'))

        zone_list = [(zone.zoneName, zone.zoneName) for zone in zones
                     if zone.zoneState['available']]
        zone_list.sort()
        if not zone_list:
            zone_list.insert(0, ("", _("No availability zones found")))
        elif len(zone_list) > 1:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        return zone_list

    def get_help_text(self, extra_context=None):
        extra = extra_context or {}
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.dumps(
                [f._info for f in instance_utils.flavor_list(self.request)])
            extra['flavors'] = flavors
            images = image_utils.get_available_images(
                self.request, self.initial['project_id'], self._images_cache)
            if images is not None:
                attrs = [{
                    'id': i.id,
                    'min_disk': getattr(i, 'min_disk', 0),
                    'min_ram': getattr(i, 'min_ram', 0)
                } for i in images]
                extra['images'] = json.dumps(attrs)

        except Exception:
            exceptions.handle(self.request,
                              _("Unable to retrieve quota information."))
        return super(SetInstanceDetailsAction, self).get_help_text(extra)

    def _init_images_cache(self):
        if not hasattr(self, '_images_cache'):
            self._images_cache = {}

    def _get_volume_display_name(self, volume):
        if hasattr(volume, "volume_id"):
            vol_type = "snap"
            visible_label = _("Snapshot")
        else:
            vol_type = "vol"
            visible_label = _("Volume")
        return (("%s:%s" % (volume.id, vol_type)),
                (_("%(name)s - %(size)s GB (%(label)s)") % {
                    'name': volume.name,
                    'size': volume.size,
                    'label': visible_label
                }))

    def get_images_list(self, request, context):
        choices = []
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        for image in images:
            image.bytes = image.size
            image.volume_size = max(image.min_disk,
                                    functions.bytes_to_gigabytes(image.bytes))
            choices.append((image.id, image))
            if context.get('image_id') == image.id and \
                    'volume_size' not in context:
                context['volume_size'] = image.volume_size
        if choices:
            choices.sort(key=lambda c: c[1].name)
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        return choices

    def populate_image_id_choices(self, request, context):
        images = self.get_images_list(request, context)
        res_images = []
        for image in images:
            if getattr(image[1], 'name', '') not in ('cserver_template_image',
                                                     'vcenter_template_image'):
                res_images.append(image)
        return res_images

    def populate_instance_snapshot_id_choices(self, request, context):
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        choices = [(image.id, image.name) for image in images
                   if image.properties.get("image_type", '') == "snapshot"]
        if choices:
            choices.sort(key=operator.itemgetter(1))
            choices.insert(0, ("", _("Select Instance Snapshot")))
        else:
            choices.insert(0, ("", _("No snapshots available")))
        return choices

    def populate_volume_id_choices(self, request, context):
        try:
            volumes = [
                self._get_volume_display_name(v)
                for v in cinder.volume_list(self.request)
                if v.status == api.cinder.VOLUME_STATE_AVAILABLE
                and v.bootable == 'true'
            ]
        except Exception:
            volumes = []
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volumes.'))
        if volumes:
            volumes.insert(0, ("", _("Select Volume")))
        else:
            volumes.insert(0, ("", _("No volumes available")))
        return volumes

    def populate_volume_snapshot_id_choices(self, request, context):
        try:
            snapshots = cinder.volume_snapshot_list(self.request)
            snapshots = [
                self._get_volume_display_name(s) for s in snapshots
                if s.status == api.cinder.VOLUME_STATE_AVAILABLE
            ]
        except Exception:
            snapshots = []
            exceptions.handle(
                self.request,
                _('Unable to retrieve list of volume '
                  'snapshots.'))
        if snapshots:
            snapshots.insert(0, ("", _("Select Volume Snapshot")))
        else:
            snapshots.insert(0, ("", _("No volume snapshots available")))
        return snapshots
class SetInstanceDetailsAction(workflows.Action):
    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
                                          required=False)

    name = forms.CharField(label=_("Instance Name"), max_length=255)

    flavor = forms.ChoiceField(label=_("Flavor"),
                               help_text=_("Size of image to launch."))

    count = forms.IntegerField(label=_("Instance Count"),
                               min_value=1,
                               initial=1,
                               help_text=_("Number of instances to launch."))

    source_type = forms.ChoiceField(label=_("Instance Boot Source"),
                                    help_text=_("Choose Your Boot Source "
                                                "Type."))

    instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
                                             required=False)

    volume_id = forms.ChoiceField(label=_("Volume"), required=False)

    volume_snapshot_id = forms.ChoiceField(label=_("Volume Snapshot"),
                                           required=False)

    image_id = forms.ChoiceField(
        label=_("Image Name"),
        required=False,
        widget=forms.SelectWidget(data_attrs=('volume_size', ),
                                  transform=lambda x:
                                  ("%s (%s)" %
                                   (x.name, filesizeformat(x.bytes)))))

    volume_size = forms.IntegerField(label=_("Device size (GB)"),
                                     initial=1,
                                     min_value=0,
                                     required=False,
                                     help_text=_("Volume size in gigabytes "
                                                 "(integer value)."))

    device_name = forms.CharField(label=_("Device Name"),
                                  required=False,
                                  initial="vda",
                                  help_text=_("Volume mount point (e.g. 'vda' "
                                              "mounts at '/dev/vda'). Leave "
                                              "this field blank to let the "
                                              "system choose a device name "
                                              "for you."))

    vol_delete_on_instance_delete = forms.BooleanField(
        label=_("Delete Volume on Instance Delete"),
        initial=False,
        required=False,
        help_text=_("Delete volume when the instance is deleted"))

    class Meta(object):
        name = _("Details")
        help_text_template = ("project/instances/" "_launch_details_help.html")

    def __init__(self, request, context, *args, **kwargs):
        self._init_images_cache()
        self.request = request
        self.context = context
        super(SetInstanceDetailsAction, self).__init__(request, context, *args,
                                                       **kwargs)

        # Hide the device field if the hypervisor doesn't support it.
        if not nova.can_set_mount_point():
            self.fields['device_name'].widget = forms.widgets.HiddenInput()

        source_type_choices = [
            ('', _("Select source")),
            ("image_id", _("Boot from image")),
            ("instance_snapshot_id", _("Boot from snapshot")),
        ]
        if cinder.is_volume_service_enabled(request):
            source_type_choices.append(("volume_id", _("Boot from volume")))

            try:
                if api.nova.extension_supported("BlockDeviceMappingV2Boot",
                                                request):
                    source_type_choices.append(
                        ("volume_image_id",
                         _("Boot from image (creates a new volume)")))
            except Exception:
                exceptions.handle(
                    request, _('Unable to retrieve extensions '
                               'information.'))

            source_type_choices.append(
                ("volume_snapshot_id",
                 _("Boot from volume snapshot (creates a new volume)")))
        self.fields['source_type'].choices = source_type_choices

    @memoized.memoized_method
    def _get_flavor(self, flavor_id):
        try:
            # We want to retrieve details for a given flavor,
            # however flavor_list uses a memoized decorator
            # so it is used instead of flavor_get to reduce the number
            # of API calls.
            flavors = instance_utils.flavor_list(self.request)
            flavor = [x for x in flavors if x.id == flavor_id][0]
        except IndexError:
            flavor = None
        return flavor

    @memoized.memoized_method
    def _get_image(self, image_id):
        try:
            # We want to retrieve details for a given image,
            # however get_available_images uses a cache of image list,
            # so it is used instead of image_get to reduce the number
            # of API calls.
            images = image_utils.get_available_images(
                self.request, self.context.get('project_id'),
                self._images_cache)
            image = [x for x in images if x.id == image_id][0]
        except IndexError:
            image = None
        return image

    def _check_quotas(self, cleaned_data):
        count = cleaned_data.get('count', 1)

        # Prevent launching more instances than the quota allows
        usages = quotas.tenant_quota_usages(self.request)
        available_count = usages['instances']['available']
        if available_count < count:
            error_message = ungettext_lazy(
                'The requested instance cannot be launched as you only '
                'have %(avail)i of your quota available. ',
                'The requested %(req)i instances cannot be launched as you '
                'only have %(avail)i of your quota available.', count)
            params = {'req': count, 'avail': available_count}
            raise forms.ValidationError(error_message % params)

        source_type = cleaned_data.get('source_type')
        if source_type in ('volume_image_id', 'volume_snapshot_id'):
            available_volume = usages['volumes']['available']
            if available_volume < count:
                msg = (_('The requested instance cannot be launched. '
                         'Requested volume exceeds quota: Available: '
                         '%(avail)s, Requested: %(req)s.') % {
                             'avail': available_volume,
                             'req': count
                         })
                raise forms.ValidationError(msg)

        flavor_id = cleaned_data.get('flavor')
        flavor = self._get_flavor(flavor_id)

        count_error = []
        # Validate cores and ram.
        available_cores = usages['cores']['available']
        if flavor and available_cores < count * flavor.vcpus:
            count_error.append(
                _("Cores(Available: %(avail)s, "
                  "Requested: %(req)s)") % {
                      'avail': available_cores,
                      'req': count * flavor.vcpus
                  })

        available_ram = usages['ram']['available']
        if flavor and available_ram < count * flavor.ram:
            count_error.append(
                _("RAM(Available: %(avail)s, "
                  "Requested: %(req)s)") % {
                      'avail': available_ram,
                      'req': count * flavor.ram
                  })

        if count_error:
            value_str = ", ".join(count_error)
            msg = (_('The requested instance cannot be launched. '
                     'The following requested resource(s) exceed '
                     'quota(s): %s.') % value_str)
            if count == 1:
                self._errors['flavor'] = self.error_class([msg])
            else:
                self._errors['count'] = self.error_class([msg])

    def _check_flavor_for_image(self, cleaned_data):
        # Prevents trying to launch an image needing more resources.
        image_id = cleaned_data.get('image_id')
        image = self._get_image(image_id)
        flavor_id = cleaned_data.get('flavor')
        flavor = self._get_flavor(flavor_id)
        if not image or not flavor:
            return
        props_mapping = (("min_ram", "ram"), ("min_disk", "disk"))
        for iprop, fprop in props_mapping:
            if getattr(image, iprop) > 0 and \
                    getattr(image, iprop) > getattr(flavor, fprop):
                msg = (_("The flavor '%(flavor)s' is too small "
                         "for requested image.\n"
                         "Minimum requirements: "
                         "%(min_ram)s MB of RAM and "
                         "%(min_disk)s GB of Root Disk.") % {
                             'flavor': flavor.name,
                             'min_ram': image.min_ram,
                             'min_disk': image.min_disk
                         })
                self._errors['image_id'] = self.error_class([msg])
                break  # Not necessary to continue the tests.

    def _check_volume_for_image(self, cleaned_data):
        image_id = cleaned_data.get('image_id')
        image = self._get_image(image_id)
        volume_size = cleaned_data.get('volume_size')
        if not image or not volume_size:
            return
        volume_size = int(volume_size)
        img_gigs = functions.bytes_to_gigabytes(image.size)
        smallest_size = max(img_gigs, image.min_disk)
        if volume_size < smallest_size:
            msg = (_("The Volume size is too small for the"
                     " '%(image_name)s' image and has to be"
                     " greater than or equal to "
                     "'%(smallest_size)d' GB.") % {
                         'image_name': image.name,
                         'smallest_size': smallest_size
                     })
            self._errors['volume_size'] = self.error_class([msg])

    def _check_source_image(self, cleaned_data):
        if not cleaned_data.get('image_id'):
            msg = _("You must select an image.")
            self._errors['image_id'] = self.error_class([msg])
        else:
            self._check_flavor_for_image(cleaned_data)

    def _check_source_volume_image(self, cleaned_data):
        volume_size = self.data.get('volume_size', None)
        if not volume_size:
            msg = _("You must set volume size")
            self._errors['volume_size'] = self.error_class([msg])
        if float(volume_size) <= 0:
            msg = _("Volume size must be greater than 0")
            self._errors['volume_size'] = self.error_class([msg])
        if not cleaned_data.get('image_id'):
            msg = _("You must select an image.")
            self._errors['image_id'] = self.error_class([msg])
            return
        else:
            self._check_flavor_for_image(cleaned_data)
            self._check_volume_for_image(cleaned_data)

    def _check_source_instance_snapshot(self, cleaned_data):
        # using the array form of get blows up with KeyError
        # if instance_snapshot_id is nil
        if not cleaned_data.get('instance_snapshot_id'):
            msg = _("You must select a snapshot.")
            self._errors['instance_snapshot_id'] = self.error_class([msg])

    def _check_source_volume(self, cleaned_data):
        if not cleaned_data.get('volume_id'):
            msg = _("You must select a volume.")
            self._errors['volume_id'] = self.error_class([msg])
        # Prevent launching multiple instances with the same volume.
        # TODO(gabriel): is it safe to launch multiple instances with
        # a snapshot since it should be cloned to new volumes?
        count = cleaned_data.get('count', 1)
        if count > 1:
            msg = _('Launching multiple instances is only supported for '
                    'images and instance snapshots.')
            raise forms.ValidationError(msg)

    def _check_source_volume_snapshot(self, cleaned_data):
        if not cleaned_data.get('volume_snapshot_id'):
            msg = _("You must select a snapshot.")
            self._errors['volume_snapshot_id'] = self.error_class([msg])

    def _check_source(self, cleaned_data):
        # Validate our instance source.
        source_type = self.data.get('source_type', None)
        source_check_methods = {
            'image_id': self._check_source_image,
            'volume_image_id': self._check_source_volume_image,
            'instance_snapshot_id': self._check_source_instance_snapshot,
            'volume_id': self._check_source_volume,
            'volume_snapshot_id': self._check_source_volume_snapshot
        }
        check_method = source_check_methods.get(source_type)
        if check_method:
            check_method(cleaned_data)

    def clean(self):
        cleaned_data = super(SetInstanceDetailsAction, self).clean()

        self._check_quotas(cleaned_data)
        self._check_source(cleaned_data)

        return cleaned_data

    def populate_flavor_choices(self, request, context):
        return instance_utils.flavor_field_data(request, False)

    def populate_availability_zone_choices(self, request, context):
        try:
            zones = api.nova.availability_zone_list(request)
        except Exception:
            zones = []
            exceptions.handle(request,
                              _('Unable to retrieve availability zones.'))

        zone_list = [(zone.zoneName, zone.zoneName) for zone in zones
                     if zone.zoneState['available']]
        zone_list.sort()
        if not zone_list:
            zone_list.insert(0, ("", _("No availability zones found")))
        elif len(zone_list) > 1:
            zone_list.insert(0, ("", _("Any Availability Zone")))
        return zone_list

    def get_help_text(self, extra_context=None):
        extra = {} if extra_context is None else dict(extra_context)
        try:
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
            extra['usages_json'] = json.dumps(extra['usages'])
            flavors = json.dumps(
                [f._info for f in instance_utils.flavor_list(self.request)])
            extra['flavors'] = flavors
            images = image_utils.get_available_images(
                self.request, self.initial['project_id'], self._images_cache)
            if images is not None:
                attrs = [{
                    'id': i.id,
                    'min_disk': getattr(i, 'min_disk', 0),
                    'min_ram': getattr(i, 'min_ram', 0),
                    'size': functions.bytes_to_gigabytes(i.size)
                } for i in images]
                extra['images'] = json.dumps(attrs)

        except Exception:
            exceptions.handle(self.request,
                              _("Unable to retrieve quota information."))
        return super(SetInstanceDetailsAction, self).get_help_text(extra)

    def _init_images_cache(self):
        if not hasattr(self, '_images_cache'):
            self._images_cache = {}

    def _get_volume_display_name(self, volume):
        if hasattr(volume, "volume_id"):
            vol_type = "snap"
            visible_label = _("Snapshot")
        else:
            vol_type = "vol"
            visible_label = _("Volume")
        return (("%s:%s" % (volume.id, vol_type)),
                (_("%(name)s - %(size)s GB (%(label)s)") % {
                    'name': volume.name,
                    'size': volume.size,
                    'label': visible_label
                }))

    def populate_image_id_choices(self, request, context):
        choices = []
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        for image in images:
            image.bytes = getattr(image, 'virtual_size', None) or image.size
            image.volume_size = max(image.min_disk,
                                    functions.bytes_to_gigabytes(image.bytes))
            choices.append((image.id, image))
            if context.get('image_id') == image.id and \
                    'volume_size' not in context:
                context['volume_size'] = image.volume_size
        if choices:
            choices.sort(key=lambda c: c[1].name or '')
            choices.insert(0, ("", _("Select Image")))
        else:
            choices.insert(0, ("", _("No images available")))
        return choices

    def populate_instance_snapshot_id_choices(self, request, context):
        images = image_utils.get_available_images(request,
                                                  context.get('project_id'),
                                                  self._images_cache)
        choices = [(image.id, image.name) for image in images
                   if image.properties.get("image_type", '') == "snapshot"]
        if choices:
            choices.sort(key=operator.itemgetter(1))
            choices.insert(0, ("", _("Select Instance Snapshot")))
        else:
            choices.insert(0, ("", _("No snapshots available")))
        return choices

    def populate_volume_id_choices(self, request, context):
        volumes = []
        try:
            if cinder.is_volume_service_enabled(request):
                available = api.cinder.VOLUME_STATE_AVAILABLE
                volumes = [
                    self._get_volume_display_name(v)
                    for v in cinder.volume_list(
                        self.request,
                        search_opts=dict(status=available, bootable=True))
                ]
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve list of volumes.'))
        if volumes:
            volumes.insert(0, ("", _("Select Volume")))
        else:
            volumes.insert(0, ("", _("No volumes available")))
        return volumes

    def populate_volume_snapshot_id_choices(self, request, context):
        snapshots = []
        try:
            if cinder.is_volume_service_enabled(request):
                available = api.cinder.VOLUME_STATE_AVAILABLE
                snapshots = [
                    self._get_volume_display_name(s)
                    for s in cinder.volume_snapshot_list(
                        self.request, search_opts=dict(status=available))
                ]
        except Exception:
            exceptions.handle(
                self.request,
                _('Unable to retrieve list of volume '
                  'snapshots.'))
        if snapshots:
            snapshots.insert(0, ("", _("Select Volume Snapshot")))
        else:
            snapshots.insert(0, ("", _("No volume snapshots available")))
        return snapshots
示例#23
0
class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255,
                           label=_("Replication Name"),
                           required=False)
    description = forms.CharField(max_length=255,
                                  widget=forms.Textarea(attrs={'rows': 4}),
                                  label=_("Description"),
                                  required=False)
    master_volume = forms.ChoiceField(
        label=_("Master Volume"),
        widget=forms.SelectWidget(attrs={'class': 'image-selector'},
                                  data_attrs=('id', 'name'),
                                  transform=lambda x: "%s (%s)" %
                                  (x.name, x.id)))
    slave_volume = forms.ChoiceField(
        label=_("Slave Volume"),
        widget=forms.SelectWidget(attrs={'class': 'image-selector'},
                                  data_attrs=('id', 'name'),
                                  transform=lambda x: "%s (%s)" %
                                  (x.name, x.id)))

    def prepare_fields_default(self, request):
        try:
            volumes = self.get_volumes(request)
            choices = [(s.id, s) for s in volumes]
            self.fields['master_volume'].choices = choices
            self.fields['slave_volume'].choices = choices
        except Exception:
            exceptions.handle(request, _("Unable to retrieve volumes."))

    def __init__(self, request, *args, **kwargs):
        super(CreateForm, self).__init__(request, *args, **kwargs)
        self.prepare_fields_default(request)

    def clean(self):
        cleaned_data = super(CreateForm, self).clean()
        if not cleaned_data.get('master_volume'):
            msg = _('Replication master_volume must be specified')
            self._errors['master_volume'] = self.error_class([msg])
        if not cleaned_data.get('slave_volume'):
            msg = _('Replication slave_volume must be specified')
            self._errors['slave_volume'] = self.error_class([msg])
        return cleaned_data

    def get_volumes(self, request):
        volumes = []
        try:
            enabled = sg_api.VOLUME_STATE_ENABLED
            for vol in sg_api.volume_list(self.request,
                                          search_opts=dict(status=enabled)):
                if vol.replicate_status in ['deleted', 'disabled', None]:
                    volumes.append(vol)
        except Exception:
            exceptions.handle(request,
                              _('Unable to retrieve list of volumes.'))
        return volumes

    def handle(self, request, data):
        try:
            name = data.get("name", None)
            description = data.get("description", None)
            master_id = data.get('master_volume', None)
            slave_id = data.get('slave_volume', None)
            master_vol = sg_api.volume_get(request, master_id)
            slave_vol = sg_api.volume_get(request, slave_id)

            if master_id == slave_id:
                error_message = (_('The slave volume and master volume can not'
                                   ' be the same'))
                raise ValidationError(error_message)
            if master_vol.availability_zone == slave_vol.availability_zone:
                error_message = (_('The slave volume and master volume can not'
                                   ' be the same availability_zone'))
                raise ValidationError(error_message)

            replication = sg_api.volume_replication_create(
                request, master_id, slave_id, name, description)
            message = _('Creating replication "%s"') % data['name']
            messages.info(request, message)
            return replication
        except ValidationError as e:
            self.api_error(e.messages[0])
            return False
        except Exception:
            redirect = reverse("horizon:storage-gateway:replications:index")
            exceptions.handle(request,
                              _("Unable to create replication."),
                              redirect=redirect)

    @memoized
    def get_volume(self, request, id):
        return sg_api.volume_get(request, id)