6dcc671543
Separating tabs of single panel to separate panels. It will reduce amount of redundant calls, because it is very unlikely that someone will need all tabs at once. Also, it is current approach that is used by core projects. Change-Id: I874253e0e9a35ede8239bc1bdf0a330a44ade413 Partially-Implements BluePrint create-share-panel-group
311 lines
13 KiB
Python
311 lines
13 KiB
Python
# Copyright (c) 2014 NetApp, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from django.conf import settings
|
|
from django.core.urlresolvers import reverse
|
|
from django.forms import ValidationError # noqa
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from horizon import exceptions
|
|
from horizon import forms
|
|
from horizon import messages
|
|
from oslo_utils import strutils
|
|
import six
|
|
|
|
from manila_ui.api import manila
|
|
from manila_ui.dashboards import utils
|
|
|
|
|
|
def _get_id_if_name_empty(data):
|
|
result = data.get('name', None)
|
|
if not result:
|
|
result = data.get('id')
|
|
if not result:
|
|
result = ''
|
|
return result
|
|
|
|
|
|
class MigrationStart(forms.SelfHandlingForm):
|
|
name = forms.CharField(
|
|
label=_("Share Name"),
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
share_id = forms.CharField(
|
|
label=_("ID"),
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
host = forms.ChoiceField(
|
|
label=_("Host to migrate share"),
|
|
help_text=_("Destination host and pool where share will be migrated "
|
|
"to."))
|
|
force_host_assisted_migration = forms.BooleanField(
|
|
label=_("Force Host Assisted Migration"),
|
|
required=False, initial=False,
|
|
help_text=_("Enforces the use of the host-assisted migration approach,"
|
|
" which bypasses driver optimizations."))
|
|
nondisruptive = forms.BooleanField(
|
|
label=_("Nondisruptive"),
|
|
required=False, initial=True,
|
|
help_text=_("Enforces migration to be nondisruptive. If set to True, "
|
|
"host-assisted migration will not be attempted."))
|
|
writable = forms.BooleanField(
|
|
label=_("Writable"), required=False, initial=True,
|
|
help_text=_("Enforces migration to keep the share writable while "
|
|
"contents are being moved. If set to True, host-assisted "
|
|
"migration will not be attempted."))
|
|
preserve_metadata = forms.BooleanField(
|
|
label=_("Preserve Metadata"), required=False, initial=True,
|
|
help_text=_("Enforces migration to preserve all file metadata when "
|
|
"moving its contents. If set to True, host-assisted "
|
|
"migration will not be attempted."))
|
|
preserve_snapshots = forms.BooleanField(
|
|
label=_("Preserve Snapshots"), required=False, initial=True,
|
|
help_text=_("Enforces migration of the share snapshots to the "
|
|
"destination. If set to True, host-assisted migration will"
|
|
" not be attempted."))
|
|
new_share_network = forms.ChoiceField(
|
|
label=_("New share network to be set in migrated share"),
|
|
required=False,
|
|
help_text=_('Specify the new share network for the share. Do not '
|
|
'specify this parameter if the migrating share has to be '
|
|
'retained within its current share network.'))
|
|
new_share_type = forms.ChoiceField(
|
|
label=_("New share type to be set in migrating share"), required=False,
|
|
help_text=_('Specify the new share type for the share. Do not specify '
|
|
'this parameter if the migrating share has to be retained '
|
|
'with its current share type.'))
|
|
|
|
def __init__(self, request, *args, **kwargs):
|
|
super(MigrationStart, self).__init__(request, *args, **kwargs)
|
|
share_networks = manila.share_network_list(request)
|
|
share_types = manila.share_type_list(request)
|
|
dests = manila.pool_list(request)
|
|
dest_choices = [('', '')] + [(d.name, d.name) for d in dests]
|
|
st_choices = [('', '')] + [(st.id, st.name) for st in share_types]
|
|
sn_choices = (
|
|
[('', '')] +
|
|
[(sn.id, sn.name or sn.id) for sn in share_networks])
|
|
self.fields['host'].choices = dest_choices
|
|
self.fields['new_share_type'].choices = st_choices
|
|
self.fields['new_share_network'].choices = sn_choices
|
|
|
|
def handle(self, request, data):
|
|
share_name = _get_id_if_name_empty(data)
|
|
try:
|
|
manila.migration_start(
|
|
request, self.initial['share_id'],
|
|
force_host_assisted_migration=(
|
|
data['force_host_assisted_migration']),
|
|
writable=data['writable'],
|
|
preserve_metadata=data['preserve_metadata'],
|
|
preserve_snapshots=data['preserve_snapshots'],
|
|
nondisruptive=data['nondisruptive'],
|
|
dest_host=data['host'],
|
|
new_share_network_id=data['new_share_network'],
|
|
new_share_type_id=data['new_share_type'])
|
|
|
|
messages.success(
|
|
request,
|
|
_('Successfully sent the request to migrate share: %s.')
|
|
% share_name)
|
|
return True
|
|
except Exception:
|
|
redirect = reverse("horizon:admin:shares:index")
|
|
exceptions.handle(
|
|
request, _("Unable to migrate share %s.") % share_name,
|
|
redirect=redirect)
|
|
return False
|
|
|
|
|
|
class MigrationForms(forms.SelfHandlingForm):
|
|
name = forms.CharField(
|
|
label=_("Share Name"), required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
share_id = forms.CharField(
|
|
label=_("ID"), required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
|
|
|
|
class MigrationComplete(MigrationForms):
|
|
|
|
def handle(self, request, data):
|
|
share_name = _get_id_if_name_empty(data)
|
|
try:
|
|
manila.migration_complete(request, self.initial['share_id'])
|
|
messages.success(
|
|
request,
|
|
_('Successfully sent the request to complete migration of '
|
|
' share: %s.') % share_name)
|
|
return True
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to complete migration "
|
|
"of share %s.") % share_name)
|
|
return False
|
|
|
|
|
|
class MigrationGetProgress(MigrationForms):
|
|
|
|
def handle(self, request, data):
|
|
share_name = _get_id_if_name_empty(data)
|
|
try:
|
|
result = manila.migration_get_progress(request,
|
|
self.initial['share_id'])
|
|
progress = result[1]
|
|
messages.success(
|
|
request,
|
|
_('Migration of share %(name)s is at %(progress)s percent.') %
|
|
{'name': share_name, 'progress': progress['total_progress']})
|
|
return True
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to obtain progress of "
|
|
"migration of share %s at this "
|
|
"moment.") % share_name)
|
|
return False
|
|
|
|
|
|
class MigrationCancel(MigrationForms):
|
|
|
|
def handle(self, request, data):
|
|
share_name = _get_id_if_name_empty(data)
|
|
try:
|
|
manila.migration_cancel(request, self.initial['share_id'])
|
|
messages.success(
|
|
request,
|
|
_('Successfully sent the request to cancel migration of '
|
|
' share: %s.') % share_name)
|
|
return True
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to cancel migration of share"
|
|
" %s at this moment.") % share_name)
|
|
return False
|
|
|
|
|
|
class ManageShare(forms.SelfHandlingForm):
|
|
name = forms.CharField(
|
|
max_length=255, label=_("Share Name"), required=False,
|
|
help_text=_("Share name to be assigned"))
|
|
description = forms.CharField(
|
|
max_length=255, label=_("Description"), required=False,
|
|
widget=forms.Textarea(
|
|
attrs={'class': 'modal-body-fixed-width', 'rows': 4}))
|
|
|
|
host = forms.CharField(
|
|
max_length=255, label=_("Host of share"), required=True,
|
|
help_text=_(
|
|
"Host where share is located, example: some.host@driver[#pool]"))
|
|
export_location = forms.CharField(
|
|
max_length=255, label=_("Export location"), required=True,
|
|
help_text=_("Export location of share. Example for NFS: "
|
|
"1.2.3.4:/path/to/share"))
|
|
|
|
protocol = forms.ChoiceField(label=_("Share Protocol"), required=True)
|
|
|
|
share_type = forms.ChoiceField(label=_("Share Type"), required=True)
|
|
|
|
driver_options = forms.CharField(
|
|
max_length=255, required=False,
|
|
label=_("Driver options ('volume_id' for Generic driver, etc...)"),
|
|
help_text=_("key=value pairs per line can be set"),
|
|
widget=forms.Textarea(
|
|
attrs={'class': 'modal-body-fixed-width', 'rows': 2}))
|
|
is_public = forms.BooleanField(
|
|
label=_("Public"), required=False, initial=False,
|
|
help_text=("Defines whether this share is available for all or not."))
|
|
|
|
def __init__(self, request, *args, **kwargs):
|
|
super(ManageShare, self).__init__(request, *args, **kwargs)
|
|
share_types = manila.share_type_list(request)
|
|
# NOTE(vponomaryov): choose only those share_types that have spec
|
|
# 'driver_handles_share_servers' set to 'False' value or alias of it.
|
|
dhss_key = 'driver_handles_share_servers'
|
|
st_choices = [('', ''), ]
|
|
for st in share_types:
|
|
dhss = st.to_dict()['extra_specs'].get(dhss_key)
|
|
if dhss and dhss.lower() in strutils.FALSE_STRINGS:
|
|
st_choices.append((st.name, st.name))
|
|
self.fields['share_type'].choices = st_choices
|
|
# NOTE(vkmc): choose only those share protocols that are enabled
|
|
# FIXME(vkmc): this should be better implemented by having a
|
|
# capabilities endpoint on the control plane.
|
|
manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
|
|
self.enabled_share_protocols = manila_features.get(
|
|
'enabled_share_protocols',
|
|
['NFS', 'CIFS', 'GlusterFS', 'HDFS', 'CephFS', 'MapRFS'])
|
|
self.fields['protocol'].choices = ([(' ', ' ')] +
|
|
[(enabled_proto, enabled_proto)
|
|
for enabled_proto in
|
|
self.enabled_share_protocols])
|
|
|
|
def handle(self, request, data):
|
|
try:
|
|
driver_options = data.get('driver_options') or {}
|
|
driver_options_error_msg = _(
|
|
"Got improper value for field 'driver_options'. "
|
|
"Expected only pairs of key=value.")
|
|
if driver_options and isinstance(driver_options, six.string_types):
|
|
try:
|
|
set_dict, unset_list = utils.parse_str_meta(driver_options)
|
|
if unset_list:
|
|
raise ValidationError(message=driver_options_error_msg)
|
|
driver_options = set_dict
|
|
except ValidationError as e:
|
|
self.api_error(e.messages[0])
|
|
return False
|
|
elif not isinstance(driver_options, dict):
|
|
self.api_error(driver_options_error_msg)
|
|
return False
|
|
|
|
manila.share_manage(
|
|
request,
|
|
service_host=data['host'],
|
|
protocol=data['protocol'],
|
|
export_path=data['export_location'],
|
|
driver_options=driver_options,
|
|
share_type=data['share_type'],
|
|
name=data['name'],
|
|
description=data['description'],
|
|
is_public=data['is_public'])
|
|
|
|
share_name = data.get('name', data.get('id'))
|
|
messages.success(
|
|
request,
|
|
_('Successfully sent the request to manage share: %s')
|
|
% share_name)
|
|
return True
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to manage share"))
|
|
return False
|
|
|
|
|
|
class UnmanageShare(forms.SelfHandlingForm):
|
|
name = forms.CharField(
|
|
label=_("Share Name"), required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
host = forms.CharField(
|
|
label=_("Host"), required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
share_id = forms.CharField(
|
|
label=_("ID"), required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
|
|
|
def handle(self, request, data):
|
|
try:
|
|
manila.share_unmanage(request, self.initial['share_id'])
|
|
messages.success(
|
|
request,
|
|
_('Successfully sent the request to unmanage share: %s')
|
|
% data['name'])
|
|
return True
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to unmanage share."))
|
|
return False
|