
horizon already deprecated launch instance Django based implementation in the wallaby cycle [1]. This patch remove code for launch instance Django based implementation as angular based implementation is the default one from long and all features gaps between angular and Django implementation is closed. It also moves SetAdvanced step code to ``resize_instance.py`` as ``workflows/create_instance.py`` file is deleted and remove server_group option from Advanced Options of resizing instance action because "server_group" is not required while resizing an instance as per nova-api reference [2]. Closes-Bug: #1869222 [1] https://review.opendev.org/c/openstack/horizon/+/779125 [2] https://docs.openstack.org/api-ref/compute/?expanded=resize-server-resize-action-detail#resize-server-resize-action Change-Id: I5e01cd81f309491f1a58ea93911030366a86e3c7
279 lines
9.4 KiB
Python
279 lines
9.4 KiB
Python
# 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.
|
|
|
|
from django.urls import reverse
|
|
from django.utils.http import urlencode
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.utils.translation import ungettext_lazy
|
|
|
|
from horizon import tables
|
|
|
|
from openstack_dashboard import api
|
|
from openstack_dashboard.api import cinder
|
|
from openstack_dashboard import policy
|
|
|
|
from openstack_dashboard.dashboards.project.volumes \
|
|
import tables as volume_tables
|
|
|
|
|
|
class LaunchSnapshot(volume_tables.LaunchVolume):
|
|
name = "launch_snapshot"
|
|
|
|
def get_link_url(self, datum):
|
|
base_url = reverse(self.url)
|
|
|
|
vol_id = "%s:snap" % self.table.get_object_id(datum)
|
|
params = urlencode({"source_type": "volume_snapshot_id",
|
|
"source_id": vol_id})
|
|
return "?".join([base_url, params])
|
|
|
|
def allowed(self, request, snapshot=None):
|
|
if snapshot:
|
|
if (snapshot._volume and
|
|
getattr(snapshot._volume, 'bootable', '') == 'true'):
|
|
return snapshot.status == "available"
|
|
return False
|
|
|
|
|
|
class LaunchSnapshotNG(LaunchSnapshot):
|
|
name = "launch_snapshot_ng"
|
|
verbose_name = _("Launch as Instance")
|
|
url = "horizon:project:snapshots:index"
|
|
classes = ("btn-launch", )
|
|
ajax = False
|
|
|
|
def __init__(self, attrs=None, **kwargs):
|
|
kwargs['preempt'] = True
|
|
super().__init__(attrs, **kwargs)
|
|
|
|
def get_link_url(self, datum):
|
|
url = reverse(self.url)
|
|
vol_id = self.table.get_object_id(datum)
|
|
ngclick = "modal.openLaunchInstanceWizard(" \
|
|
"{successUrl: '%s', snapshotId: '%s'})" % (url, vol_id)
|
|
self.attrs.update({
|
|
"ng-controller": "LaunchInstanceModalController as modal",
|
|
"ng-click": ngclick
|
|
})
|
|
return "javascript:void(0);"
|
|
|
|
|
|
class DeleteVolumeSnapshot(policy.PolicyTargetMixin, tables.DeleteAction):
|
|
help_text = _("Deleted volume snapshots are not recoverable.")
|
|
|
|
@staticmethod
|
|
def action_present(count):
|
|
return ungettext_lazy(
|
|
"Delete Volume Snapshot",
|
|
"Delete Volume Snapshots",
|
|
count
|
|
)
|
|
|
|
@staticmethod
|
|
def action_past(count):
|
|
return ungettext_lazy(
|
|
"Scheduled deletion of Volume Snapshot",
|
|
"Scheduled deletion of Volume Snapshots",
|
|
count
|
|
)
|
|
|
|
policy_rules = (("volume", "volume:delete_snapshot"),)
|
|
policy_target_attrs = (("project_id",
|
|
'os-extended-snapshot-attributes:project_id'),)
|
|
|
|
def delete(self, request, obj_id):
|
|
api.cinder.volume_snapshot_delete(request, obj_id)
|
|
|
|
def allowed(self, request, datum=None):
|
|
if datum:
|
|
# Can't delete snapshot if part of group snapshot
|
|
if getattr(datum, 'group_snapshot_id', None):
|
|
return False
|
|
if datum.status == 'backing-up':
|
|
return False
|
|
return True
|
|
|
|
|
|
class EditVolumeSnapshot(policy.PolicyTargetMixin, tables.LinkAction):
|
|
name = "edit"
|
|
verbose_name = _("Edit Snapshot")
|
|
url = "horizon:project:snapshots:update"
|
|
classes = ("ajax-modal",)
|
|
icon = "pencil"
|
|
policy_rules = (("volume", "volume:update_snapshot"),)
|
|
policy_target_attrs = (("project_id",
|
|
'os-extended-snapshot-attributes:project_id'),)
|
|
|
|
def allowed(self, request, snapshot=None):
|
|
return snapshot.status == "available"
|
|
|
|
def get_link_url(self, datum):
|
|
params = urlencode({"success_url": self.table.get_full_url()})
|
|
snapshot_id = self.table.get_object_id(datum)
|
|
return "?".join([reverse(self.url, args=(snapshot_id,)), params])
|
|
|
|
|
|
class CreateVolumeFromSnapshot(tables.LinkAction):
|
|
name = "create_from_snapshot"
|
|
verbose_name = _("Create Volume")
|
|
url = "horizon:project:volumes:create"
|
|
classes = ("ajax-modal",)
|
|
icon = "camera"
|
|
policy_rules = (("volume", "volume:create"),)
|
|
|
|
def get_link_url(self, datum):
|
|
base_url = reverse(self.url)
|
|
params = urlencode({"snapshot_id": self.table.get_object_id(datum)})
|
|
return "?".join([base_url, params])
|
|
|
|
def allowed(self, request, volume=None):
|
|
if volume and cinder.is_volume_service_enabled(request):
|
|
return volume.status == "available"
|
|
return False
|
|
|
|
|
|
class CreateBackup(policy.PolicyTargetMixin, tables.LinkAction):
|
|
name = "backups"
|
|
verbose_name = _("Create Backup")
|
|
url = "horizon:project:volumes:create_snapshot_backup"
|
|
classes = ("ajax-modal",)
|
|
policy_rules = (("volume", "backup:create"),)
|
|
|
|
def get_link_url(self, datum):
|
|
snap_id = self.table.get_object_id(datum)
|
|
url = reverse(self.url, args=(datum.volume_id, snap_id))
|
|
return url
|
|
|
|
def allowed(self, request, snapshot=None):
|
|
return (cinder.volume_backup_supported(request) and
|
|
snapshot.status == 'available')
|
|
|
|
|
|
class UpdateMetadata(tables.LinkAction):
|
|
name = "update_metadata"
|
|
verbose_name = _("Update Metadata")
|
|
policy_rules = (("volume", "volume:update_snapshot_metadata"),)
|
|
ajax = False
|
|
attrs = {"ng-controller": "MetadataModalHelperController as modal"}
|
|
|
|
def __init__(self, **kwargs):
|
|
kwargs['preempt'] = True
|
|
super().__init__(**kwargs)
|
|
|
|
def get_link_url(self, datum):
|
|
obj_id = self.table.get_object_id(datum)
|
|
self.attrs['ng-click'] = (
|
|
"modal.openMetadataModal('volume_snapshot', '%s', true)" % obj_id)
|
|
return "javascript:void(0);"
|
|
|
|
|
|
class UpdateRow(tables.Row):
|
|
ajax = True
|
|
|
|
def get_data(self, request, snapshot_id):
|
|
snapshot = cinder.volume_snapshot_get(request, snapshot_id)
|
|
snapshot._volume = cinder.volume_get(request, snapshot.volume_id)
|
|
if getattr(snapshot, 'group_snapshot_id', None):
|
|
snapshot.group_snapshot = cinder.group_snapshot_get(
|
|
request, snapshot.group_snapshot_id)
|
|
else:
|
|
snapshot.group_snapshot = None
|
|
return snapshot
|
|
|
|
|
|
class SnapshotVolumeNameColumn(tables.WrappingColumn):
|
|
def get_raw_data(self, snapshot):
|
|
volume = snapshot._volume
|
|
return volume.name if volume else _("Unknown")
|
|
|
|
def get_link_url(self, snapshot):
|
|
volume = snapshot._volume
|
|
if volume:
|
|
volume_id = volume.id
|
|
return reverse(self.link, args=(volume_id,))
|
|
|
|
|
|
class GroupSnapshotNameColumn(tables.WrappingColumn):
|
|
def get_raw_data(self, snapshot):
|
|
group_snapshot = snapshot.group_snapshot
|
|
return group_snapshot.name_or_id if group_snapshot else _("-")
|
|
|
|
def get_link_url(self, snapshot):
|
|
group_snapshot = snapshot.group_snapshot
|
|
if group_snapshot:
|
|
return reverse(self.link, args=(group_snapshot.id,))
|
|
|
|
|
|
class VolumeSnapshotsFilterAction(tables.FilterAction):
|
|
|
|
def filter(self, table, snapshots, filter_string):
|
|
"""Naive case-insensitive search."""
|
|
query = filter_string.lower()
|
|
return [snapshot for snapshot in snapshots
|
|
if query in snapshot.name.lower()]
|
|
|
|
|
|
class VolumeDetailsSnapshotsTable(volume_tables.VolumesTableBase):
|
|
group_snapshot = GroupSnapshotNameColumn(
|
|
"name",
|
|
verbose_name=_("Group Snapshot"),
|
|
link="horizon:project:vg_snapshots:detail")
|
|
name = tables.WrappingColumn(
|
|
"name",
|
|
verbose_name=_("Name"),
|
|
link="horizon:project:snapshots:detail")
|
|
|
|
class Meta(object):
|
|
name = "volume_snapshots"
|
|
verbose_name = _("Volume Snapshots")
|
|
pagination_param = 'snapshot_marker'
|
|
prev_pagination_param = 'prev_snapshot_marker'
|
|
table_actions = (VolumeSnapshotsFilterAction, DeleteVolumeSnapshot,)
|
|
|
|
launch_actions = (LaunchSnapshotNG,)
|
|
|
|
row_actions = ((CreateVolumeFromSnapshot,) + launch_actions +
|
|
(EditVolumeSnapshot, DeleteVolumeSnapshot, CreateBackup,
|
|
UpdateMetadata))
|
|
row_class = UpdateRow
|
|
status_columns = ("status",)
|
|
|
|
|
|
class VolumeSnapshotsTable(VolumeDetailsSnapshotsTable):
|
|
volume_name = SnapshotVolumeNameColumn(
|
|
"name",
|
|
verbose_name=_("Volume Name"),
|
|
link="horizon:project:volumes:detail")
|
|
|
|
class Meta(VolumeDetailsSnapshotsTable.Meta):
|
|
pass
|
|
|
|
|
|
class SnapshotMessagesTable(tables.DataTable):
|
|
message_id = tables.Column("id", verbose_name=_("ID"))
|
|
message_level = tables.Column("message_level",
|
|
verbose_name=_("Message Level"))
|
|
event_id = tables.Column("event_id",
|
|
verbose_name=_("Event Id"))
|
|
user_message = tables.Column("user_message",
|
|
verbose_name=_("User Message"))
|
|
created_at = tables.Column("created_at",
|
|
verbose_name=_("Created At"))
|
|
guaranteed_until = tables.Column("guaranteed_until",
|
|
verbose_name=_("Guaranteed Until"))
|
|
|
|
class Meta(object):
|
|
name = "snapshot_messages"
|
|
verbose_name = _("Messages")
|