Add update method of snapshot name and description

Change-Id: I0c396bee1f14bdb96812012ea89d6fd2bf0c6e34
Closes-Bug: #1265140
Closes-Bug: #1296398
This commit is contained in:
Zhenguo Niu 2014-02-25 10:23:30 +08:00
parent 62c766754d
commit 235ceff89c
11 changed files with 250 additions and 44 deletions

View File

@ -222,6 +222,14 @@ def volume_snapshot_delete(request, snapshot_id):
return cinderclient(request).volume_snapshots.delete(snapshot_id)
def volume_snapshot_update(request, snapshot_id, name, description):
snapshot_data = {'name': name,
'description': description}
snapshot_data = _replace_v2_parameters(snapshot_data)
return cinderclient(request).volume_snapshots.update(snapshot_id,
**snapshot_data)
def tenant_quota_get(request, tenant_id):
c_client = cinderclient(request)
if c_client is None:

View File

@ -16,6 +16,7 @@
"volume:create_snapshot": [["rule:default"]],
"volume:delete_snapshot": [["rule:default"]],
"volume:get_snapshot": [],
"volume:update_snapshot": [["rule:default"]],
"volume:get_all_snapshots": [],
"volume:extend": [],

View File

@ -0,0 +1,44 @@
# 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 exceptions
from horizon import forms
from horizon import messages
from openstack_dashboard.api import cinder
class UpdateForm(forms.SelfHandlingForm):
name = forms.CharField(max_length="255", label=_("Snapshot Name"))
description = forms.CharField(widget=forms.Textarea,
label=_("Description"), required=False)
def handle(self, request, data):
snapshot_id = self.initial['snapshot_id']
try:
cinder.volume_snapshot_update(request,
snapshot_id,
data['name'],
data['description'])
message = _('Updating volume snapshot "%s"') % data['name']
messages.info(request, message)
return True
except Exception:
redirect = reverse("horizon:project:volumes:index")
exceptions.handle(request,
_('Unable to update volume snapshot.'),
redirect=redirect)

View File

@ -60,6 +60,25 @@ class DeleteVolumeSnapshot(tables.DeleteAction):
api.cinder.volume_snapshot_delete(request, obj_id)
class EditVolumeSnapshot(tables.LinkAction):
name = "edit"
verbose_name = _("Edit Snapshot")
url = "horizon:project:volumes:snapshots:update"
classes = ("ajax-modal", "btn-edit")
policy_rules = (("volume", "volume:update_snapshot"),)
def get_policy_target(self, request, datum=None):
project_id = None
if datum:
project_id = getattr(datum,
"os-extended-snapshot-attributes:project_id",
None)
return {"project_id": project_id}
def allowed(self, request, snapshot=None):
return snapshot.status == "available"
class CreateVolumeFromSnapshot(tables.LinkAction):
name = "create_from_snapshot"
verbose_name = _("Create Volume")
@ -107,8 +126,9 @@ class SnapshotVolumeNameColumn(tables.Column):
class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
name = tables.Column("name",
verbose_name=_("Name"),
link="horizon:project:volumes:detail")
volume_name = SnapshotVolumeNameColumn("name",
link="horizon:project:volumes:snapshots:detail")
volume_name = SnapshotVolumeNameColumn(
"name",
verbose_name=_("Volume Name"),
link="horizon:project:volumes:volumes:detail")
@ -117,7 +137,7 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
verbose_name = _("Volume Snapshots")
table_actions = (DeleteVolumeSnapshot,)
row_actions = (CreateVolumeFromSnapshot, LaunchSnapshot,
DeleteVolumeSnapshot)
EditVolumeSnapshot, DeleteVolumeSnapshot)
row_class = UpdateRow
status_columns = ("status",)
permissions = ['openstack.services.volume']

View File

@ -153,7 +153,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
self.mox.ReplayAll()
url = reverse('horizon:project:volumes:detail',
url = reverse('horizon:project:volumes:snapshots:detail',
args=[snapshot.id])
res = self.client.get(url)
@ -174,7 +174,7 @@ class VolumeSnapshotsViewTests(test.TestCase):
AndRaise(self.exceptions.cinder)
self.mox.ReplayAll()
url = reverse('horizon:project:volumes:detail',
url = reverse('horizon:project:volumes:snapshots:detail',
args=[snapshot.id])
res = self.client.get(url)
@ -193,8 +193,30 @@ class VolumeSnapshotsViewTests(test.TestCase):
self.mox.ReplayAll()
url = reverse('horizon:project:volumes:detail',
url = reverse('horizon:project:volumes:snapshots:detail',
args=[snapshot.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({cinder: ('volume_snapshot_update',
'volume_snapshot_get')})
def test_update_snapshot(self):
snapshot = self.cinder_volume_snapshots.first()
cinder.volume_snapshot_get(IsA(http.HttpRequest), snapshot.id) \
.AndReturn(snapshot)
cinder.volume_snapshot_update(IsA(http.HttpRequest),
snapshot.id,
snapshot.name,
snapshot.description) \
.AndReturn(snapshot)
self.mox.ReplayAll()
formData = {'method': 'UpdateSnapshotForm',
'name': snapshot.name,
'description': snapshot.description}
url = reverse(('horizon:project:volumes:snapshots:update'),
args=[snapshot.id])
res = self.client.post(url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -0,0 +1,26 @@
# 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.urls import patterns # noqa
from django.conf.urls import url # noqa
from openstack_dashboard.dashboards.project.volumes.snapshots import views
urlpatterns = patterns('',
url(r'^(?P<snapshot_id>[^/]+)$',
views.DetailView.as_view(),
name='detail'),
url(r'^(?P<snapshot_id>[^/]+)/update/$',
views.UpdateView.as_view(),
name='update'),
)

View File

@ -0,0 +1,83 @@
# 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.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.utils import memoized
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.volumes \
.snapshots import forms as vol_snapshot_forms
from openstack_dashboard.dashboards.project.volumes \
.snapshots import tabs as vol_snapshot_tabs
class UpdateView(forms.ModalFormView):
form_class = vol_snapshot_forms.UpdateForm
template_name = 'project/volumes/snapshots/update.html'
success_url = reverse_lazy("horizon:project:volumes:index")
@memoized.memoized_method
def get_object(self):
snap_id = self.kwargs['snapshot_id']
try:
self._object = api.cinder.volume_snapshot_get(self.request,
snap_id)
except Exception:
msg = _('Unable to retrieve volume snapshot.')
url = reverse('horizon:project:volumes:index')
exceptions.handle(self.request, msg, redirect=url)
return self._object
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
context['snapshot'] = self.get_object()
return context
def get_initial(self):
snapshot = self.get_object()
return {'snapshot_id': self.kwargs["snapshot_id"],
'name': snapshot.name,
'description': snapshot.description}
class DetailView(tabs.TabView):
tab_group_class = vol_snapshot_tabs.SnapshotDetailTabs
template_name = 'project/volumes/snapshots/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["snapshot"] = self.get_data()
return context
@memoized.memoized_method
def get_data(self):
try:
snapshot_id = self.kwargs['snapshot_id']
snapshot = api.cinder.volume_snapshot_get(self.request,
snapshot_id)
except Exception:
redirect = reverse('horizon:project:volumes:index')
exceptions.handle(self.request,
_('Unable to retrieve snapshot details.'),
redirect=redirect)
return snapshot
def get_tabs(self, request, *args, **kwargs):
snapshot = self.get_data()
return self.tab_group_class(request, snapshot=snapshot, **kwargs)

View File

@ -0,0 +1,26 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% load url from future %}
{% block form_id %}update_snapshot_form{% endblock %}
{% block form_action %}{% url 'horizon:project:volumes:snapshots:update' snapshot.id %}{% endblock %}
{% block modal_id %}update_snapshot_modal{% endblock %}
{% block modal-header %}{% trans "Edit Snapshot" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description:" %}</h3>
<p>{% trans "From here you can modify the name and description of a snapshot." %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Edit Snapshot" %}" />
<a href="{% url 'horizon:project:volumes:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Edit Snapshot" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Edit Snapshot") %}
{% endblock page_header %}
{% block main %}
{% include 'project/volumes/snapshots/_update.html' %}
{% endblock %}

View File

@ -18,6 +18,8 @@ from django.conf.urls import include # noqa
from django.conf.urls import patterns # noqa
from django.conf.urls import url # noqa
from openstack_dashboard.dashboards.project.volumes.snapshots \
import urls as snapshot_urls
from openstack_dashboard.dashboards.project.volumes import views
from openstack_dashboard.dashboards.project.volumes.volumes \
import urls as volume_urls
@ -30,7 +32,5 @@ urlpatterns = patterns('',
url(r'^\?tab=volumes_and_snapshots__volumes_tab$',
views.IndexView.as_view(), name='volumes_tab'),
url(r'', include(volume_urls, namespace='volumes')),
url(r'^snapshots/(?P<snapshot_id>[^/]+)/$',
views.DetailView.as_view(),
name='detail'),
url(r'snapshots/', include(snapshot_urls, namespace='snapshots')),
)

View File

@ -14,47 +14,12 @@
# 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 exceptions
from horizon import tabs
from horizon.utils import memoized
from openstack_dashboard.api import cinder
from openstack_dashboard.dashboards.project.volumes \
import tabs as project_tabs
from openstack_dashboard.dashboards.project.volumes \
.snapshots import tabs as vol_snapshot_tabs
class IndexView(tabs.TabbedTableView):
tab_group_class = project_tabs.VolumeAndSnapshotTabs
template_name = 'project/volumes/index.html'
class DetailView(tabs.TabView):
tab_group_class = vol_snapshot_tabs.SnapshotDetailTabs
template_name = 'project/volumes/snapshots/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["snapshot"] = self.get_data()
return context
@memoized.memoized_method
def get_data(self):
try:
snapshot_id = self.kwargs['snapshot_id']
snapshot = cinder.volume_snapshot_get(self.request, snapshot_id)
except Exception:
redirect = reverse('horizon:project:volumes:index')
exceptions.handle(self.request,
_('Unable to retrieve snapshot details.'),
redirect=redirect)
return snapshot
def get_tabs(self, request, *args, **kwargs):
snapshot = self.get_data()
return self.tab_group_class(request, snapshot=snapshot, **kwargs)