Refactor admin dashboard

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
This commit is contained in:
Valeriy Ponomaryov 2017-05-12 20:55:24 +03:00
parent 3c7864717e
commit 6dcc671543
103 changed files with 3337 additions and 2087 deletions

View File

@ -57,7 +57,7 @@ Install Manila UI with all dependencies in your virtual environment::
And enable it in Horizon::
cp ../manila-ui/manila_ui/local/enabled/_90_manila_*.py openstack_dashboard/local/enabled
cp ../manila-ui/manila_ui/local/enabled/_*.py openstack_dashboard/local/enabled
cp ../manila-ui/manila_ui/local/local_settings.d/_90_manila_*.py openstack_dashboard/local/local_settings.d

View File

@ -0,0 +1,30 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class SecurityServices(horizon.Panel):
name = _("Security Services")
slug = 'security_services'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(SecurityServices)

View File

@ -0,0 +1,68 @@
# 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.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import tables
from manila_ui.api import manila
class DeleteSecurityService(tables.DeleteAction):
policy_rules = (("share", "security_service:delete"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Security Service",
u"Delete Security Services",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Security Service",
u"Deleted Security Services",
count
)
def delete(self, request, obj_id):
manila.security_service_delete(request, obj_id)
class SecurityServicesTable(tables.DataTable):
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:admin:security_services:security_service_detail")
project = tables.Column("project_name", verbose_name=_("Project"))
dns_ip = tables.Column("dns_ip", verbose_name=_("DNS IP"))
server = tables.Column("server", verbose_name=_("Server"))
domain = tables.Column("domain", verbose_name=_("Domain"))
user = tables.Column("user", verbose_name=_("Sid"))
def get_object_display(self, security_service):
return security_service.name
def get_object_id(self, security_service):
return str(security_service.id)
class Meta(object):
name = "security_services"
verbose_name = _("Security Services")
table_actions = (
tables.NameFilterAction,
DeleteSecurityService,
)
row_actions = (
DeleteSecurityService,
)

View File

@ -0,0 +1,32 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
from horizon import tabs
class SecurityServiceOverviewTab(tabs.Tab):
name = _("Security Service Overview")
slug = "security_service_overview_tab"
template_name = "admin/security_services/_detail.html"
def get_context_data(self, request):
return {"sec_service": self.tab_group.kwargs["sec_service"]}
class SecurityServiceDetailTabs(tabs.TabGroup):
slug = "security_service_details"
tabs = (
SecurityServiceOverviewTab,
)

View File

@ -0,0 +1,37 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "Security Service Overview" %}</h3>
<div class="detail">
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Type" %}</dt>
<dd>{{ sec_service.type }}</dd>
<dt>{% trans "Name" %}</dt>
<dd>{{ sec_service.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ sec_service.id }}</dd>
{% if sec_service.description %}
<dt>{% trans "Description" %}</dt>
<dd>{{ sec_service.description }}</dd>
{% endif %}
<dt>{% trans "Created at" %}</dt>
<dd>{{ sec_service.created_at|parse_date }}</dd>
</dl>
</div>
<div class="detail">
<h4>{% trans "Security Service Details" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "DNS IP" %}</dt>
<dd>{{ sec_service.dns_ip }}</dd>
<dt>{% trans "Server" %}</dt>
<dd>{{ sec_service.server }}</dd>
<dt>{% trans "Domain" %}</dt>
<dd>{{ sec_service.domain }}</dd>
<dt>{% trans "User" %}</dt>
<dd>{{ sec_service.user }}</dd>
</dl>
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Security Service Details" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Shares" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ security_services_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
# 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 urls
from manila_ui.dashboards.admin.security_services import views
urlpatterns = [
urls.url(
r'^$',
views.SecurityServicesView.as_view(),
name='index'),
urls.url(
r'^(?P<sec_service_id>[^/]+)$',
views.SecurityServiceDetailView.as_view(),
name='security_service_detail'),
]

View File

@ -0,0 +1,55 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.
"""
Admin views for managing security services.
"""
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon.utils import memoized
from manila_ui.api import manila
import manila_ui.dashboards.admin.security_services.tables as ss_tables
import manila_ui.dashboards.admin.security_services.tabs as ss_tabs
from manila_ui.dashboards.admin import utils
import manila_ui.dashboards.project.shares.security_services.views as ss_views
class SecurityServicesView(tables.MultiTableView):
table_classes = (
ss_tables.SecurityServicesTable,
)
template_name = "admin/security_services/index.html"
page_title = _("Security Services")
@memoized.memoized_method
def get_security_services_data(self):
try:
security_services = manila.security_service_list(
self.request, search_opts={'all_tenants': True})
utils.set_project_name_to_objects(self.request, security_services)
except Exception:
security_services = []
exceptions.handle(
self.request, _("Unable to retrieve security services"))
return security_services
class SecurityServiceDetailView(ss_views.Detail):
tab_group_class = ss_tabs.SecurityServiceDetailTabs
template_name = "admin/security_services/detail.html"
redirect_url = reverse_lazy('horizon:admin:security_services:index')

View File

@ -0,0 +1,28 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class ShareInstances(horizon.Panel):
name = _("Share Instances")
slug = 'share_instances'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(ShareInstances)

View File

@ -0,0 +1,93 @@
# 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.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import tables
import six
class ShareInstancesTable(tables.DataTable):
STATUS_CHOICES = (
("available", True),
("creating", None),
("deleting", None),
("error", False),
("error_deleting", False),
)
STATUS_DISPLAY_CHOICES = (
("available", u"Available"),
("creating", u"Creating"),
("deleting", u"Deleting"),
("error", u"Error"),
("error_deleting", u"Error deleting"),
)
uuid = tables.Column(
"id", verbose_name=_("ID"),
link="horizon:admin:share_instances:share_instance_detail")
host = tables.Column("host", verbose_name=_("Host"))
status = tables.Column(
"status",
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
availability_zone = tables.Column(
"availability_zone", verbose_name=_("Availability Zone"))
class Meta(object):
name = "share_instances"
verbose_name = _("Share Instances")
status_columns = ("status", )
table_actions = (
tables.NameFilterAction,)
multi_select = False
def get_share_network_link(share_instance):
if getattr(share_instance, 'share_network_id', None):
return reverse("horizon:admin:share_networks:share_network_detail",
args=(share_instance.share_network_id,))
else:
return None
def get_share_server_link(share_instance):
if getattr(share_instance, 'share_server_id', None):
return reverse("horizon:admin:share_servers:share_server_detail",
args=(share_instance.share_server_id,))
else:
return None
def get_share_link(share_instance):
if getattr(share_instance, 'share_id', None):
return reverse("horizon:admin:shares:detail",
args=(share_instance.share_id,))
else:
return None
share_net_id = tables.Column(
"share_network_id",
verbose_name=_("Share Network"),
link=get_share_network_link)
share_server_id = tables.Column(
"share_server_id",
verbose_name=_("Share Server Id"),
link=get_share_server_link)
share_id = tables.Column(
"share_id",
verbose_name=_("Share ID"),
link=get_share_link)
def get_object_display(self, share_instance):
return six.text_type(share_instance.id)
def get_object_id(self, share_instance):
return six.text_type(share_instance.id)

View File

@ -0,0 +1,32 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
from horizon import tabs
class ShareInstanceOverviewTab(tabs.Tab):
name = _("Share Instance Overview")
slug = "share_instance_overview_tab"
template_name = "admin/share_instances/_detail.html"
def get_context_data(self, request):
return {"share_instance": self.tab_group.kwargs["share_instance"]}
class ShareInstanceDetailTabs(tabs.TabGroup):
slug = "share_instance_details"
tabs = (
ShareInstanceOverviewTab,
)

View File

@ -29,16 +29,16 @@
<dt>{% trans "Availability Zone" %}</dt>
<dd>{{ share_instance.availability_zone}}</dd>
<dt>{% trans "Share ID" %}</dt>
{% url 'horizon:project:shares:detail' share_instance.share_id as share_url %}
{% url 'horizon:admin:shares:detail' share_instance.share_id as share_url %}
<dd><a href="{{ share_url }}">{{ share_instance.share_id }}</a></dd>
{% if share_instance.share_network_id %}
<dt>{% trans "Share network" %}</dt>
{% url 'horizon:project:shares:share_network_detail' share_instance.share_network_id as sn_url%}
{% url 'horizon:admin:share_networks:share_network_detail' share_instance.share_network_id as sn_url%}
<dd><a href="{{ sn_url }}">{{ share_instance.share_network_id }}</a></dd>
{% endif %}
{% if share_instance.share_server_id %}
<dt>{% trans "Share server" %}</dt>
{% url 'horizon:admin:shares:share_server_detail' share_instance.share_server_id as share_server_url%}
{% url 'horizon:admin:share_servers:share_server_detail' share_instance.share_server_id as share_server_url%}
<dd><a href="{{ share_server_url }}">{{ share_instance.share_server_id }}</a></dd>
{% endif %}
<dt>{% trans "Created" %}</dt>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Share Instances" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ share_instances_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
# 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 urls
from manila_ui.dashboards.admin.share_instances import views
urlpatterns = [
urls.url(
r'^$',
views.ShareInstancesView.as_view(),
name='index'),
urls.url(
r'^(?P<share_instance_id>[^/]+)$',
views.ShareInstanceDetailView.as_view(),
name='share_instance_detail'),
]

View File

@ -0,0 +1,87 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.
"""
Admin views for managing share instances.
"""
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon import tabs
from horizon.utils import memoized
from manila_ui.api import manila
from manila_ui.dashboards.admin.share_instances import tables as si_tables
from manila_ui.dashboards.admin.share_instances import tabs as si_tabs
from manila_ui.dashboards import utils as ui_utils
class ShareInstancesView(tables.MultiTableView):
table_classes = (
si_tables.ShareInstancesTable,
)
template_name = "admin/share_instances/index.html"
page_title = _("Share Instances")
@memoized.memoized_method
def get_share_instances_data(self):
try:
share_instances = manila.share_instance_list(self.request)
except Exception:
share_instances = []
exceptions.handle(
self.request, _("Unable to retrieve share instances."))
return share_instances
class ShareInstanceDetailView(tabs.TabView):
tab_group_class = si_tabs.ShareInstanceDetailTabs
template_name = 'admin/share_instances/detail.html'
def get_context_data(self, **kwargs):
context = super(self.__class__, self).get_context_data(**kwargs)
share_instance = self.get_data()
context["share_instance"] = share_instance
context["page_title"] = (
_("Share Instance Details: %s") % share_instance.id)
return context
@memoized.memoized_method
def get_data(self):
try:
share_instance_id = self.kwargs['share_instance_id']
share_instance = manila.share_instance_get(
self.request, share_instance_id)
share_instance.export_locations = (
manila.share_instance_export_location_list(
self.request, share_instance_id))
export_locations = [
exp['path'] for exp in share_instance.export_locations
]
share_instance.el_size = ui_utils.calculate_longest_str_size(
export_locations)
return share_instance
except Exception:
redirect = reverse('horizon:admin:share_instances:index')
exceptions.handle(
self.request,
_('Unable to retrieve share instance details.'),
redirect=redirect)
def get_tabs(self, request, *args, **kwargs):
share_instance = self.get_data()
return self.tab_group_class(
request, share_instance=share_instance, **kwargs)

View File

@ -0,0 +1,28 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class ShareNetworks(horizon.Panel):
name = _("Share Networks")
slug = 'share_networks'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(ShareNetworks)

View File

@ -0,0 +1,50 @@
# 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.utils.translation import ugettext_lazy as _
from horizon import tables
import six
import manila_ui.dashboards.project.shares.share_networks.tables as sn_tables
class ShareNetworksTable(tables.DataTable):
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:admin:share_networks:share_network_detail")
project = tables.Column("project_name", verbose_name=_("Project"))
neutron_net = tables.Column("neutron_net", verbose_name=_("Neutron Net"))
neutron_subnet = tables.Column(
"neutron_subnet", verbose_name=_("Neutron Subnet"))
ip_version = tables.Column("ip_version", verbose_name=_("IP Version"))
network_type = tables.Column(
"network_type", verbose_name=_("Network Type"))
segmentation_id = tables.Column(
"segmentation_id", verbose_name=_("Segmentation Id"))
def get_object_display(self, share_network):
return share_network.name or six.text_type(share_network.id)
def get_object_id(self, share_network):
return six.text_type(share_network.id)
class Meta(object):
name = "share_networks"
verbose_name = _("Share Networks")
table_actions = (
tables.NameFilterAction,
sn_tables.Delete,
)
row_class = sn_tables.UpdateRow
row_actions = (
sn_tables.Delete,
)

View File

@ -0,0 +1,32 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
from horizon import tabs
class ShareNetworkOverviewTab(tabs.Tab):
name = _("Share Network Overview")
slug = "share_network_overview_tab"
template_name = "admin/share_networks/_detail.html"
def get_context_data(self, request):
return {"share_network": self.tab_group.kwargs["share_network"]}
class ShareNetworkDetailTabs(tabs.TabGroup):
slug = "share_network_details"
tabs = (
ShareNetworkOverviewTab,
)

View File

@ -0,0 +1,56 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "Share Network Overview" %}</h3>
<div class="detail">
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ share_network.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ share_network.id }}</dd>
{% if share_network.description %}
<dt>{% trans "Description" %}</dt>
<dd>{{ share_network.description }}</dd>
{% endif %}
{% if share_network.share_servers %}
<dt>{% trans "Share Servers" %}</dt>
{% for server in share_network.share_servers %}
{% url 'horizon:admin:share_servers:share_server_detail' server.id as server_url %}
<dd><a href="{{ server_url }}">{{ server.id }}</a></dd>
{% endfor %}
{% endif %}
</dl>
</div>
<div class="detail">
<h4>{% trans "Net Details" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Network" %}</dt>
{% if share_network.neutron_net %}
<dd>{{ share_network.neutron_net }}</dd>
<dt>{% trans "Subnet" %}</dt>
<dd>{{ share_network.neutron_subnet}}</dd>
{% endif %}
{% if share_network.nova_net %}
<dd>{{ share_network.nova_net }}</dd>
{% endif %}
</dl>
</div>
<div class="detail">
<h4>{% trans "Security Services" %}</h4>
<hr class="header_rule">
{% for sec_service in share_network.sec_services %}
{% url 'horizon:admin:security_services:security_service_detail' sec_service.id as sec_service_url%}
<dl class="dl-horizontal">
<dt>{% trans "Id" %}</dt>
<dd><a href="{{ sec_service_url }}">{{ sec_service.id }}</a></dd>
<dt>{% trans "Name" %}</dt>
<dd><a href="{{ sec_service_url }}">{{ sec_service.name }}</a></dd>
<dt>{% trans "Type" %}</dt>
<dd>{{ sec_service.type }}</dd>
</dl>
<br />
{% endfor %}
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Share Network Details" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Share Networks" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ share_networks_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
# 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 urls
from manila_ui.dashboards.admin.share_networks import views
urlpatterns = [
urls.url(
r'^$',
views.ShareNetworksView.as_view(),
name='index'),
urls.url(
r'^(?P<share_network_id>[^/]+)$',
views.ShareNetworkDetailView.as_view(),
name='share_network_detail'),
]

View File

@ -0,0 +1,69 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.
"""
Admin views for managing share networks.
"""
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon.utils import memoized
from openstack_dashboard.api import base
from openstack_dashboard.api import neutron
from manila_ui.api import manila
from manila_ui.dashboards.admin.share_networks import tables as sn_tables
from manila_ui.dashboards.admin.share_networks import tabs as sn_tabs
from manila_ui.dashboards.admin import utils
import manila_ui.dashboards.project.shares.share_networks.views as p_views
class ShareNetworksView(tables.MultiTableView):
table_classes = (
sn_tables.ShareNetworksTable,
)
template_name = "admin/share_networks/index.html"
page_title = _("Share Networks")
@memoized.memoized_method
def get_share_networks_data(self):
try:
share_networks = manila.share_network_list(
self.request, detailed=True, search_opts={'all_tenants': True})
if base.is_service_enabled(self.request, 'network'):
neutron_net_names = dict(
(net.id, net.name)
for net in neutron.network_list(self.request))
neutron_subnet_names = dict(
(net.id, net.name)
for net in neutron.subnet_list(self.request))
for sn in share_networks:
sn.neutron_net = neutron_net_names.get(
sn.neutron_net_id) or sn.neutron_net_id or "-"
sn.neutron_subnet = neutron_subnet_names.get(
sn.neutron_subnet_id) or sn.neutron_subnet_id or "-"
except Exception:
share_networks = []
exceptions.handle(
self.request, _("Unable to retrieve share networks"))
utils.set_project_name_to_objects(self.request, share_networks)
return share_networks
class ShareNetworkDetailView(p_views.Detail):
tab_group_class = sn_tabs.ShareNetworkDetailTabs
template_name = "admin/share_networks/detail.html"
redirect_url = reverse_lazy("horizon:admin:share_networks:index")

View File

@ -0,0 +1,28 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class ShareServers(horizon.Panel):
name = _("Share Servers")
slug = 'share_servers'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(ShareServers)

View File

@ -0,0 +1,118 @@
# 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.core.urlresolvers import reverse
from django.template.defaultfilters import title # noqa
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import tables
import six
from manila_ui.api import manila
class DeleteShareServer(tables.DeleteAction):
policy_rules = (("share", "share_server:delete"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Share Server",
u"Delete Share Server",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Share Server",
u"Deleted Share Server",
count
)
def delete(self, request, obj_id):
manila.share_server_delete(request, obj_id)
def allowed(self, request, share_serv):
if share_serv:
share_search_opts = {'share_server_id': share_serv.id}
shares_list = manila.share_list(
request, search_opts=share_search_opts)
if shares_list:
return False
return share_serv.status not in ("deleting", "creating")
return True
class UpdateShareServerRow(tables.Row):
ajax = True
def get_data(self, request, share_serv_id):
share_serv = manila.share_server_get(request, share_serv_id)
return share_serv
class ShareServersTable(tables.DataTable):
STATUS_CHOICES = (
("active", True),
("deleting", None),
("creating", None),
("error", False),
)
STATUS_DISPLAY_CHOICES = (
("in-use", pgettext_lazy("Current status of share server", u"In-use")),
("active", pgettext_lazy("Current status of share server", u"Active")),
("creating", pgettext_lazy("Current status of share server",
u"Creating")),
("error", pgettext_lazy("Current status of share server",
u"Error")),
)
uid = tables.Column(
"id", verbose_name=_("Id"),
link="horizon:admin:share_servers:share_server_detail")
host = tables.Column("host", verbose_name=_("Host"))
project = tables.Column("project_name", verbose_name=_("Project"))
def get_share_server_link(share_serv):
if hasattr(share_serv, 'share_network_id'):
return reverse("horizon:admin:share_networks:share_network_detail",
args=(share_serv.share_network_id,))
else:
return None
share_net_name = tables.Column(
"share_network_name", verbose_name=_("Share Network"),
link=get_share_server_link)
status = tables.Column(
"status", verbose_name=_("Status"),
status=True, filters=(title,), status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
def get_object_display(self, share_server):
return six.text_type(share_server.id)
def get_object_id(self, share_server):
return six.text_type(share_server.id)
class Meta(object):
name = "share_servers"
status_columns = ["status"]
verbose_name = _("Share Server")
table_actions = (
tables.NameFilterAction,
DeleteShareServer,
)
row_class = UpdateShareServerRow
row_actions = (
DeleteShareServer,
)

View File

@ -0,0 +1,32 @@
# Copyright 2014 OpenStack Foundation
#
# 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.utils.translation import ugettext_lazy as _
from horizon import tabs
class ShareServerOverviewTab(tabs.Tab):
name = _("Share Server Overview")
slug = "share_server_overview_tab"
template_name = "admin/share_servers/_detail.html"
def get_context_data(self, request):
return {"share_server": self.tab_group.kwargs["share_server"]}
class ShareServerDetailTabs(tabs.TabGroup):
slug = "share_server_details"
tabs = (
ShareServerOverviewTab,
)

View File

@ -12,7 +12,7 @@
<dd>{{ share_server.host}}</dd>
<dt>{% trans "Share Network" %}</dt>
{% if share_server.share_network_id %}
{% url 'horizon:admin:shares:share_network_detail' share_server.share_network_id as share_net_url %}
{% url 'horizon:admin:share_networks:share_network_detail' share_server.share_network_id as share_net_url %}
<dd><a href="{{ share_net_url }}">{{ share_server.share_network_name }}</a></dd>
{% else %}
<dd>{{ share_server.share_network_name }}</dd>
@ -30,9 +30,9 @@
<h4>{% trans "Specs" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
{% for key in share_server.backend_details %}
{% for key, value in share_server.backend_details.items %}
<dt>{{ key }}</dt>
<dd>{{ share_server.backend_details|get_item:key }}</dd>
<dd>{{ value }}</dd>
{% endfor %}
</dl>
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Share Server Details" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Share Servers" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ share_servers_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
# 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 urls
from manila_ui.dashboards.admin.share_servers import views
urlpatterns = [
urls.url(
r'^$',
views.ShareServersView.as_view(),
name='index'),
urls.url(
r'^(?P<share_server_id>[^/]+)$',
views.ShareServerDetailView.as_view(),
name='share_server_detail'),
]

View File

@ -0,0 +1,91 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
"""
Admin views for managing share servers.
"""
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon import tabs
from horizon.utils import memoized
from manila_ui.api import manila
from manila_ui.dashboards.admin.share_servers import tables as ss_tables
from manila_ui.dashboards.admin.share_servers import tabs as ss_tabs
from manila_ui.dashboards.admin import utils
class ShareServersView(tables.MultiTableView):
table_classes = (
ss_tables.ShareServersTable,
)
template_name = "admin/share_servers/index.html"
page_title = _("Share Servers")
@memoized.memoized_method
def get_share_servers_data(self):
try:
share_servers = manila.share_server_list(self.request)
except Exception:
share_servers = []
exceptions.handle(
self.request, _("Unable to retrieve share servers"))
utils.set_project_name_to_objects(self.request, share_servers)
return share_servers
class ShareServerDetailView(tabs.TabView):
tab_group_class = ss_tabs.ShareServerDetailTabs
template_name = "admin/share_servers/detail.html"
redirect_url = reverse_lazy('horizon:admin:share_servers:index')
def get_context_data(self, **kwargs):
context = super(self.__class__, self).get_context_data(**kwargs)
share_server = self.get_data()
share_server_display_name = share_server.id
context["share_server"] = share_server
context["share_server_display_name"] = share_server_display_name
context["page_title"] = _("Share Server Details: %(server_name)s") % {
'server_name': share_server_display_name}
return context
@memoized.memoized_method
def get_data(self):
try:
share_serv_id = self.kwargs['share_server_id']
share_serv = manila.share_server_get(self.request, share_serv_id)
share_search_opts = {'share_server_id': share_serv.id}
shares_list = manila.share_list(
self.request, search_opts=share_search_opts)
for share in shares_list:
share.name_or_id = share.name or share.id
share_serv.shares_list = shares_list
if not hasattr(share_serv, 'share_network_id'):
share_serv.share_network_id = None
except Exception:
redirect = reverse('horizon:admin:share_servers:index')
exceptions.handle(
self.request,
_('Unable to retrieve share server details.'),
redirect=redirect)
return share_serv
def get_tabs(self, request, *args, **kwargs):
share_server = self.get_data()
return self.tab_group_class(
request, share_server=share_server, **kwargs)

View File

@ -0,0 +1,28 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class ShareSnapshots(horizon.Panel):
name = _("Share Snapshots")
slug = 'share_snapshots'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(ShareSnapshots)

View File

@ -0,0 +1,128 @@
# 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.core.urlresolvers import reverse
from django.template.defaultfilters import title # noqa
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import tables
from manila_ui.api import manila
from manila_ui.dashboards.project.shares.shares import tables as shares_tables
import manila_ui.dashboards.project.shares.snapshots.tables as ss_tables
def get_size(share):
return _("%sGiB") % share.size
class ShareSnapshotShareNameColumn(tables.Column):
def get_link_url(self, snapshot):
return reverse(self.link, args=(snapshot.share_id,))
class DeleteShareSnapshot(tables.DeleteAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Snapshot",
u"Delete Snapshots",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Snapshot",
u"Deleted Snapshots",
count
)
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "project_id", None)
return {"project_id": project_id}
def delete(self, request, obj_id):
obj = self.table.get_object_by_id(obj_id)
name = self.table.get_object_display(obj)
try:
manila.share_snapshot_delete(request, obj_id)
except Exception:
msg = _('Unable to delete snapshot "%s". One or more shares '
'depend on it.')
exceptions.check_message(["snapshots", "dependent"], msg % name)
raise
def allowed(self, request, snapshot=None):
if snapshot:
return snapshot.status.upper() in shares_tables.DELETABLE_STATES
return True
class ShareSnapshotsTable(tables.DataTable):
STATUS_CHOICES = (
("in-use", True),
("available", True),
("creating", None),
("error", False),
)
STATUS_DISPLAY_CHOICES = (
("in-use", pgettext_lazy("Current status of snapshot", u"In-use")),
("available",
pgettext_lazy("Current status of snapshot", u"Available")),
("creating", pgettext_lazy("Current status of snapshot", u"Creating")),
("error", pgettext_lazy("Current status of snapshot", u"Error")),
)
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:admin:share_snapshots:share_snapshot_detail")
description = tables.Column(
"description",
verbose_name=_("Description"),
truncate=40)
size = tables.Column(
get_size,
verbose_name=_("Size"),
attrs={'data-type': 'size'})
status = tables.Column(
"status",
filters=(title,),
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
source = ShareSnapshotShareNameColumn(
"share",
verbose_name=_("Source"),
link="horizon:admin:shares:detail")
def get_object_display(self, obj):
return obj.name
class Meta(object):
name = "share_snapshots"
verbose_name = _("Share Snapshots")
status_columns = ["status"]
row_class = ss_tables.UpdateRow
table_actions = (
tables.NameFilterAction,
DeleteShareSnapshot,
)
row_actions = (
DeleteShareSnapshot,
)

View File

@ -0,0 +1,32 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
from horizon import tabs
class ShareSnapshotOverviewTab(tabs.Tab):
name = _("Share Snapshot Overview")
slug = "share_snapshot_overview_tab"
template_name = "admin/share_snapshots/_detail.html"
def get_context_data(self, request):
return {"snapshot": self.tab_group.kwargs["snapshot"]}
class SnapshotDetailTabs(tabs.TabGroup):
slug = "share_snapshot_details"
tabs = (
ShareSnapshotOverviewTab,
)

View File

@ -0,0 +1,66 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "Share Snapshot Overview" %}</h3>
<div class="detail">
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ snapshot.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ snapshot.id }}</dd>
{% url 'horizon:admin:shares:detail' snapshot.share_id as share_url %}
<dt>{% trans "Source" %}</dt>
<dd><a href="{{ share_url }}">{{ snapshot.share_name_or_id }}</a></dd>
{% if snapshot.description %}
<dt>{% trans "Description" %}</dt>
<dd>{{ snapshot.description }}</dd>
{% endif %}
<dt>{% trans "Status" %}</dt>
<dd>{{ snapshot.status|capfirst }}</dd>
{% if snapshot.export_locations %}
<dt>{% trans "Export locations" %}</dt>
{% for el in snapshot.export_locations %}
<dd><p>
<div><b>Path:</b>
<input type="text" readonly="true"
value="{{ el.path }}" size="{{ snapshot.el_size }}"
onClick="this.setSelectionRange(0, this.value.length)">
</div>
{% if el.is_admin_only != None %}
<div><b>Is admin only:</b> {{ el.is_admin_only }}</div>
{% endif %}
{% if el.share_snapshot_instance_id %}
<div><b>Snapshot Replica ID:</b> {{ el.share_snapshot_instance_id }}</div>
{% endif %}
</p></dd>
{% endfor %}
{% endif %}
</dl>
</div>
{% if snapshot.rules != None %}
<div class="detail">
<h4>{% trans "Access Rules" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
{% for rule in snapshot.rules %}
<dt>{{ rule.access_type }}</dt>
<dd><p>
<div><b>Access to: </b>{{ rule.access_to }}</div>
<div><b>Status: </b>{{ rule.state }}</div>
</p></dd>
{% endfor %}
</dl>
</div>
{% endif %}
<div class="detail">
<h4>{% trans "Specs" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Size" %}</dt>
<dd>{{ snapshot.size }} {% trans "GiB" %}</dd>
<dt>{% trans "Created" %}</dt>
<dd>{{ snapshot.created_at|parse_date }}</dd>
</dl>
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Shares" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ share_snapshots_table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
# 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 urls
from manila_ui.dashboards.admin.share_snapshots import views
urlpatterns = [
urls.url(
r'^$',
views.ShareSnapshotsView.as_view(),
name='index'),
urls.url(
r'^(?P<snapshot_id>[^/]+)$',
views.ShareSnapshotDetailView.as_view(),
name='share_snapshot_detail'),
]

View File

@ -0,0 +1,63 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.
"""
Admin views for managing share snapshots.
"""
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon.utils import memoized
from manila_ui.api import manila
from manila_ui.dashboards.admin.share_snapshots import tables as ss_tables
from manila_ui.dashboards.admin.share_snapshots import tabs as ss_tabs
from manila_ui.dashboards.admin import utils
import manila_ui.dashboards.project.shares.snapshots.views as snapshot_views
class ShareSnapshotsView(tables.MultiTableView):
table_classes = (
ss_tables.ShareSnapshotsTable,
)
template_name = "admin/share_snapshots/index.html"
page_title = _("Share Snapshots")
@memoized.memoized_method
def get_share_snapshots_data(self):
snapshots = []
try:
snapshots = manila.share_snapshot_list(
self.request, search_opts={'all_tenants': True})
shares = manila.share_list(self.request)
share_names = dict([(share.id, share.name or share.id)
for share in shares])
for snapshot in snapshots:
snapshot.share = share_names.get(snapshot.share_id)
except Exception:
msg = _("Unable to retrieve share snapshot list.")
exceptions.handle(self.request, msg)
# Gather our projects to correlate against IDs
utils.set_project_name_to_objects(self.request, snapshots)
return snapshots
class ShareSnapshotDetailView(snapshot_views.SnapshotDetailView):
tab_group_class = ss_tabs.SnapshotDetailTabs
template_name = "admin/share_snapshots/detail.html"
redirect_url = reverse_lazy("horizon:admin:share_snapshots:index")

View File

@ -0,0 +1,133 @@
# 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.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 manila_ui.api import manila
from manila_ui.dashboards import utils
ST_EXTRA_SPECS_FORM_ATTRS = {
"rows": 5,
"cols": 40,
"style": "height: 135px; width: 100%;", # in case 'rows' not picked up
}
class CreateShareType(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Name"), required=True)
spec_driver_handles_share_servers = forms.ChoiceField(
label=_("Driver handles share servers"), required=True,
choices=(('False', 'False'), ('True', 'True')))
extra_specs = forms.CharField(
required=False, label=_("Extra specs"),
widget=forms.widgets.Textarea(attrs=ST_EXTRA_SPECS_FORM_ATTRS))
is_public = forms.BooleanField(
label=_("Public"), required=False, initial=True,
help_text=("Defines whether this share type is available for all "
"or not. List of allowed tenants should be set "
"separately."))
def __init__(self, *args, **kwargs):
super(CreateShareType, self).__init__(*args, **kwargs)
manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
self.enable_public_share_type_creation = manila_features.get(
'enable_public_share_type_creation', True)
if not self.enable_public_share_type_creation:
self.fields.pop('is_public')
def handle(self, request, data):
try:
spec_dhss = data['spec_driver_handles_share_servers'].lower()
allowed_dhss_values = ('true', 'false')
if spec_dhss not in allowed_dhss_values:
msg = _("Improper value set to required extra spec "
"'spec_driver_handles_share_servers'. "
"Allowed values are %s. "
"Case insensitive.") % allowed_dhss_values
raise ValidationError(message=msg)
set_dict, unset_list = utils.parse_str_meta(data['extra_specs'])
if unset_list:
msg = _("Expected only pairs of key=value.")
raise ValidationError(message=msg)
is_public = (self.enable_public_share_type_creation
and data["is_public"])
share_type = manila.share_type_create(
request, data["name"], spec_dhss, is_public=is_public)
if set_dict:
manila.share_type_set_extra_specs(
request, share_type.id, set_dict)
msg = _("Successfully created share type: %s") % share_type.name
messages.success(request, msg)
return True
except ValidationError as e:
# handle error without losing dialog
self.api_error(e.messages[0])
return False
except Exception:
exceptions.handle(request, _('Unable to create share type.'))
return False
class UpdateShareType(forms.SelfHandlingForm):
def __init__(self, *args, **kwargs):
super(UpdateShareType, self).__init__(*args, **kwargs)
# NOTE(vponomaryov): parse existing extra specs
# to str view for textarea html element
es_str = ""
for k, v in self.initial["extra_specs"].iteritems():
es_str += "%s=%s\r\n" % (k, v)
self.initial["extra_specs"] = es_str
extra_specs = forms.CharField(
required=False, label=_("Extra specs"),
widget=forms.widgets.Textarea(attrs=ST_EXTRA_SPECS_FORM_ATTRS))
def handle(self, request, data):
try:
set_dict, unset_list = utils.parse_str_meta(data['extra_specs'])
if set_dict:
manila.share_type_set_extra_specs(
request, self.initial["id"], set_dict)
if unset_list:
get = manila.share_type_get_extra_specs(
request, self.initial["id"])
# NOTE(vponomaryov): skip keys that are already unset
to_unset = set(unset_list).intersection(set(get.keys()))
if to_unset:
manila.share_type_unset_extra_specs(
request, self.initial["id"], to_unset)
msg = _("Successfully updated extra specs for share type '%s'.")
msg = msg % self.initial['name']
messages.success(request, msg)
return True
except ValidationError as e:
# handle error without losing dialog
self.api_error(e.messages[0])
return False
except Exception:
msg = _("Unable to update extra_specs for share type.")
exceptions.handle(request, msg)
return False

View File

@ -0,0 +1,30 @@
# Copyright 2017 Mirantis, Inc.
#
# 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.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class ShareTypes(horizon.Panel):
name = _("Share Types")
slug = 'share_types'
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(ShareTypes)

View File

@ -0,0 +1,111 @@
# 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.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import tables
import six
from manila_ui.api import manila
def get_size(share):
return _("%sGiB") % share.size
class CreateShareType(tables.LinkAction):
name = "create"
verbose_name = _("Create Share Type")
url = "horizon:admin:share_types:create_type"
classes = ("ajax-modal", "btn-create")
icon = "plus"
class DeleteShareType(tables.DeleteAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Share Type",
u"Delete Share Types",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Share Type",
u"Deleted Share Types",
count
)
def delete(self, request, obj_id):
manila.share_type_delete(request, obj_id)
class ManageShareTypeAccess(tables.LinkAction):
name = "manage"
verbose_name = _("Manage Share Type Access")
url = "horizon:admin:share_types:manage_share_type_access"
classes = ("ajax-modal", "btn-create")
def allowed(self, request, obj_id):
st = manila.share_type_get(request, obj_id)
# Enable it only for private share types
return not st.is_public
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "os-share-tenant-attr:tenant_id", None)
return {"project_id": project_id}
class UpdateShareType(tables.LinkAction):
name = "update share type"
verbose_name = _("Update Share Type")
url = "horizon:admin:share_types:update_type"
classes = ("ajax-modal", "btn-create")
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "os-share-tenant-attr:tenant_id", None)
return {"project_id": project_id}
class ShareTypesTable(tables.DataTable):
name = tables.WrappingColumn("name", verbose_name=_("Name"))
extra_specs = tables.Column("extra_specs", verbose_name=_("Extra specs"), )
visibility = tables.Column(
"is_public", verbose_name=_("Visibility"),
filters=(lambda d: 'public' if d is True else 'private', ),
)
def get_object_display(self, share_type):
return share_type.name
def get_object_id(self, share_type):
return six.text_type(share_type.id)
class Meta(object):
name = "share_types"
verbose_name = _("Share Types")
table_actions = (
tables.NameFilterAction,
CreateShareType,
DeleteShareType,
)
row_actions = (
UpdateShareType,
ManageShareTypeAccess,
DeleteShareType,
)

View File

@ -2,9 +2,9 @@
{% load i18n %}
{% block form_id %}{% endblock %}
{% block form_action %}{% url 'horizon:admin:shares:manage_share_type_access' share_type.id %}{% endblock %}
{% block form_action %}{% url 'horizon:admin:share_types:manage_share_type_access' share_type.id %}{% endblock %}
{% block modal_id %}add_security_service_modal{% endblock %}
{% block modal_id %}manage_share_type_access_modal{% endblock %}
{% block modal-header %}{% trans "Manage Share Type Access" %}{% endblock %}
@ -55,5 +55,5 @@
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Submit" %}" />
<a href="{% url 'horizon:admin:shares:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
<a href="{% url 'horizon:admin:share_types:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Create Share Type" %}{% endblock %}
{% block main %}
{% include 'admin/shares/_create_share_type.html' %}
{% include 'admin/share_types/_create.html' %}
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Share Types" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ share_types_table.render }}
</div>
</div>
{% endblock %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Manage Share Type Access" %}{% endblock %}
{% block main %}
{% include 'admin/shares/_manage_share_type_access.html' %}
{% include 'admin/share_types/_manage_access.html' %}
{% endblock %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Update Share Type" %}{% endblock %}
{% block main %}
{% include 'admin/shares/_update_share_type.html' %}
{% include 'admin/share_types/_update.html' %}
{% endblock %}

View File

@ -0,0 +1,35 @@
# 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 urls
from manila_ui.dashboards.admin.share_types import views
urlpatterns = [
urls.url(
r'^$',
views.ShareTypesView.as_view(),
name='index'),
urls.url(
r'^create_type$',
views.CreateShareTypeView.as_view(),
name='create_type'),
urls.url(
r'^update_type/(?P<share_type_id>[^/]+)/extra_specs$',
views.UpdateShareTypeView.as_view(),
name='update_type'),
urls.url(
r'^manage_share_type_access/(?P<share_type_id>[^/]+)$',
views.ManageShareTypeAccessView.as_view(),
name='manage_share_type_access'),
]

View File

@ -0,0 +1,119 @@
# 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.
"""
Admin views for managing share types.
"""
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import tables
from horizon.utils import memoized
from horizon import workflows
from manila_ui.api import manila
from manila_ui.dashboards.admin.share_types import forms as project_forms
from manila_ui.dashboards.admin.share_types import tables as st_tables
import manila_ui.dashboards.admin.share_types.workflows as st_workflows
from manila_ui.dashboards import utils as common_utils
class ShareTypesView(tables.MultiTableView):
table_classes = (
st_tables.ShareTypesTable,
)
template_name = "admin/share_types/index.html"
page_title = _("Share Types")
@memoized.memoized_method
def get_share_types_data(self):
try:
share_types = manila.share_type_list(self.request)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share types.'))
return []
# Convert dict with extra specs to friendly view
for st in share_types:
st.extra_specs = common_utils.metadata_to_str(
st.extra_specs, 8, 45)
return share_types
class CreateShareTypeView(forms.ModalFormView):
form_class = project_forms.CreateShareType
form_id = "create_share_type"
template_name = 'admin/share_types/create.html'
modal_header = _("Create Share Type")
submit_label = _("Create")
submit_url = reverse_lazy("horizon:admin:share_types:create_type")
success_url = 'horizon:admin:share_types:index'
page_title = _("Create Share Type")
def get_success_url(self):
return reverse(self.success_url)
class ManageShareTypeAccessView(workflows.WorkflowView):
workflow_class = st_workflows.ManageShareTypeAccessWorkflow
template_name = "admin/share_types/manage_access.html"
success_url = 'horizon:project:share_types:index'
page_title = _("Manage Share Type Access")
def get_initial(self):
return {'id': self.kwargs["share_type_id"]}
def get_context_data(self, **kwargs):
context = super(ManageShareTypeAccessView, self).get_context_data(
**kwargs)
context['id'] = self.kwargs['share_type_id']
return context
class UpdateShareTypeView(forms.ModalFormView):
form_class = project_forms.UpdateShareType
form_id = "update_share_type"
template_name = "admin/share_types/update.html"
modal_header = _("Update Share Type")
modal_id = "update_share_type_modal"
submit_label = _("Update")
submit_url = "horizon:admin:share_types:update_type"
success_url = reverse_lazy("horizon:admin:share_types:index")
page_title = _("Update Share Type")
def get_object(self):
if not hasattr(self, "_object"):
st_id = self.kwargs["share_type_id"]
try:
self._object = manila.share_type_get(self.request, st_id)
except Exception:
msg = _("Unable to retrieve share_type.")
url = reverse("horizon:admin:share_types:index")
exceptions.handle(self.request, msg, redirect=url)
return self._object
def get_context_data(self, **kwargs):
context = super(UpdateShareTypeView, self).get_context_data(**kwargs)
args = (self.get_object().id,)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
def get_initial(self):
share_type = self.get_object()
return {
"id": self.kwargs["share_type_id"],
"name": share_type.name,
"extra_specs": share_type.extra_specs,
}

View File

@ -92,7 +92,7 @@ class ManageShareTypeAccessWorkflow(workflows.Workflow):
finalize_button_name = _("Manage Share Type Access")
success_message = _('Updated access for share type "%s".')
failure_message = _('Unable to update access for share type "%s".')
success_url = 'horizon:admin:shares:index'
success_url = 'horizon:admin:share_types:index'
default_steps = (AddProjectStep, )
def format_status_message(self, message):

View File

@ -17,25 +17,23 @@ 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 oslo_utils import strutils
import six
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
from openstack_dashboard.api import keystone
from openstack_dashboard.api import neutron
ST_EXTRA_SPECS_FORM_ATTRS = {
"rows": 5,
"cols": 40,
"style": "height: 135px; width: 100%;", # in case 'rows' not picked up
}
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):
@ -310,189 +308,3 @@ class UnmanageShare(forms.SelfHandlingForm):
except Exception:
exceptions.handle(request, _("Unable to unmanage share."))
return False
class CreateShareType(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Name"), required=True)
spec_driver_handles_share_servers = forms.ChoiceField(
label=_("Driver handles share servers"), required=True,
choices=(('False', 'False'), ('True', 'True')))
extra_specs = forms.CharField(
required=False, label=_("Extra specs"),
widget=forms.widgets.Textarea(attrs=ST_EXTRA_SPECS_FORM_ATTRS))
is_public = forms.BooleanField(
label=_("Public"), required=False, initial=True,
help_text=("Defines whether this share type is available for all "
"or not. List of allowed tenants should be set "
"separately."))
def __init__(self, *args, **kwargs):
super(CreateShareType, self).__init__(*args, **kwargs)
manila_features = getattr(settings, 'OPENSTACK_MANILA_FEATURES', {})
self.enable_public_share_type_creation = manila_features.get(
'enable_public_share_type_creation', True)
if not self.enable_public_share_type_creation:
self.fields.pop('is_public')
def handle(self, request, data):
try:
spec_dhss = data['spec_driver_handles_share_servers'].lower()
allowed_dhss_values = ('true', 'false')
if spec_dhss not in allowed_dhss_values:
msg = _("Improper value set to required extra spec "
"'spec_driver_handles_share_servers'. "
"Allowed values are %s. "
"Case insensitive.") % allowed_dhss_values
raise ValidationError(message=msg)
set_dict, unset_list = utils.parse_str_meta(data['extra_specs'])
if unset_list:
msg = _("Expected only pairs of key=value.")
raise ValidationError(message=msg)
is_public = (self.enable_public_share_type_creation
and data["is_public"])
share_type = manila.share_type_create(
request, data["name"], spec_dhss, is_public=is_public)
if set_dict:
manila.share_type_set_extra_specs(
request, share_type.id, set_dict)
msg = _("Successfully created share type: %s") % share_type.name
messages.success(request, msg)
return True
except ValidationError as e:
# handle error without losing dialog
self.api_error(e.messages[0])
return False
except Exception:
exceptions.handle(request, _('Unable to create share type.'))
return False
class UpdateShareType(forms.SelfHandlingForm):
def __init__(self, *args, **kwargs):
super(UpdateShareType, self).__init__(*args, **kwargs)
# NOTE(vponomaryov): parse existing extra specs
# to str view for textarea html element
es_str = ""
for k, v in self.initial["extra_specs"].iteritems():
es_str += "%s=%s\r\n" % (k, v)
self.initial["extra_specs"] = es_str
extra_specs = forms.CharField(
required=False, label=_("Extra specs"),
widget=forms.widgets.Textarea(attrs=ST_EXTRA_SPECS_FORM_ATTRS))
def handle(self, request, data):
try:
set_dict, unset_list = utils.parse_str_meta(data['extra_specs'])
if set_dict:
manila.share_type_set_extra_specs(
request, self.initial["id"], set_dict)
if unset_list:
get = manila.share_type_get_extra_specs(
request, self.initial["id"])
# NOTE(vponomaryov): skip keys that are already unset
to_unset = set(unset_list).intersection(set(get.keys()))
if to_unset:
manila.share_type_unset_extra_specs(
request, self.initial["id"], to_unset)
msg = _("Successfully updated extra specs for share type '%s'.")
msg = msg % self.initial['name']
messages.success(request, msg)
return True
except ValidationError as e:
# handle error without losing dialog
self.api_error(e.messages[0])
return False
except Exception:
msg = _("Unable to update extra_specs for share type.")
exceptions.handle(request, msg)
return False
class CreateSecurityService(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Name"))
dns_ip = forms.CharField(max_length="15", label=_("DNS IP"))
server = forms.CharField(max_length="255", label=_("Server"))
domain = forms.CharField(max_length="255", label=_("Domain"))
user = forms.CharField(max_length="255", label=_("User"))
password = forms.CharField(max_length="255", label=_("Password"))
type = forms.ChoiceField(choices=(("", ""),
("active_directory", "Active Directory"),
("ldap", "LDAP"),
("kerberos", "Kerberos")),
label=_("Type"))
description = forms.CharField(widget=forms.Textarea,
label=_("Description"), required=False)
def handle(self, request, data):
try:
# Remove any new lines in the public key
security_service = manila.security_service_create(
request, **data)
messages.success(request,
_('Successfully created security service: %s')
% data['name'])
return security_service
except Exception:
exceptions.handle(request,
_('Unable to create security service.'))
return False
class CreateShareNetworkForm(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Name"))
neutron_net_id = forms.ChoiceField(choices=(), label=_("Neutron Net ID"))
neutron_subnet_id = forms.ChoiceField(choices=(),
label=_("Neutron Subnet ID"))
# security_service = forms.MultipleChoiceField(
# widget=forms.SelectMultiple,
# label=_("Security Service"))
project = forms.ChoiceField(choices=(), label=_("Project"))
description = forms.CharField(widget=forms.Textarea,
label=_("Description"), required=False)
def __init__(self, request, *args, **kwargs):
super(CreateShareNetworkForm, self).__init__(
request, *args, **kwargs)
net_choices = neutron.network_list(request)
subnet_choices = neutron.subnet_list(request)
self.fields['neutron_net_id'].choices = [(' ', ' ')] + \
[(choice.id, choice.name_or_id)
for choice in net_choices]
self.fields['neutron_subnet_id'].choices = [(' ', ' ')] + \
[(choice.id,
choice.name_or_id) for
choice in subnet_choices]
tenants, has_more = keystone.tenant_list(request)
self.fields['project'].choices = [(' ', ' ')] + \
[(choice.id,
choice.name) for
choice in tenants]
def handle(self, request, data):
try:
# Remove any new lines in the public key
share_network = manila.share_network_create(request, **data)
messages.success(request,
_('Successfully created share network: %s')
% data['name'])
return share_network
except Exception:
exceptions.handle(request,
_('Unable to create share network.'))
return False
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

View File

@ -1,3 +1,5 @@
# Copyright 2017 Mirantis, Inc.
#
# 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
@ -11,15 +13,16 @@
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.admin import dashboard
class Shares(horizon.Panel):
name = _("Shares")
slug = "shares"
permissions = ('openstack.services.share',)
permissions = (
'openstack.services.share',
)
dashboard.Admin.register(Shares)

View File

@ -10,77 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from django.core.urlresolvers import reverse
from django.template.defaultfilters import title # noqa
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import tables
from manila_ui.api import manila
from manila_ui.dashboards.project.shares.share_networks \
import tables as share_networks_tables
from manila_ui.dashboards.project.shares.shares import tables as shares_tables
from manila_ui.dashboards.project.shares.snapshots \
import tables as snapshot_tables
def get_size(share):
return _("%sGiB") % share.size
class CreateShareType(tables.LinkAction):
name = "create"
verbose_name = _("Create Share Type")
url = "horizon:admin:shares:create_type"
classes = ("ajax-modal", "btn-create")
icon = "plus"
policy_rules = (("share", "share_extension:types_manage"),)
class DeleteShareType(tables.DeleteAction):
policy_rules = (("share", "share_extension:types_manage"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Share Type",
u"Delete Share Types",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Share Type",
u"Deleted Share Types",
count
)
def delete(self, request, obj_id):
manila.share_type_delete(request, obj_id)
class ManageShareTypeAccess(tables.LinkAction):
name = "manage"
verbose_name = _("Manage Share Type Access")
url = "horizon:admin:shares:manage_share_type_access"
classes = ("ajax-modal", "btn-create")
policy_rules = (("share", "share_extension:share_type_access"),)
def allowed(self, request, obj_id):
st = manila.share_type_get(request, obj_id)
# Enable it only for private share types
return not st.is_public
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "os-share-tenant-attr:tenant_id", None)
return {"project_id": project_id}
class MigrationStartAction(tables.LinkAction):
@ -171,46 +106,6 @@ class UnmanageShareAction(tables.LinkAction):
return False
class UpdateShareType(tables.LinkAction):
name = "update share type"
verbose_name = _("Update Share Type")
url = "horizon:admin:shares:update_type"
classes = ("ajax-modal", "btn-create")
policy_rules = (("share", "share_extension:types_manage"),)
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "os-share-tenant-attr:tenant_id", None)
return {"project_id": project_id}
class ShareTypesTable(tables.DataTable):
name = tables.WrappingColumn("name", verbose_name=_("Name"))
extra_specs = tables.Column("extra_specs", verbose_name=_("Extra specs"), )
visibility = tables.Column(
"is_public", verbose_name=_("Visibility"),
filters=(lambda d: 'public' if d is True else 'private', ),)
def get_object_display(self, share_type):
return share_type.name
def get_object_id(self, share_type):
return str(share_type.id)
class Meta(object):
name = "share_types"
verbose_name = _("Share Types")
table_actions = (
tables.NameFilterAction,
CreateShareType,
DeleteShareType)
row_actions = (
UpdateShareType,
ManageShareTypeAccess,
DeleteShareType)
class ManageReplicas(tables.LinkAction):
name = "manage_replicas"
verbose_name = _("Manage Replicas")
@ -232,14 +127,15 @@ class SharesTable(shares_tables.SharesTable):
def get_share_server_link(share):
if getattr(share, 'share_server_id', None):
return reverse("horizon:admin:shares:share_server_detail",
return reverse("horizon:admin:share_servers:share_server_detail",
args=(share.share_server_id,))
else:
return None
share_server = tables.Column("share_server_id",
verbose_name=_("Share Server"),
link=get_share_server_link)
share_server = tables.Column(
"share_server_id",
verbose_name=_("Share Server"),
link=get_share_server_link)
class Meta(object):
name = "shares"
@ -249,7 +145,8 @@ class SharesTable(shares_tables.SharesTable):
table_actions = (
tables.NameFilterAction,
ManageShareAction,
shares_tables.DeleteShare)
shares_tables.DeleteShare,
)
row_actions = (
ManageReplicas,
MigrationStartAction,
@ -257,382 +154,9 @@ class SharesTable(shares_tables.SharesTable):
MigrationGetProgressAction,
MigrationCancelAction,
UnmanageShareAction,
shares_tables.DeleteShare)
shares_tables.DeleteShare,
)
columns = (
'tenant', 'host', 'name', 'size', 'status', 'visibility',
'share_type', 'protocol', 'share_server',
)
class SnapshotShareNameColumn(tables.Column):
def get_link_url(self, snapshot):
return reverse(self.link, args=(snapshot.share_id,))
class DeleteSnapshot(tables.DeleteAction):
policy_rules = (("snapshot", "snapshot:delete"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Snapshot",
u"Delete Snapshots",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Snapshot",
u"Deleted Snapshots",
count
)
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum, "project_id", None)
return {"project_id": project_id}
def delete(self, request, obj_id):
obj = self.table.get_object_by_id(obj_id)
name = self.table.get_object_display(obj)
try:
manila.share_snapshot_delete(request, obj_id)
except Exception:
msg = _('Unable to delete snapshot "%s". One or more shares '
'depend on it.')
exceptions.check_message(["snapshots", "dependent"], msg % name)
raise
def allowed(self, request, snapshot=None):
if snapshot:
return snapshot.status.upper() in shares_tables.DELETABLE_STATES
return True
class SnapshotsTable(tables.DataTable):
STATUS_CHOICES = (
("in-use", True),
("available", True),
("creating", None),
("error", False),
)
STATUS_DISPLAY_CHOICES = (
("in-use", pgettext_lazy("Current status of snapshot", u"In-use")),
("available", pgettext_lazy("Current status of snapshot",
u"Available")),
("creating", pgettext_lazy("Current status of snapshot", u"Creating")),
("error", pgettext_lazy("Current status of snapshot", u"Error")),
)
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:admin:shares:snapshot-detail")
description = tables.Column("description",
verbose_name=_("Description"),
truncate=40)
size = tables.Column(get_size,
verbose_name=_("Size"),
attrs={'data-type': 'size'})
status = tables.Column("status",
filters=(title,),
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
source = SnapshotShareNameColumn("share",
verbose_name=_("Source"),
link="horizon:admin:shares:detail")
def get_object_display(self, obj):
return obj.name
class Meta(object):
name = "snapshots"
verbose_name = _("Snapshots")
status_columns = ["status"]
row_class = snapshot_tables.UpdateRow
table_actions = (
tables.NameFilterAction,
DeleteSnapshot)
row_actions = (
DeleteSnapshot,)
class DeleteSecurityService(tables.DeleteAction):
policy_rules = (("share", "security_service:delete"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Security Service",
u"Delete Security Services",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Security Service",
u"Deleted Security Services",
count
)
def delete(self, request, obj_id):
manila.security_service_delete(request, obj_id)
class DeleteShareServer(tables.DeleteAction):
policy_rules = (("share", "share_server:delete"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Share Server",
u"Delete Share Server",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Share Server",
u"Deleted Share Server",
count
)
def delete(self, request, obj_id):
manila.share_server_delete(request, obj_id)
def allowed(self, request, share_serv):
if share_serv:
share_search_opts = {'share_server_id': share_serv.id}
shares_list = manila.share_list(request,
search_opts=share_search_opts)
if shares_list:
return False
return share_serv.status not in ["deleting", "creating"]
return True
class SecurityServiceTable(tables.DataTable):
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:admin:shares:security_service_detail")
project = tables.Column("project_name", verbose_name=_("Project"))
dns_ip = tables.Column("dns_ip", verbose_name=_("DNS IP"))
server = tables.Column("server", verbose_name=_("Server"))
domain = tables.Column("domain", verbose_name=_("Domain"))
user = tables.Column("user", verbose_name=_("Sid"))
def get_object_display(self, security_service):
return security_service.name
def get_object_id(self, security_service):
return str(security_service.id)
class Meta(object):
name = "security_services"
verbose_name = _("Security Services")
table_actions = (
tables.NameFilterAction,
DeleteSecurityService)
row_actions = (
DeleteSecurityService,)
class UpdateShareServerRow(tables.Row):
ajax = True
def get_data(self, request, share_serv_id):
share_serv = manila.share_server_get(request, share_serv_id)
return share_serv
class NovaShareNetworkTable(tables.DataTable):
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:admin:shares:share_network_detail")
project = tables.Column("project_name", verbose_name=_("Project"))
nova_net = tables.Column("nova_net", verbose_name=_("Nova Net"))
ip_version = tables.Column("ip_version", verbose_name=_("IP Version"))
network_type = tables.Column("network_type",
verbose_name=_("Network Type"))
segmentation_id = tables.Column("segmentation_id",
verbose_name=_("Segmentation Id"))
def get_object_display(self, share_network):
return share_network.name or str(share_network.id)
def get_object_id(self, share_network):
return str(share_network.id)
class Meta(object):
name = "share_networks"
verbose_name = _("Share Networks")
table_actions = (
tables.NameFilterAction,
share_networks_tables.Delete)
row_class = share_networks_tables.UpdateRow
row_actions = (
share_networks_tables.Delete,)
class NeutronShareNetworkTable(tables.DataTable):
name = tables.WrappingColumn(
"name", verbose_name=_("Name"),
link="horizon:project:shares:share_network_detail")
project = tables.Column("project_name", verbose_name=_("Project"))
neutron_net = tables.Column("neutron_net", verbose_name=_("Neutron Net"))
neutron_subnet = tables.Column(
"neutron_subnet", verbose_name=_("Neutron Subnet"))
ip_version = tables.Column("ip_version", verbose_name=_("IP Version"))
network_type = tables.Column("network_type",
verbose_name=_("Network Type"))
segmentation_id = tables.Column("segmentation_id",
verbose_name=_("Segmentation Id"))
def get_object_display(self, share_network):
return share_network.name or str(share_network.id)
def get_object_id(self, share_network):
return str(share_network.id)
class Meta(object):
name = "share_networks"
verbose_name = _("Share Networks")
table_actions = (
tables.NameFilterAction,
share_networks_tables.Delete)
row_class = share_networks_tables.UpdateRow
row_actions = (
share_networks_tables.Delete,)
class ShareServerTable(tables.DataTable):
STATUS_CHOICES = (
("active", True),
("deleting", None),
("creating", None),
("error", False),
)
STATUS_DISPLAY_CHOICES = (
("in-use", pgettext_lazy("Current status of share server", u"In-use")),
("active", pgettext_lazy("Current status of share server", u"Active")),
("creating", pgettext_lazy("Current status of share server",
u"Creating")),
("error", pgettext_lazy("Current status of share server",
u"Error")),
)
uid = tables.Column("id", verbose_name=_("Id"),
link="horizon:admin:shares:share_server_detail")
host = tables.Column("host", verbose_name=_("Host"))
project = tables.Column("project_name", verbose_name=_("Project"))
def get_share_server_link(share_serv):
if hasattr(share_serv, 'share_network_id'):
return reverse("horizon:admin:shares:share_network_detail",
args=(share_serv.share_network_id,))
else:
return None
share_net_name = tables.Column("share_network_name",
verbose_name=_("Share Network"),
link=get_share_server_link)
status = tables.Column("status", verbose_name=_("Status"),
status=True, filters=(title,),
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
def get_object_display(self, share_server):
return six.text_type(share_server.id)
def get_object_id(self, share_server):
return six.text_type(share_server.id)
class Meta(object):
name = "share_servers"
status_columns = ["status"]
verbose_name = _("Share Server")
table_actions = (
tables.NameFilterAction,
DeleteShareServer)
row_class = UpdateShareServerRow
row_actions = (
DeleteShareServer,)
class ShareInstancesTable(tables.DataTable):
STATUS_CHOICES = (
("available", True),
("creating", None),
("deleting", None),
("error", False),
("error_deleting", False),
)
STATUS_DISPLAY_CHOICES = (
("available", u"Available"),
("creating", u"Creating"),
("deleting", u"Deleting"),
("error", u"Error"),
("error_deleting", u"Error deleting"),
)
uuid = tables.Column(
"id", verbose_name=_("ID"),
link="horizon:admin:shares:share_instance_detail")
host = tables.Column("host", verbose_name=_("Host"))
status = tables.Column("status",
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES,
display_choices=STATUS_DISPLAY_CHOICES)
availability_zone = tables.Column(
"availability_zone", verbose_name=_("Availability Zone"))
class Meta(object):
name = "share_instances"
verbose_name = _("Share Instances")
status_columns = ("status", )
table_actions = (
tables.NameFilterAction,)
multi_select = False
def get_share_network_link(share_instance):
if getattr(share_instance, 'share_network_id', None):
return reverse("horizon:admin:shares:share_network_detail",
args=(share_instance.share_network_id,))
else:
return None
def get_share_server_link(share_instance):
if getattr(share_instance, 'share_server_id', None):
return reverse("horizon:admin:shares:share_server_detail",
args=(share_instance.share_server_id,))
else:
return None
def get_share_link(share_instance):
if getattr(share_instance, 'share_id', None):
return reverse("horizon:project:shares:detail",
args=(share_instance.share_id,))
else:
return None
share_net_id = tables.Column(
"share_network_id",
verbose_name=_("Share Network"),
link=get_share_network_link)
share_server_id = tables.Column(
"share_server_id",
verbose_name=_("Share Server Id"),
link=get_share_server_link)
share_id = tables.Column(
"share_id",
verbose_name=_("Share ID"),
link=get_share_link)
def get_object_display(self, share_instance):
return six.text_type(share_instance.id)
def get_object_id(self, share_instance):
return six.text_type(share_instance.id)

View File

@ -1,4 +1,4 @@
# Copyright 2014 OpenStack Foundation
# Copyright 2017 Mirantis, Inc.
#
# 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
@ -13,241 +13,20 @@
# under the License.
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tabs
from openstack_dashboard.api import base
from openstack_dashboard.api import neutron
from manila_ui.api import manila
from manila_ui.api import network
from manila_ui.dashboards.admin.shares import tables
from manila_ui.dashboards.admin.shares import utils
from manila_ui.dashboards import utils as common_utils
class SnapshotsTab(tabs.TableTab):
table_classes = (tables.SnapshotsTable, )
name = _("Snapshots")
slug = "snapshots_tab"
template_name = "horizon/common/_detail_table.html"
def _set_id_if_nameless(self, snapshots):
for snap in snapshots:
if not snap.name:
snap.name = snap.id
def get_snapshots_data(self):
snapshots = []
try:
snapshots = manila.share_snapshot_list(
self.request, search_opts={'all_tenants': True})
shares = manila.share_list(self.request)
share_names = dict([(share.id, share.name or share.id)
for share in shares])
for snapshot in snapshots:
snapshot.share = share_names.get(snapshot.share_id)
except Exception:
msg = _("Unable to retrieve snapshot list.")
exceptions.handle(self.request, msg)
# Gather our projects to correlate against IDs
utils.set_project_name_to_objects(self.request, snapshots)
return snapshots
class SharesTab(tabs.TableTab):
table_classes = (tables.SharesTable, )
name = _("Shares")
slug = "shares_tab"
template_name = "horizon/common/_detail_table.html"
def get_shares_data(self):
shares = []
try:
shares = manila.share_list(
self.request, search_opts={'all_tenants': True})
snapshots = manila.share_snapshot_list(
self.request, detailed=True, search_opts={'all_tenants': True})
share_ids_with_snapshots = []
for snapshot in snapshots:
share_ids_with_snapshots.append(snapshot.to_dict()['share_id'])
for share in shares:
if share.to_dict()['id'] in share_ids_with_snapshots:
setattr(share, 'has_snapshot', True)
else:
setattr(share, 'has_snapshot', False)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share list.'))
# Gather our projects to correlate against IDs
utils.set_project_name_to_objects(self.request, shares)
return shares
class ShareTypesTab(tabs.TableTab):
table_classes = (tables.ShareTypesTable, )
name = _("Share Types")
slug = "share_types_tab"
template_name = "horizon/common/_detail_table.html"
def get_share_types_data(self):
try:
share_types = manila.share_type_list(self.request)
except Exception:
share_types = []
exceptions.handle(self.request,
_("Unable to retrieve share types"))
# Convert dict with extra specs to friendly view
for st in share_types:
st.extra_specs = common_utils.metadata_to_str(
st.extra_specs, 8, 45)
return share_types
class SecurityServiceTab(tabs.TableTab):
table_classes = (tables.SecurityServiceTable,)
name = _("Security Services")
slug = "security_services_tab"
template_name = "horizon/common/_detail_table.html"
def get_security_services_data(self):
try:
security_services = manila.security_service_list(
self.request, search_opts={'all_tenants': True})
except Exception:
security_services = []
exceptions.handle(self.request,
_("Unable to retrieve security services"))
utils.set_project_name_to_objects(self.request, security_services)
return security_services
class ShareNetworkTab(tabs.TableTab):
name = _("Share Networks")
slug = "share_networks_tab"
template_name = "horizon/common/_detail_table.html"
def __init__(self, tab_group, request):
if base.is_service_enabled(request, 'network'):
self.table_classes = (tables.NeutronShareNetworkTable,)
else:
self.table_classes = (tables.NovaShareNetworkTable,)
super(ShareNetworkTab, self).__init__(tab_group, request)
def get_share_networks_data(self):
try:
share_networks = manila.share_network_list(
self.request, detailed=True, search_opts={'all_tenants': True})
if base.is_service_enabled(self.request, 'network'):
neutron_net_names = dict((net.id, net.name) for net in
neutron.network_list(self.request))
neutron_subnet_names = dict((net.id, net.name) for net in
neutron.subnet_list(self.request))
for sn in share_networks:
sn.neutron_net = neutron_net_names.get(
sn.neutron_net_id) or sn.neutron_net_id or "-"
sn.neutron_subnet = neutron_subnet_names.get(
sn.neutron_subnet_id) or sn.neutron_subnet_id or "-"
else:
nova_net_names = dict(
[(net.id, net.label)
for net in network.network_list(self.request)])
for sn in share_networks:
sn.nova_net = nova_net_names.get(
sn.nova_net_id) or sn.nova_net_id or "-"
except Exception:
share_networks = []
exceptions.handle(self.request,
_("Unable to retrieve share networks"))
utils.set_project_name_to_objects(self.request, share_networks)
return share_networks
class ShareInstancesTab(tabs.TableTab):
table_classes = (tables.ShareInstancesTable,)
name = _("Share Instances")
slug = "share_instances_tab"
template_name = "horizon/common/_detail_table.html"
def get_share_instances_data(self):
try:
share_instances = manila.share_instance_list(self.request)
except Exception:
share_instances = []
exceptions.handle(
self.request, _("Unable to retrieve share instances."))
return share_instances
class ShareInstanceOverviewTab(tabs.Tab):
name = _("Overview")
slug = "overview"
template_name = ("admin/shares/_detail_share_instance.html")
class ShareOverviewTab(tabs.Tab):
name = _("Share Overview")
slug = "share_overview_tab"
template_name = "admin/shares/_detail.html"
def get_context_data(self, request):
return {"share_instance": self.tab_group.kwargs['share_instance']}
return {"share": self.tab_group.kwargs["share"]}
class ShareInstanceDetailTabs(tabs.TabGroup):
slug = "share_instance_details"
tabs = (ShareInstanceOverviewTab,)
class ShareServerTab(tabs.TableTab):
table_classes = (tables.ShareServerTable,)
name = _("Share Servers")
slug = "share_servers_tab"
template_name = "horizon/common/_detail_table.html"
def get_share_servers_data(self):
try:
share_servers = manila.share_server_list(
self.request)
except Exception:
share_servers = []
exceptions.handle(self.request,
_("Unable to retrieve share servers"))
utils.set_project_name_to_objects(self.request, share_servers)
return share_servers
class ShareServerOverviewTab(tabs.Tab):
name = _("Overview")
slug = "overview"
template_name = ("admin/shares/_detail_share_server.html")
def get_context_data(self, request):
return {"share_server": self.tab_group.kwargs['share_server']}
class ShareServerDetailTabs(tabs.TabGroup):
slug = "share_server_details"
tabs = (ShareServerOverviewTab,)
class ShareTabs(tabs.TabGroup):
slug = "share_tabs"
tabs = (SharesTab, SnapshotsTab, ShareNetworkTab, SecurityServiceTab,
ShareTypesTab, ShareServerTab, ShareInstancesTab)
sticky = True
class SnapshotOverviewTab(tabs.Tab):
name = _("Snapshot Overview")
slug = "snapshot_overview_tab"
template_name = ("admin/shares/"
"_snapshot_detail_overview.html")
def get_context_data(self, request):
return {"snapshot": self.tab_group.kwargs['snapshot']}
class SnapshotDetailTabs(tabs.TabGroup):
slug = "snapshot_details"
tabs = (SnapshotOverviewTab,)
class ShareDetailTabs(tabs.TabGroup):
slug = "share_details"
tabs = (
ShareOverviewTab,
)

View File

@ -0,0 +1,100 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "Share Overview" %}</h3>
<div class="detail">
<hr class="header_rule">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ share.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ share.id }}</dd>
{% if share.description %}
<dt>{% trans "Description" %}</dt>
<dd>{{ share.description }}</dd>
{% endif %}
<dt>{% trans "Status" %}</dt>
<dd>{{ share.status|capfirst }}</dd>
<dt>{% trans "Export locations" %}</dt>
{% for el in share.export_locations %}
<dd><p>
<div><b>Path:</b>
<input type="text" readonly="true"
value="{{ el.path }}" size="{{ share.el_size }}"
onClick="this.setSelectionRange(0, this.value.length)">
</div>
<div><b>Preferred:</b> {{ el.preferred }}</div>
{% if el.is_admin_only == True or el.is_admin_only == False %}
<div><b>Is admin only:</b> {{ el.is_admin_only }}</div>
{% endif %}
{% if el.share_instance_id %}
<div><b>Share Replica ID:</b> {{ el.share_instance_id }}</div>
{% endif %}
</p></dd>
{% endfor %}
{% if share.snapshot_id %}
<dt>{% trans "Snapshot ID" %}</dt>
{% url 'horizon:admin:share_snapshots:snapshot_detail' share.snapshot_id as snapshot_url%}
<dd><a href="{{ snapshot_url }}">{{ share.snapshot_id }}</a></dd>
{% endif %}
<dt>{% trans "Visibility" %}</dt>
{% if share.is_public == True %}
<dd>{{ 'public' }}</dd>
{% else %}
<dd>{{ 'private' }}</dd>
<dt>{% trans "Availability zone" %}</dt>
<dd>{{ share.availability_zone }}</dd>
{% endif %}
<dt>{% trans "Size" %}</dt>
<dd>{{ share.size }} {% trans "GiB" %}</dd>
<dt>{% trans "Protocol" %}</dt>
<dd>{{ share.share_proto }}</dd>
{% if share.share_type %}
<dt>{% trans "Share type" %}</dt>
<dd><p>
<div><b>Name: </b>{{ share.share_type_name }}</div>
<div><b>ID: </b>{{ share.share_type }}</div>
</p></dd>
{% endif %}
{% if share.share_network_id %}
<dt>{% trans "Share network" %}</dt>
{% url 'horizon:admin:share_networks:share_network_detail' share.share_network_id as sn_url%}
<dd><a href="{{ sn_url }}">{{ share.share_network_id }}</a></dd>
{% endif %}
<dt>{% trans "Mount snapshot support" %}</dt>
<dd>{{ share.mount_snapshot_support }}</dd>
<dt>{% trans "Created" %}</dt>
<dd>{{ share.created_at|parse_date }}</dd>
<dt>{% trans "Host" %}</dt>
<dd>{{ share.host }}</dd>
<dt>{% trans "Task state" %}</dt>
<dd>{{ share.task_state }}</dd>
</dl>
</div>
<div class="status row-fluid detail">
<h4>{% trans "Access Rules" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
{% for rule in share.rules %}
<dt>{{ rule.access_type }}</dt>
<dd><p>
<div><b>Access to: </b>{{ rule.access_to }}</div>
<div><b>Access Level: </b>{{ rule.access_level }}</div>
<div><b>Status: </b>{{ rule.state }}</div>
<div><b>Access Key: </b>{{ rule.access_key }}</div>
</p></dd>
{% endfor %}
</dl>
</div>
<div class="status row-fluid detail">
<h4>{% trans "Metadata" %}</h4>
<hr class="header_rule">
<dl class="dl-horizontal">
{% for key, value in share.metadata.items %}
<dt>{{ key }}</dt>
<dd>{{ value }}</dd>
{% endfor %}
</dl>
</div>

View File

@ -1,33 +0,0 @@
{% load i18n sizeformat parse_date %}
<h3>{% trans "Snapshot Overview" %}</h3>
<div class="info row-fluid detail">
<h4>{% trans "Info" %}</h4>
<hr class="header_rule">
<dl>
<dt>{% trans "Name" %}</dt>
<dd>{{ snapshot.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ snapshot.id }}</dd>
<dt>{% trans "Source" %}</dt>
<dd>{{ snapshot.share_id }}</dd>
{% if snapshot.description %}
<dt>{% trans "Description" %}</dt>
<dd>{{ snapshot.description }}</dd>
{% endif %}
<dt>{% trans "Status" %}</dt>
<dd>{{ snapshot.status|capfirst }}</dd>
</dl>
</div>
<div class="specs row-fluid detail">
<h4>{% trans "Specs" %}</h4>
<hr class="header_rule">
<dl>
<dt>{% trans "Size" %}</dt>
<dd>{{ snapshot.size }} {% trans "GiB" %}</dd>
<dt>{% trans "Created" %}</dt>
<dd>{{ snapshot.created_at|parse_date }}</dd>
</dl>
</div>

View File

@ -5,7 +5,7 @@
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
{{ shares_table.render }}
</div>
</div>
{% endblock %}

View File

@ -10,75 +10,72 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import url # noqa
from django.conf import urls
from manila_ui.api import manila
from manila_ui.dashboards.admin.shares.replicas import views as replica_views
from manila_ui.dashboards.admin.shares import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<share_id>[^/]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^snapshots/(?P<snapshot_id>[^/]+)$',
views.SnapshotDetailView.as_view(),
name='snapshot-detail'),
url(r'^share_networks/(?P<share_network_id>[^/]+)$',
views.ShareNetworkDetailView.as_view(),
name='share_network_detail'),
url(r'^security_services/(?P<sec_service_id>[^/]+)$',
views.SecurityServiceDetailView.as_view(),
name='security_service_detail'),
url(r'^create_type$', views.CreateShareTypeView.as_view(),
name='create_type'),
url(r'^manage_share_type_access/(?P<share_type_id>[^/]+)$',
views.ManageShareTypeAccessView.as_view(),
name='manage_share_type_access'),
url(r'^update_type/(?P<share_type_id>[^/]+)/extra_specs$',
views.UpdateShareTypeView.as_view(),
name='update_type'),
url(r'^share_servers/(?P<share_server_id>[^/]+)$',
views.ShareServDetail.as_view(),
name='share_server_detail'),
url(r'^\?tab=share_tabs__share_servers_tab$', views.IndexView.as_view(),
name='share_servers_tab'),
url(r'^share_instances/(?P<share_instance_id>[^/]+)$',
views.ShareInstanceDetailView.as_view(),
name='share_instance_detail'),
url(r'^\?tab=share_tabs__share_instances_tab$', views.IndexView.as_view(),
name='share_instances_tab'),
url(r'^manage$', views.ManageShareView.as_view(), name='manage'),
url(r'^unmanage/(?P<share_id>[^/]+)$', views.UnmanageShareView.as_view(),
urls.url(
r'^$',
views.SharesView.as_view(),
name='index'),
urls.url(
r'^(?P<share_id>[^/]+)/$',
views.DetailView.as_view(),
name='detail'),
urls.url(
r'^manage$',
views.ManageShareView.as_view(),
name='manage'),
urls.url(
r'^unmanage/(?P<share_id>[^/]+)$',
views.UnmanageShareView.as_view(),
name='unmanage'),
]
if manila.is_replication_enabled():
urlpatterns.extend([
url(r'^(?P<share_id>[^/]+)/replicas/$',
urls.url(
r'^(?P<share_id>[^/]+)/replicas/$',
replica_views.ManageReplicasView.as_view(),
name='manage_replicas'),
url(r'^replica/(?P<replica_id>[^/]+)$',
urls.url(
r'^replica/(?P<replica_id>[^/]+)$',
replica_views.DetailReplicaView.as_view(),
name='replica_detail'),
url(r'^replica/(?P<replica_id>[^/]+)/resync_replica$',
urls.url(
r'^replica/(?P<replica_id>[^/]+)/resync_replica$',
replica_views.ResyncReplicaView.as_view(),
name='resync_replica'),
url(r'^replica/(?P<replica_id>[^/]+)/reset_replica_status$',
urls.url(
r'^replica/(?P<replica_id>[^/]+)/reset_replica_status$',
replica_views.ResetReplicaStatusView.as_view(),
name='reset_replica_status'),
url(r'^replica/(?P<replica_id>[^/]+)/reset_replica_state$',
urls.url(
r'^replica/(?P<replica_id>[^/]+)/reset_replica_state$',
replica_views.ResetReplicaStateView.as_view(),
name='reset_replica_state'),
])
if manila.is_migration_enabled():
urlpatterns.extend([
url(r'^migration_start/(?P<share_id>[^/]+)$',
views.MigrationStartView.as_view(), name='migration_start'),
url(r'^migration_complete/(?P<share_id>[^/]+)$',
views.MigrationCompleteView.as_view(), name='migration_complete'),
url(r'^migration_cancel/(?P<share_id>[^/]+)$',
views.MigrationCancelView.as_view(), name='migration_cancel'),
url(r'^migration_get_progress/(?P<share_id>[^/]+)$',
urls.url(
r'^migration_start/(?P<share_id>[^/]+)$',
views.MigrationStartView.as_view(),
name='migration_start'),
urls.url(
r'^migration_complete/(?P<share_id>[^/]+)$',
views.MigrationCompleteView.as_view(),
name='migration_complete'),
urls.url(
r'^migration_cancel/(?P<share_id>[^/]+)$',
views.MigrationCancelView.as_view(),
name='migration_cancel'),
urls.url(
r'^migration_get_progress/(?P<share_id>[^/]+)$',
views.MigrationGetProgressView.as_view(),
name='migration_get_progress'),
])

View File

@ -19,37 +19,54 @@ Admin views for managing shares.
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import tabs
from horizon import tables
from horizon.utils import memoized
from horizon import workflows
from manila_ui.api import manila
from manila_ui.dashboards.admin.shares import forms as project_forms
from manila_ui.dashboards.admin.shares import tabs as project_tabs
import manila_ui.dashboards.admin.shares.workflows as share_workflows
from manila_ui.dashboards.project.shares.security_services import \
views as ss_views
from manila_ui.dashboards.project.shares.share_networks import \
views as sn_views
from manila_ui.dashboards.admin.shares import tables as s_tables
from manila_ui.dashboards.admin.shares import tabs as s_tabs
from manila_ui.dashboards.admin import utils
from manila_ui.dashboards.project.shares.shares import views as share_views
from manila_ui.dashboards.project.shares.snapshots import \
views as snapshot_views
from manila_ui.dashboards import utils as ui_utils
from manila_ui.utils import filters
filters = (filters.get_item,)
class IndexView(tabs.TabbedTableView, share_views.ShareTableMixIn):
tab_group_class = project_tabs.ShareTabs
class SharesView(tables.MultiTableView, share_views.ShareTableMixIn):
table_classes = (
s_tables.SharesTable,
)
template_name = "admin/shares/index.html"
page_title = _("Shares")
@memoized.memoized_method
def get_shares_data(self):
shares = []
try:
shares = manila.share_list(
self.request, search_opts={'all_tenants': True})
snapshots = manila.share_snapshot_list(
self.request, detailed=True, search_opts={'all_tenants': True})
share_ids_with_snapshots = []
for snapshot in snapshots:
share_ids_with_snapshots.append(snapshot.to_dict()['share_id'])
for share in shares:
if share.to_dict()['id'] in share_ids_with_snapshots:
setattr(share, 'has_snapshot', True)
else:
setattr(share, 'has_snapshot', False)
except Exception:
exceptions.handle(
self.request, _('Unable to retrieve share list.'))
# Gather our projects to correlate against IDs
utils.set_project_name_to_objects(self.request, shares)
return shares
class DetailView(share_views.DetailView):
tab_group_class = s_tabs.ShareDetailTabs
template_name = "admin/shares/detail.html"
def get_context_data(self, **kwargs):
@ -59,18 +76,6 @@ class DetailView(share_views.DetailView):
return context
class SecurityServiceDetailView(ss_views.Detail):
redirect_url = reverse_lazy('horizon:admin:shares:index')
class ShareNetworkDetailView(sn_views.Detail):
redirect_url = reverse_lazy('horizon:admin:shares:index')
class SnapshotDetailView(snapshot_views.SnapshotDetailView):
redirect_url = reverse_lazy('horizon:admin:shares:index')
class ManageShareView(forms.ModalFormView):
form_class = project_forms.ManageShare
template_name = 'admin/shares/manage_share.html'
@ -271,151 +276,3 @@ class UnmanageShareView(forms.ModalFormView):
'name': share.name,
'host': getattr(share, "host"),
}
class CreateShareTypeView(forms.ModalFormView):
form_class = project_forms.CreateShareType
form_id = "create_share_type"
template_name = 'admin/shares/create_share_type.html'
modal_header = _("Create Share Type")
submit_label = _("Create")
submit_url = reverse_lazy("horizon:admin:shares:create_type")
success_url = 'horizon:admin:shares:index'
page_title = _("Create Share Type")
def get_success_url(self):
return reverse(self.success_url)
class ManageShareTypeAccessView(workflows.WorkflowView):
workflow_class = share_workflows.ManageShareTypeAccessWorkflow
template_name = "admin/shares/manage_share_type_access.html"
success_url = 'horizon:project:shares:index'
page_title = _("Manage Share Type Access")
def get_initial(self):
return {'id': self.kwargs["share_type_id"]}
def get_context_data(self, **kwargs):
context = super(ManageShareTypeAccessView, self).get_context_data(
**kwargs)
context['id'] = self.kwargs['share_type_id']
return context
class UpdateShareTypeView(forms.ModalFormView):
form_class = project_forms.UpdateShareType
form_id = "update_share_type"
template_name = "admin/shares/update_share_type.html"
modal_header = _("Update Share Type")
modal_id = "update_share_type_modal"
submit_label = _("Update")
submit_url = "horizon:admin:shares:update_type"
success_url = reverse_lazy("horizon:admin:shares:index")
page_title = _("Update Share Type")
def get_object(self):
if not hasattr(self, "_object"):
st_id = self.kwargs["share_type_id"]
try:
self._object = manila.share_type_get(self.request, st_id)
except Exception:
msg = _("Unable to retrieve share_type.")
url = reverse("horizon:admin:shares:index")
exceptions.handle(self.request, msg, redirect=url)
return self._object
def get_context_data(self, **kwargs):
context = super(UpdateShareTypeView, self).get_context_data(**kwargs)
args = (self.get_object().id,)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
def get_initial(self):
share_type = self.get_object()
return {
"id": self.kwargs["share_type_id"],
"name": share_type.name,
"extra_specs": share_type.extra_specs,
}
class ShareServDetail(tabs.TabView):
tab_group_class = project_tabs.ShareServerDetailTabs
template_name = 'admin/shares/detail_share_server.html'
def get_context_data(self, **kwargs):
context = super(ShareServDetail, self).get_context_data(**kwargs)
share_server = self.get_data()
share_server_display_name = share_server.id
context["share_server"] = share_server
context["share_server_display_name"] = share_server_display_name
context["page_title"] = _("Share Server Details: %(server_name)s") % {
'server_name': share_server_display_name}
return context
@memoized.memoized_method
def get_data(self):
try:
share_serv_id = self.kwargs['share_server_id']
share_serv = manila.share_server_get(self.request, share_serv_id)
share_search_opts = {'share_server_id': share_serv.id}
shares_list = manila.share_list(self.request,
search_opts=share_search_opts)
for share in shares_list:
share.name_or_id = share.name or share.id
share_serv.shares_list = shares_list
if not hasattr(share_serv, 'share_network_id'):
share_serv.share_network_id = None
except Exception:
redirect = reverse('horizon:admin:shares:index')
exceptions.handle(self.request,
_('Unable to retrieve share server details.'),
redirect=redirect)
return share_serv
def get_tabs(self, request, *args, **kwargs):
share_server = self.get_data()
return self.tab_group_class(request, share_server=share_server,
**kwargs)
class ShareInstanceDetailView(tabs.TabView):
tab_group_class = project_tabs.ShareInstanceDetailTabs
template_name = 'admin/shares/share_instance_detail.html'
def get_context_data(self, **kwargs):
context = super(self.__class__, self).get_context_data(**kwargs)
share_instance = self.get_data()
context["share_instance"] = share_instance
context["page_title"] = (
_("Share Instance Details: %s") % share_instance.id)
return context
@memoized.memoized_method
def get_data(self):
try:
share_instance_id = self.kwargs['share_instance_id']
share_instance = manila.share_instance_get(
self.request, share_instance_id)
share_instance.export_locations = (
manila.share_instance_export_location_list(
self.request, share_instance_id))
export_locations = [
exp['path'] for exp in share_instance.export_locations
]
share_instance.el_size = ui_utils.calculate_longest_str_size(
export_locations)
return share_instance
except Exception:
redirect = reverse('horizon:admin:shares:index')
exceptions.handle(
self.request,
_('Unable to retrieve share instance details.'),
redirect=redirect)
def get_tabs(self, request, *args, **kwargs):
share_instance = self.get_data()
return self.tab_group_class(
request, share_instance=share_instance, **kwargs)

View File

@ -13,16 +13,16 @@
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.project import dashboard
class Shares(horizon.Panel):
name = _("Shares")
slug = 'shares'
permissions = ('openstack.services.share', )
permissions = (
'openstack.services.share',
)
dashboard.Project.register(Shares)

View File

@ -132,7 +132,7 @@ class NovaShareNetworkTable(tables.DataTable):
Delete)
class NeutronShareNetworkTable(tables.DataTable):
class ShareNetworksTable(tables.DataTable):
STATUS_CHOICES = (
("ACTIVE", True),
("INACTIVE", True),

View File

@ -32,7 +32,7 @@ class ShareNetworkTab(tabs.TableTab):
def __init__(self, tab_group, request):
if base.is_service_enabled(request, 'network'):
self.table_classes = (share_net_tables.NeutronShareNetworkTable,)
self.table_classes = (share_net_tables.ShareNetworksTable,)
else:
self.table_classes = (share_net_tables.NovaShareNetworkTable,)
super(ShareNetworkTab, self).__init__(tab_group, request)

View File

@ -0,0 +1,6 @@
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
PANEL_GROUP = 'share'
# The display name of the PANEL_GROUP. Required.
PANEL_GROUP_NAME = 'Share'
# The slug of the dashboard the PANEL_GROUP associated with. Required.
PANEL_GROUP_DASHBOARD = 'admin'

View File

@ -1,4 +1,4 @@
# Copyright 2016 Mirantis Inc.
# Copyright 2017 Mirantis Inc.
#
# 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
@ -13,6 +13,6 @@
# under the License.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'admin'
PANEL_GROUP = 'share'
PANEL = 'shares'
ADD_PANEL = 'manila_ui.dashboards.admin.shares.panel.Shares'

View File

@ -0,0 +1,18 @@
# Copyright 2017 Mirantis Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'share_snapshots'
ADD_PANEL = 'manila_ui.dashboards.admin.share_snapshots.panel.ShareSnapshots'

View File

@ -0,0 +1,18 @@
# Copyright 2017 Mirantis Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'share_types'
ADD_PANEL = 'manila_ui.dashboards.admin.share_types.panel.ShareTypes'

View File

@ -0,0 +1,18 @@
# Copyright 2017 Mirantis Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'share_networks'
ADD_PANEL = 'manila_ui.dashboards.admin.share_networks.panel.ShareNetworks'

View File

@ -0,0 +1,19 @@
# Copyright 2017 Mirantis Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'security_services'
ADD_PANEL = (
'manila_ui.dashboards.admin.security_services.panel.SecurityServices')

View File

@ -0,0 +1,18 @@
# Copyright 2017 Mirantis Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'share_servers'
ADD_PANEL = 'manila_ui.dashboards.admin.share_servers.panel.ShareServers'

View File

@ -0,0 +1,18 @@
# Copyright 2017 Mirantis Inc.
#
# 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.
PANEL_DASHBOARD = 'admin'
PANEL_GROUP = 'share'
PANEL = 'share_instances'
ADD_PANEL = 'manila_ui.dashboards.admin.share_instances.panel.ShareInstances'

View File

@ -0,0 +1,95 @@
# Copyright (c) 2014 NetApp, Inc.
# Copyright (c) 2015 Mirantis, Inc.
#
# 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.core.urlresolvers import reverse
from horizon import exceptions as horizon_exceptions
import mock
from openstack_dashboard.api import keystone as api_keystone
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.admin import utils
from manila_ui.tests.dashboards.project.shares import test_data
from manila_ui.tests import helpers as test
from manila_ui.tests.test_data import keystone_data
INDEX_URL = reverse('horizon:admin:security_services:index')
class SecurityServicesTests(test.BaseAdminViewTests):
def setUp(self):
super(self.__class__, self).setUp()
self.mock_object(
api_keystone, "tenant_list",
mock.Mock(return_value=(keystone_data.projects, None)))
# Reset taken list of projects to avoid test interference
utils.PROJECTS = {}
def test_detail_view(self):
sec_service = test_data.sec_service
self.mock_object(
api_manila, "security_service_get",
mock.Mock(return_value=sec_service))
url = reverse(
'horizon:admin:security_services:security_service_detail',
args=[sec_service.id])
res = self.client.get(url)
self.assertContains(res, "<h1>Security Service Details: %s</h1>"
% sec_service.name,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % sec_service.name, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % sec_service.id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % sec_service.user, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % sec_service.server, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % sec_service.dns_ip, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % sec_service.domain, 1, 200)
self.assertNoMessages()
api_manila.security_service_get.assert_called_once_with(
mock.ANY, sec_service.id)
def test_detail_view_with_exception(self):
url = reverse(
'horizon:admin:security_services:security_service_detail',
args=[test_data.sec_service.id])
self.mock_object(
api_manila, "security_service_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.security_service_get.assert_called_once_with(
mock.ANY, test_data.sec_service.id)
def test_delete_security_service(self):
security_service = test_data.sec_service
formData = {
'action': 'security_services__delete__%s' % security_service.id,
}
self.mock_object(api_manila, "security_service_delete")
self.mock_object(
api_manila, "security_service_list",
mock.Mock(return_value=[test_data.sec_service]))
res = self.client.post(INDEX_URL, formData)
api_keystone.tenant_list.assert_called_once_with(mock.ANY)
api_manila.security_service_delete.assert_called_once_with(
mock.ANY, test_data.sec_service.id)
api_manila.security_service_list.assert_called_once_with(
mock.ANY, search_opts={'all_tenants': True})
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -0,0 +1,127 @@
# Copyright (c) 2014 NetApp, Inc.
# Copyright (c) 2015 Mirantis, Inc.
#
# 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.core.urlresolvers import reverse
from horizon import exceptions as horizon_exceptions
import mock
from openstack_dashboard.api import keystone as api_keystone
from manila_ui.api import manila as api_manila
from manila_ui.tests.dashboards.project.shares import test_data
from manila_ui.tests import helpers as test
INDEX_URL = reverse('horizon:admin:share_instances:index')
class ShareInstanceTests(test.BaseAdminViewTests):
def test_list_share_instances(self):
share_instances = [
test_data.share_instance,
test_data.share_instance_no_ss,
]
self.mock_object(
api_manila, "share_instance_list",
mock.Mock(return_value=share_instances))
self.mock_object(
api_keystone, "tenant_list", mock.Mock(return_value=([], None)))
res = self.client.get(INDEX_URL)
self.assertContains(res, "<h1>Share Instances</h1>")
self.assertContains(
res,
'<a href="/admin/share_servers/%s" >%s</a>' % (
share_instances[0].share_server_id,
share_instances[0].share_server_id),
1, 200)
self.assertContains(
res,
'<a href="/admin/share_networks/%s" >%s</a>' % (
share_instances[0].share_network_id,
share_instances[0].share_network_id),
1, 200)
for si in share_instances:
self.assertContains(
res, '<a href="/admin/share_instances/%s" >%s</a>' % (
si.id, si.id))
self.assertContains(res, si.host)
self.assertContains(res, si.availability_zone)
self.assertContains(
res,
'<a href="/admin/shares/%s/" >%s</a>' % (
si.share_id, si.share_id),
1, 200)
api_manila.share_instance_list.assert_called_once_with(mock.ANY)
self.assertEqual(0, api_keystone.tenant_list.call_count)
def test_detail_view_share_instance(self):
share_instance = test_data.share_instance
share_id = share_instance.share_id
ss_id = share_instance.share_server_id
url = reverse('horizon:admin:share_instances:share_instance_detail',
args=[share_instance.id])
self.mock_object(
api_manila, "share_instance_get",
mock.Mock(return_value=share_instance))
self.mock_object(
api_manila, "share_instance_export_location_list",
mock.Mock(return_value=test_data.export_locations))
res = self.client.get(url)
self.assertContains(
res, "<h1>Share Instance Details: %s</h1>" % share_instance.id,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_instance.id, 1, 200)
self.assertContains(res, "<dd>Available</dd>", 1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_instance.host, 1, 200)
self.assertContains(
res, "<dd>%s</dd>" % share_instance.availability_zone, 1, 200)
self.assertContains(
res,
"<dd><a href=\"/admin/shares/%s/\">%s</a></dd>" % (
share_id, share_id),
1, 200)
self.assertContains(
res,
"<dd><a href=\"/admin/share_networks/%s\">%s</a></dd>" % (
share_instance.share_network_id,
share_instance.share_network_id),
1, 200)
self.assertContains(
res,
"<dd><a href=\"/admin/share_servers/%s\">%s</a></dd>" % (
ss_id, ss_id),
1, 200)
self.assertNoMessages()
api_manila.share_instance_get.assert_called_once_with(
mock.ANY, share_instance.id)
api_manila.share_instance_export_location_list.assert_called_once_with(
mock.ANY, share_instance.id)
def test_detail_view_share_instance_with_exception(self):
share_instance = test_data.share_instance
url = reverse('horizon:admin:share_instances:share_instance_detail',
args=[share_instance.id])
self.mock_object(
api_manila, "share_instance_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.share_instance_get.assert_called_once_with(
mock.ANY, share_instance.id)

View File

@ -0,0 +1,166 @@
# Copyright (c) 2014 NetApp, Inc.
# Copyright (c) 2015 Mirantis, Inc.
#
# 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.core.urlresolvers import reverse
from horizon import exceptions as horizon_exceptions
import mock
from neutronclient.client import exceptions
from openstack_dashboard.api import keystone as api_keystone
from openstack_dashboard.api import neutron as api_neutron
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.admin import utils
from manila_ui.tests.dashboards.project.shares import test_data
from manila_ui.tests import helpers as test
from manila_ui.tests.test_data import keystone_data
INDEX_URL = reverse('horizon:admin:share_networks:index')
class ShareNetworksTests(test.BaseAdminViewTests):
def setUp(self):
super(self.__class__, self).setUp()
self.mock_object(
api_keystone, "tenant_list",
mock.Mock(return_value=(keystone_data.projects, None)))
# Reset taken list of projects to avoid test interference
utils.PROJECTS = {}
def test_detail_view(self):
share_net = test_data.active_share_network
sec_service = test_data.sec_service
self.mock_object(
api_manila, "share_server_list", mock.Mock(return_value=[]))
self.mock_object(
api_manila, "share_network_get", mock.Mock(return_value=share_net))
self.mock_object(
api_manila, "share_network_security_service_list",
mock.Mock(return_value=[sec_service]))
network = self.networks.first()
subnet = self.subnets.first()
self.mock_object(
api_neutron, "network_get", mock.Mock(return_value=network))
self.mock_object(
api_neutron, "subnet_get", mock.Mock(return_value=subnet))
url = reverse('horizon:admin:share_networks:share_network_detail',
args=[share_net.id])
res = self.client.get(url)
self.assertContains(res, "<h1>Share Network Details: %s</h1>"
% share_net.name,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_net.name, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_net.id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % network.name_or_id, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % subnet.name_or_id, 1, 200)
self.assertContains(res, "<a href=\"/admin/security_services"
"/%s\">%s</a>" % (sec_service.id,
sec_service.name), 1, 200)
self.assertNoMessages()
api_manila.share_network_security_service_list.assert_called_once_with(
mock.ANY, share_net.id)
api_manila.share_server_list.assert_called_once_with(
mock.ANY, search_opts={'share_network_id': share_net.id})
api_manila.share_network_get.assert_called_once_with(
mock.ANY, share_net.id)
api_neutron.network_get.assert_called_once_with(
mock.ANY, share_net.neutron_net_id)
api_neutron.subnet_get.assert_called_once_with(
mock.ANY, share_net.neutron_subnet_id)
def test_detail_view_network_not_found(self):
share_net = test_data.active_share_network
sec_service = test_data.sec_service
url = reverse('horizon:admin:share_networks:share_network_detail',
args=[share_net.id])
self.mock_object(
api_manila, "share_server_list", mock.Mock(return_value=[]))
self.mock_object(
api_manila, "share_network_get", mock.Mock(return_value=share_net))
self.mock_object(
api_manila, "share_network_security_service_list",
mock.Mock(return_value=[sec_service]))
self.mock_object(
api_neutron, "network_get", mock.Mock(
side_effect=exceptions.NeutronClientException('fake', 500)))
self.mock_object(
api_neutron, "subnet_get", mock.Mock(
side_effect=exceptions.NeutronClientException('fake', 500)))
res = self.client.get(url)
self.assertContains(res, "<h1>Share Network Details: %s</h1>"
% share_net.name,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_net.name, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_net.id, 1, 200)
self.assertContains(res, "<dd>Unknown</dd>", 2, 200)
self.assertNotContains(res, "<dd>%s</dd>" % share_net.neutron_net_id)
self.assertNotContains(res,
"<dd>%s</dd>" % share_net.neutron_subnet_id)
self.assertContains(res, "<a href=\"/admin/security_services"
"/%s\">%s</a>" % (sec_service.id,
sec_service.name), 1, 200)
self.assertNoMessages()
api_manila.share_network_security_service_list.assert_called_once_with(
mock.ANY, share_net.id)
api_manila.share_server_list.assert_called_once_with(
mock.ANY, search_opts={'share_network_id': share_net.id})
api_manila.share_network_get.assert_called_once_with(
mock.ANY, share_net.id)
api_neutron.network_get.assert_called_once_with(
mock.ANY, share_net.neutron_net_id)
api_neutron.subnet_get.assert_called_once_with(
mock.ANY, share_net.neutron_subnet_id)
def test_detail_view_with_exception(self):
url = reverse('horizon:admin:share_networks:share_network_detail',
args=[test_data.active_share_network.id])
self.mock_object(
api_manila, "share_network_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.share_network_get.assert_called_once_with(
mock.ANY, test_data.active_share_network.id)
def test_delete_share_network(self):
share_network = test_data.inactive_share_network
formData = {'action': 'share_networks__delete__%s' % share_network.id}
self.mock_object(
api_neutron, "network_list", mock.Mock(return_value=[]))
self.mock_object(
api_neutron, "subnet_list", mock.Mock(return_value=[]))
self.mock_object(api_manila, "share_network_delete")
self.mock_object(
api_manila, "share_network_list",
mock.Mock(return_value=[
test_data.active_share_network,
test_data.inactive_share_network]))
res = self.client.post(INDEX_URL, formData)
api_keystone.tenant_list.assert_called_once_with(mock.ANY)
api_manila.share_network_delete.assert_called_once_with(
mock.ANY, test_data.inactive_share_network.id)
api_manila.share_network_list.assert_called_once_with(
mock.ANY, detailed=True, search_opts={'all_tenants': True})
api_neutron.network_list.assert_called_once_with(mock.ANY)
api_neutron.subnet_list.assert_called_once_with(mock.ANY)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -0,0 +1,135 @@
# Copyright (c) 2014 NetApp, Inc.
# Copyright (c) 2015 Mirantis, Inc.
#
# 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.core.urlresolvers import reverse
from horizon import exceptions as horizon_exceptions
import mock
from openstack_dashboard.api import keystone as api_keystone
from manila_ui.api import manila as api_manila
from manila_ui.tests.dashboards.project.shares import test_data
from manila_ui.tests import helpers as test
INDEX_URL = reverse('horizon:admin:share_servers:index')
class ShareServerTests(test.BaseAdminViewTests):
def test_list_share_servers(self):
share_servers = [
test_data.share_server,
test_data.share_server_errored,
]
projects = [
type('FakeProject', (object, ),
{'id': s.project_id, 'name': '%s_name' % s.project_id})
for s in share_servers
]
projects_dict = {p.id: p for p in projects}
self.mock_object(
api_manila, "share_server_list",
mock.Mock(return_value=share_servers))
self.mock_object(
api_manila, "share_list",
mock.Mock(side_effect=[
[], [test_data.share], [test_data.nameless_share]]))
self.mock_object(
api_keystone, "tenant_list",
mock.Mock(return_value=(projects, None)))
res = self.client.get(INDEX_URL)
self.assertContains(res, "<h1>Share Servers</h1>")
for share_server in share_servers:
self.assertContains(
res,
'<a href="/admin/share_servers/%s" >%s</a>' % (
share_server.id, share_server.id),
1, 200)
self.assertContains(res, share_server.host, 1, 200)
self.assertContains(
res, projects_dict[share_server.project_id].name, 1, 200)
self.assertContains(
res,
'<a href="/admin/share_networks/%s" >%s</a>' % (
share_server.share_network_id,
share_server.share_network),
1, 200)
api_manila.share_list.assert_has_calls([
mock.call(
mock.ANY, search_opts={'share_server_id': share_server.id})
for share_server in share_servers
])
api_manila.share_server_list.assert_called_once_with(mock.ANY)
self.assertEqual(1, api_keystone.tenant_list.call_count)
def test_detail_view_share_server(self):
share_server = test_data.share_server
shares = [test_data.share, test_data.nameless_share]
url = reverse(
'horizon:admin:share_servers:share_server_detail',
args=[share_server.id])
self.mock_object(
api_manila, "share_server_get",
mock.Mock(return_value=share_server))
self.mock_object(
api_manila, "share_list", mock.Mock(return_value=shares))
res = self.client.get(url)
self.assertContains(
res, "<h1>Share Server Details: %s</h1>" % share_server.id,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_server.id, 1, 200)
self.assertContains(res, "<dd>Active</dd>", 1, 200)
self.assertContains(res, "<dd>%s</dd>" % share_server.host, 1, 200)
self.assertContains(
res,
"<dd><a href=\"/admin/share_networks/%s\">%s</a></dd>" % (
share_server.share_network_id,
share_server.share_network_name),
1, 200)
self.assertContains(
res,
"<dd><a href=\"/admin/shares/%s/\">%s</a></dd>" % (
shares[0].id, shares[0].name),
1, 200)
self.assertContains(
res,
"<dd><a href=\"/admin/shares/%s/\">%s</a></dd>" % (
shares[1].id, shares[1].id),
1, 200)
for k, v in share_server.backend_details.items():
self.assertContains(res, "<dt>%s</dt>" % k)
self.assertContains(res, "<dd>%s</dd>" % v)
self.assertNoMessages()
api_manila.share_server_get.assert_called_once_with(
mock.ANY, share_server.id)
api_manila.share_list.assert_called_once_with(
mock.ANY, search_opts={"share_server_id": share_server.id})
def test_detail_view_share_server_with_exception(self):
share_server = test_data.share_server
url = reverse('horizon:admin:share_servers:share_server_detail',
args=[share_server.id])
self.mock_object(
api_manila, "share_server_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.share_server_get.assert_called_once_with(
mock.ANY, share_server.id)

View File

@ -0,0 +1,151 @@
# Copyright (c) 2014 NetApp, Inc.
# Copyright (c) 2015 Mirantis, Inc.
#
# 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.core.urlresolvers import reverse
from horizon import exceptions as horizon_exceptions
import mock
from openstack_dashboard.api import keystone as api_keystone
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.admin import utils
from manila_ui.tests.dashboards.project.shares import test_data
from manila_ui.tests import helpers as test
from manila_ui.tests.test_data import keystone_data
INDEX_URL = reverse('horizon:admin:share_snapshots:index')
class SnapshotsTests(test.BaseAdminViewTests):
def setUp(self):
super(self.__class__, self).setUp()
self.mock_object(
api_keystone, "tenant_list",
mock.Mock(return_value=(keystone_data.projects, None)))
# Reset taken list of projects to avoid test interference
utils.PROJECTS = {}
def test_detail_view(self):
snapshot = test_data.snapshot
share = test_data.share
url = reverse('horizon:admin:share_snapshots:share_snapshot_detail',
args=[snapshot.id])
self.mock_object(
api_manila, "share_snapshot_get", mock.Mock(return_value=snapshot))
self.mock_object(
api_manila, "share_get", mock.Mock(return_value=share))
res = self.client.get(url)
self.assertContains(res, "<h1>Snapshot Details: %s</h1>"
% snapshot.name,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % snapshot.name, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % snapshot.id, 1, 200)
self.assertContains(res,
"<dd><a href=\"/admin/shares/%s/\">%s</a></dd>" %
(snapshot.share_id, share.name), 1, 200)
self.assertContains(res, "<dd>%s GiB</dd>" % snapshot.size, 1, 200)
self.assertNoMessages()
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
api_manila.share_snapshot_get.assert_called_once_with(
mock.ANY, snapshot.id)
def test_detail_view_with_mount_support(self):
snapshot = test_data.snapshot_mount_support
rules = [test_data.ip_rule, test_data.user_rule, test_data.cephx_rule]
export_locations = test_data.admin_snapshot_export_locations
share = test_data.share_mount_snapshot
url = reverse('horizon:admin:share_snapshots:share_snapshot_detail',
args=[snapshot.id])
self.mock_object(
api_manila, "share_snapshot_get", mock.Mock(return_value=snapshot))
self.mock_object(
api_manila, "share_snapshot_rules_list", mock.Mock(
return_value=rules))
self.mock_object(
api_manila, "share_snap_export_location_list", mock.Mock(
return_value=export_locations))
self.mock_object(
api_manila, "share_get", mock.Mock(return_value=share))
res = self.client.get(url)
self.assertContains(res, "<h1>Snapshot Details: %s</h1>"
% snapshot.name,
1, 200)
self.assertContains(res, "<dd>%s</dd>" % snapshot.name, 1, 200)
self.assertContains(res, "<dd>%s</dd>" % snapshot.id, 1, 200)
self.assertContains(res,
"<dd><a href=\"/admin/shares/%s/\">%s</a></dd>" %
(snapshot.share_id, share.name), 1, 200)
self.assertContains(res, "<dd>%s GiB</dd>" % snapshot.size, 1, 200)
for el in export_locations:
self.assertContains(res, "value=\"%s\"" % el.path, 1, 200)
self.assertContains(
res, "<div><b>Is admin only:</b> %s</div>" % el.is_admin_only,
1, 200)
self.assertContains(
res, ("<div><b>Snapshot Replica ID:</b> %s</div>" %
el.share_snapshot_instance_id), 1, 200)
for rule in rules:
self.assertContains(res, "<dt>%s</dt>" % rule.access_type, 1, 200)
self.assertContains(
res, "<div><b>Access to: </b>%s</div>" % rule.access_to,
1, 200)
self.assertContains(
res, "<div><b>Status: </b>active</div>", len(rules), 200)
self.assertNoMessages()
api_manila.share_get.assert_called_once_with(mock.ANY, share.id)
api_manila.share_snapshot_get.assert_called_once_with(
mock.ANY, snapshot.id)
api_manila.share_snapshot_rules_list.assert_called_once_with(
mock.ANY, snapshot.id)
api_manila.share_snap_export_location_list.assert_called_once_with(
mock.ANY, snapshot)
def test_detail_view_with_exception(self):
url = reverse('horizon:admin:share_snapshots:share_snapshot_detail',
args=[test_data.snapshot.id])
self.mock_object(
api_manila, "share_snapshot_get",
mock.Mock(side_effect=horizon_exceptions.NotFound(404)))
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
api_manila.share_snapshot_get.assert_called_once_with(
mock.ANY, test_data.snapshot.id)
def test_delete_snapshot(self):
share = test_data.share
snapshot = test_data.snapshot
formData = {'action': 'share_snapshots__delete__%s' % snapshot.id}
self.mock_object(api_manila, "share_snapshot_delete")
self.mock_object(
api_manila, "share_snapshot_list",
mock.Mock(return_value=[snapshot]))
self.mock_object(
api_manila, "share_list", mock.Mock(return_value=[share]))
res = self.client.post(INDEX_URL, formData)
api_keystone.tenant_list.assert_called_once_with(mock.ANY)
api_manila.share_snapshot_delete.assert_called_once_with(
mock.ANY, test_data.snapshot.id)
api_manila.share_snapshot_list.assert_called_once_with(
mock.ANY, search_opts={'all_tenants': True})
api_manila.share_list.assert_called_once_with(mock.ANY)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -0,0 +1,254 @@
# Copyright (c) 2015 Mirantis, 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.
import ddt
from django.core.handlers import wsgi
from django import forms as django_forms
from horizon import forms as horizon_forms
import mock
from manila_ui.dashboards.admin.share_types import forms
from manila_ui.tests import helpers as base
@ddt.ddt
class ManilaDashboardsAdminSharesUpdateShareTypeFormTests(base.APITestCase):
def setUp(self):
super(self.__class__, self).setUp()
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
def _get_form(self, initial):
kwargs = {
'prefix': None,
'initial': initial,
}
return forms.UpdateShareType(self.request, **kwargs)
@ddt.data(
({}, []),
({'foo': 'bar', 'quuz': 'zaab'}, ["foo=bar\r\n", "quuz=zaab\r\n"]),
)
@ddt.unpack
def test___init__(self, extra_specs_dict_input, extra_specs_str_output):
form = self._get_form({'extra_specs': extra_specs_dict_input})
for expected_extra_spec in extra_specs_str_output:
self.assertIn(expected_extra_spec, form.initial['extra_specs'])
self.assertIn('extra_specs', list(form.fields.keys()))
self.assertTrue(
isinstance(form.fields['extra_specs'], horizon_forms.CharField))
@mock.patch('horizon.messages.success')
def test_handle_success_no_changes(self, mock_horizon_messages_success):
initial = {'id': 'fake_id', 'name': 'fake_name', 'extra_specs': {}}
form = self._get_form(initial)
data = {'extra_specs': ''}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
@mock.patch('horizon.messages.success')
def test_handle_success_only_set(self, mock_horizon_messages_success):
initial = {
'id': 'fake_id',
'name': 'fake_name',
'extra_specs': {'foo': 'bar'}
}
form = self._get_form(initial)
data = {'extra_specs': 'foo=bar\r\n'}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
self.manilaclient.share_types.get.assert_called_once_with(
initial['id'])
self.manilaclient.share_types.get.return_value.set_keys.\
assert_called_once_with({'foo': 'bar'})
self.assertFalse(
self.manilaclient.share_types.get.return_value.unset_keys.called)
@mock.patch('horizon.messages.success')
def test_handle_success_only_unset(self, mock_horizon_messages_success):
initial = {
'id': 'fake_id',
'name': 'fake_name',
'extra_specs': {'foo': 'bar'}
}
form = self._get_form(initial)
data = {'extra_specs': 'foo\r\n'}
share_types_get = self.manilaclient.share_types.get
share_types_get.return_value.get_keys.return_value = {
'foo': 'bar', 'quuz': 'zaab'}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
self.manilaclient.share_types.get.assert_has_calls([
mock.call(initial['id'])])
share_types_get.return_value.get_keys.assert_called_once_with()
self.assertFalse(share_types_get.return_value.set_keys.called)
share_types_get.return_value.unset_keys.assert_called_once_with(
{'foo'})
@mock.patch('horizon.messages.success')
def test_handle_success_set_and_unset(self, mock_horizon_messages_success):
initial = {
'id': 'fake_id',
'name': 'fake_name',
'extra_specs': {'foo': 'bar'}
}
form = self._get_form(initial)
data = {'extra_specs': 'foo\r\nquuz=zaab'}
share_types_get = self.manilaclient.share_types.get
share_types_get.return_value.get_keys.return_value = {'foo': 'bar'}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
self.manilaclient.share_types.get.assert_has_calls([
mock.call(initial['id'])])
share_types_get.return_value.get_keys.assert_called_once_with()
share_types_get.return_value.set_keys.assert_called_once_with(
{'quuz': 'zaab'})
share_types_get.return_value.unset_keys.assert_called_once_with(
{'foo'})
def test_handle_validation_error(self):
initial = {'id': 'fake_id', 'name': 'fake_name', 'extra_specs': {}}
form = self._get_form(initial)
form.api_error = mock.Mock()
data = {'extra_specs': 'a b'}
result = form.handle(self.request, data)
self.assertFalse(result)
form.api_error.assert_called_once_with(mock.ANY)
@mock.patch('horizon.exceptions.handle')
def test_handle_other_exception(self, mock_horizon_exceptions_handle):
django_forms.ValidationError
initial = {'id': 'fake_id', 'name': 'fake_name', 'extra_specs': {}}
form = self._get_form(initial)
data = {'extra_specs': None}
result = form.handle(self.request, data)
self.assertFalse(result)
mock_horizon_exceptions_handle.assert_called_once_with(
self.request, mock.ANY)
@ddt.ddt
class ManilaDashboardsAdminSharesCreateShareTypeFormTests(base.APITestCase):
def setUp(self):
super(self.__class__, self).setUp()
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
def _get_form(self, **kwargs):
return forms.CreateShareType(self.request, **kwargs)
@mock.patch('horizon.messages.success')
def test_create_share_type(self, mock_horizon_messages_success):
form = self._get_form()
data = {
'extra_specs': '',
'is_public': False,
'spec_driver_handles_share_servers': 'True',
'name': 'share',
}
result = form.handle(self.request, data)
self.assertTrue(result)
self.manilaclient.share_types.create.assert_called_once_with(
name=data['name'],
spec_driver_handles_share_servers='true',
spec_snapshot_support=True,
is_public=data["is_public"])
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@mock.patch('horizon.messages.success')
def test_create_share_type_with_extra_specs(self,
mock_horizon_messages_success):
form = self._get_form()
data = {'extra_specs': 'a=b \n c=d',
'is_public': False,
'spec_driver_handles_share_servers': 'True',
'name': 'share'}
result = form.handle(self.request, data)
self.assertTrue(result)
set_keys = self.manilaclient.share_types.get.return_value.set_keys
set_keys.assert_called_once_with(
{'a': 'b', 'c': 'd'})
self.manilaclient.share_types.create.assert_called_once_with(
name=data['name'],
spec_driver_handles_share_servers='true',
spec_snapshot_support=True,
is_public=data["is_public"])
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@ddt.data(True, False)
@mock.patch('horizon.messages.success')
def test_public_share_type_creation(self,
enable_public_share_type_creation,
mock_horizon_messages_success):
with self.settings(OPENSTACK_MANILA_FEATURES={
'enable_public_share_type_creation':
enable_public_share_type_creation}):
form = self._get_form()
data = {
'extra_specs': '',
'is_public': enable_public_share_type_creation,
'spec_driver_handles_share_servers': 'True',
'name': 'share',
}
result = form.handle(self.request, data)
self.assertTrue(result)
self.assertEqual(
enable_public_share_type_creation,
form.enable_public_share_type_creation)
if enable_public_share_type_creation:
self.assertIn("is_public", form.fields)
self.assertTrue(form.fields["is_public"])
else:
self.assertNotIn("is_public", form.fields)
self.manilaclient.share_types.create.assert_called_once_with(
name=data['name'],
spec_driver_handles_share_servers='true',
spec_snapshot_support=True,
is_public=enable_public_share_type_creation)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)

View File

@ -0,0 +1,88 @@
# Copyright (c) 2014 NetApp, Inc.
# Copyright (c) 2015 Mirantis, Inc.
#
# 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.core.urlresolvers import reverse
import mock
from openstack_dashboard.api import keystone as api_keystone
from openstack_dashboard.api import neutron as api_neutron
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.admin import utils
from manila_ui.tests.dashboards.project.shares import test_data
from manila_ui.tests import helpers as test
from manila_ui.tests.test_data import keystone_data
INDEX_URL = reverse('horizon:admin:share_types:index')
class ShareTypeTests(test.BaseAdminViewTests):
def setUp(self):
super(self.__class__, self).setUp()
self.share_type = test_data.share_type
self.url = reverse('horizon:admin:share_types:update_type',
args=[self.share_type.id])
self.mock_object(
api_manila, "share_type_get",
mock.Mock(return_value=self.share_type))
self.mock_object(
api_keystone, "tenant_list",
mock.Mock(return_value=(keystone_data.projects, None)))
self.mock_object(
api_neutron, "is_service_enabled", mock.Mock(return_value=[True]))
# Reset taken list of projects to avoid test interference
utils.PROJECTS = {}
def test_create_share_type(self):
url = reverse('horizon:admin:share_types:create_type')
data = {
'is_public': True,
'name': 'my_share_type',
'spec_driver_handles_share_servers': 'False'
}
form_data = data.copy()
form_data['spec_driver_handles_share_servers'] = 'false'
self.mock_object(api_manila, "share_type_create")
res = self.client.post(url, data)
api_manila.share_type_create.assert_called_once_with(
mock.ANY, form_data['name'],
form_data['spec_driver_handles_share_servers'],
is_public=form_data['is_public'])
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_update_share_type_get(self):
res = self.client.get(self.url)
api_manila.share_type_get.assert_called_once_with(
mock.ANY, self.share_type.id)
self.assertNoMessages()
self.assertTemplateUsed(res, 'admin/share_types/update.html')
def test_update_share_type_post(self):
data = {
'extra_specs': 'driver_handles_share_servers=True'
}
form_data = {
'extra_specs': {'driver_handles_share_servers': 'True'},
}
self.mock_object(api_manila, "share_type_set_extra_specs")
res = self.client.post(self.url, data)
api_manila.share_type_set_extra_specs.assert_called_once_with(
mock.ANY, self.share_type.id, form_data['extra_specs'])
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -13,10 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from django.core.handlers import wsgi
from django import forms as django_forms
from horizon import forms as horizon_forms
import mock
from manila_ui.api import manila as api
@ -24,237 +21,6 @@ from manila_ui.dashboards.admin.shares import forms
from manila_ui.tests import helpers as base
@ddt.ddt
class ManilaDashboardsAdminSharesUpdateShareTypeFormTests(base.APITestCase):
def setUp(self):
super(self.__class__, self).setUp()
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
def _get_form(self, initial):
kwargs = {
'prefix': None,
'initial': initial,
}
return forms.UpdateShareType(self.request, **kwargs)
@ddt.data(
({}, []),
({'foo': 'bar', 'quuz': 'zaab'}, ["foo=bar\r\n", "quuz=zaab\r\n"]),
)
@ddt.unpack
def test___init__(self, extra_specs_dict_input, extra_specs_str_output):
form = self._get_form({'extra_specs': extra_specs_dict_input})
for expected_extra_spec in extra_specs_str_output:
self.assertIn(expected_extra_spec, form.initial['extra_specs'])
self.assertIn('extra_specs', list(form.fields.keys()))
self.assertTrue(
isinstance(form.fields['extra_specs'], horizon_forms.CharField))
@mock.patch('horizon.messages.success')
def test_handle_success_no_changes(self, mock_horizon_messages_success):
initial = {'id': 'fake_id', 'name': 'fake_name', 'extra_specs': {}}
form = self._get_form(initial)
data = {'extra_specs': ''}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
@mock.patch('horizon.messages.success')
def test_handle_success_only_set(self, mock_horizon_messages_success):
initial = {
'id': 'fake_id',
'name': 'fake_name',
'extra_specs': {'foo': 'bar'}
}
form = self._get_form(initial)
data = {'extra_specs': 'foo=bar\r\n'}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
self.manilaclient.share_types.get.assert_called_once_with(
initial['id'])
self.manilaclient.share_types.get.return_value.set_keys.\
assert_called_once_with({'foo': 'bar'})
self.assertFalse(
self.manilaclient.share_types.get.return_value.unset_keys.called)
@mock.patch('horizon.messages.success')
def test_handle_success_only_unset(self, mock_horizon_messages_success):
initial = {
'id': 'fake_id',
'name': 'fake_name',
'extra_specs': {'foo': 'bar'}
}
form = self._get_form(initial)
data = {'extra_specs': 'foo\r\n'}
share_types_get = self.manilaclient.share_types.get
share_types_get.return_value.get_keys.return_value = {
'foo': 'bar', 'quuz': 'zaab'}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
self.manilaclient.share_types.get.assert_has_calls([
mock.call(initial['id'])])
share_types_get.return_value.get_keys.assert_called_once_with()
self.assertFalse(share_types_get.return_value.set_keys.called)
share_types_get.return_value.unset_keys.assert_called_once_with(
{'foo'})
@mock.patch('horizon.messages.success')
def test_handle_success_set_and_unset(self, mock_horizon_messages_success):
initial = {
'id': 'fake_id',
'name': 'fake_name',
'extra_specs': {'foo': 'bar'}
}
form = self._get_form(initial)
data = {'extra_specs': 'foo\r\nquuz=zaab'}
share_types_get = self.manilaclient.share_types.get
share_types_get.return_value.get_keys.return_value = {'foo': 'bar'}
result = form.handle(self.request, data)
self.assertTrue(result)
mock_horizon_messages_success.assert_called_once_with(
mock.ANY, mock.ANY)
self.manilaclient.share_types.get.assert_has_calls([
mock.call(initial['id'])])
share_types_get.return_value.get_keys.assert_called_once_with()
share_types_get.return_value.set_keys.assert_called_once_with(
{'quuz': 'zaab'})
share_types_get.return_value.unset_keys.assert_called_once_with(
{'foo'})
def test_handle_validation_error(self):
initial = {'id': 'fake_id', 'name': 'fake_name', 'extra_specs': {}}
form = self._get_form(initial)
form.api_error = mock.Mock()
data = {'extra_specs': 'a b'}
result = form.handle(self.request, data)
self.assertFalse(result)
form.api_error.assert_called_once_with(mock.ANY)
@mock.patch('horizon.exceptions.handle')
def test_handle_other_exception(self, mock_horizon_exceptions_handle):
django_forms.ValidationError
initial = {'id': 'fake_id', 'name': 'fake_name', 'extra_specs': {}}
form = self._get_form(initial)
data = {'extra_specs': None}
result = form.handle(self.request, data)
self.assertFalse(result)
mock_horizon_exceptions_handle.assert_called_once_with(
self.request, mock.ANY)
@ddt.ddt
class ManilaDashboardsAdminSharesCreateShareTypeFormTests(base.APITestCase):
def setUp(self):
super(self.__class__, self).setUp()
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
def _get_form(self, **kwargs):
return forms.CreateShareType(self.request, **kwargs)
@mock.patch('horizon.messages.success')
def test_create_share_type(self, mock_horizon_messages_success):
form = self._get_form()
data = {
'extra_specs': '',
'is_public': False,
'spec_driver_handles_share_servers': 'True',
'name': 'share',
}
result = form.handle(self.request, data)
self.assertTrue(result)
self.manilaclient.share_types.create.assert_called_once_with(
name=data['name'],
spec_driver_handles_share_servers='true',
spec_snapshot_support=True,
is_public=data["is_public"])
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@mock.patch('horizon.messages.success')
def test_create_share_type_with_extra_specs(self,
mock_horizon_messages_success):
form = self._get_form()
data = {'extra_specs': 'a=b \n c=d',
'is_public': False,
'spec_driver_handles_share_servers': 'True',
'name': 'share'}
result = form.handle(self.request, data)
self.assertTrue(result)
set_keys = self.manilaclient.share_types.get.return_value.set_keys
set_keys.assert_called_once_with(
{'a': 'b', 'c': 'd'})
self.manilaclient.share_types.create.assert_called_once_with(
name=data['name'],
spec_driver_handles_share_servers='true',
spec_snapshot_support=True,
is_public=data["is_public"])
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
@ddt.data(True, False)
@mock.patch('horizon.messages.success')
def test_public_share_type_creation(self,
enable_public_share_type_creation,
mock_horizon_messages_success):
with self.settings(OPENSTACK_MANILA_FEATURES={
'enable_public_share_type_creation':
enable_public_share_type_creation}):
form = self._get_form()
data = {
'extra_specs': '',
'is_public': enable_public_share_type_creation,
'spec_driver_handles_share_servers': 'True',
'name': 'share',
}
result = form.handle(self.request, data)
self.assertTrue(result)
self.assertEqual(
enable_public_share_type_creation,
form.enable_public_share_type_creation)
if enable_public_share_type_creation:
self.assertIn("is_public", form.fields)
self.assertTrue(form.fields["is_public"])
else:
self.assertNotIn("is_public", form.fields)
self.manilaclient.share_types.create.assert_called_once_with(
name=data['name'],
spec_driver_handles_share_servers='true',
spec_snapshot_support=True,
is_public=enable_public_share_type_creation)
mock_horizon_messages_success.assert_called_once_with(
self.request, mock.ANY)
class ManilaDashboardsAdminMigrationFormTests(base.APITestCase):
def setUp(self):

Some files were not shown because too many files have changed in this diff Show More