support for Masakari VMove
It supports for Masakari VMove API in microversion 1.3. Change-Id: Id06f4d1dd8a4e59760bd63f5f7df6a73319d6a9c
This commit is contained in:
parent
f99ce40c08
commit
d9c739b146
|
@ -192,3 +192,16 @@ def get_notification_with_progress_details(request, notification_id):
|
|||
return openstack_connection(
|
||||
request, version='1.1').get_notification(
|
||||
notification_id)
|
||||
|
||||
|
||||
@handle_errors(_("Unable to get vmoves list"), [])
|
||||
def get_vmoves_list(request, notification_id, filters):
|
||||
"""return vmoves list """
|
||||
return openstack_connection(
|
||||
request, version='1.3').vmoves(notification_id, **filters)
|
||||
|
||||
|
||||
def get_vmove(request, notification_id, vmove_id):
|
||||
"""return single vmove"""
|
||||
return openstack_connection(
|
||||
request, version='1.3').get_vmove(vmove_id, notification_id)
|
||||
|
|
|
@ -7,3 +7,4 @@ os_masakari_api:extensions: rule:admin_api
|
|||
os_masakari_api:segments: rule:admin_api
|
||||
os_masakari_api:os-hosts: rule:admin_api
|
||||
os_masakari_api:notifications: rule:admin_api
|
||||
os_masakari_api:vmoves: rule:admin_api
|
||||
|
|
|
@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__)
|
|||
class MasakariDashboard(horizon.Dashboard):
|
||||
slug = "masakaridashboard"
|
||||
name = _("Instance-ha")
|
||||
panels = ('default', 'segments', 'hosts', 'notifications')
|
||||
panels = ('default', 'segments', 'hosts', 'notifications', 'vmoves')
|
||||
default_panel = 'default'
|
||||
policy_rules = (('instance-ha', 'context_is_admin'),)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ from horizon import tabs
|
|||
|
||||
from masakaridashboard.api import api
|
||||
from masakaridashboard.notifications import tables as notification_tab
|
||||
from masakaridashboard.vmoves import tables as vmove_table
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
|
@ -66,6 +67,29 @@ class NotificationProgressDetailsTab(tabs.TableTab):
|
|||
return []
|
||||
|
||||
|
||||
class VMoveTab(tabs.TableTab):
|
||||
table_classes = (vmove_table.VMoveTable,)
|
||||
name = _("VMoves")
|
||||
slug = "vmove_tab"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
preload = False
|
||||
|
||||
def get_vmove_data(self):
|
||||
vmove_list = []
|
||||
notification_type = self.tab_group.kwargs['type']
|
||||
if notification_type != "COMPUTE_HOST":
|
||||
return vmove_list
|
||||
|
||||
notification_id = self.tab_group.kwargs['notification_uuid']
|
||||
vmove_gen = api.get_vmoves_list(
|
||||
self.request, notification_id, filters={})
|
||||
|
||||
for item in vmove_gen:
|
||||
vmove_list.append(item)
|
||||
|
||||
return vmove_list
|
||||
|
||||
|
||||
class NotificationDetailTabs(tabs.DetailTabsGroup):
|
||||
slug = "notification_details"
|
||||
tabs = (OverviewTab, NotificationProgressDetailsTab)
|
||||
tabs = (OverviewTab, NotificationProgressDetailsTab, VMoveTab)
|
||||
|
|
|
@ -19,6 +19,7 @@ from oslo_utils import timeutils
|
|||
from openstack.instance_ha.v1 import host
|
||||
from openstack.instance_ha.v1 import notification
|
||||
from openstack.instance_ha.v1 import segment
|
||||
from openstack.instance_ha.v1 import vmove
|
||||
from openstack_dashboard.test.test_data import utils as test_data_utils
|
||||
|
||||
from masakaridashboard.test import uuidsentinel
|
||||
|
@ -97,3 +98,18 @@ def data(TEST):
|
|||
TEST.masakari_notification.add(notification1)
|
||||
TEST.masakari_notification.add(notification2)
|
||||
TEST.masakari_notification.add(notification3)
|
||||
TEST.masakari_vmove = test_data_utils.TestDataContainer()
|
||||
|
||||
vmove1 = vmove.VMove(
|
||||
uuid=uuidsentinel.vmove1,
|
||||
notification_id=uuidsentinel.notification1,
|
||||
server_id=uuidsentinel.server1,
|
||||
server_name="vm",
|
||||
source_host='host1',
|
||||
dest_host='host2',
|
||||
start_time=(NOW - datetime.timedelta(seconds=3)),
|
||||
end_time=(NOW - datetime.timedelta(seconds=1)),
|
||||
status='succeeded',
|
||||
type='evacuation')
|
||||
|
||||
TEST.masakari_vmove.add(vmove1)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Copyright(c) 2022 Inspur
|
||||
#
|
||||
# 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 masakaridashboard import dashboard
|
||||
|
||||
|
||||
class VMoves(horizon.Panel):
|
||||
name = _("VMoves")
|
||||
slug = 'vmoves'
|
||||
|
||||
|
||||
dashboard.MasakariDashboard.register(VMoves)
|
|
@ -0,0 +1,61 @@
|
|||
# Copyright(c) 2022 Inspur
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
VMOVE_FILTER_CHOICES = (
|
||||
('notification_id', _("Notification UUId ="), True),
|
||||
('type', _("Type ="), True),
|
||||
('status', _("Status ="), True),
|
||||
)
|
||||
|
||||
|
||||
class VMoveFilterAction(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = VMOVE_FILTER_CHOICES
|
||||
|
||||
|
||||
class VMoveTable(tables.DataTable):
|
||||
|
||||
uuid = tables.Column('uuid', verbose_name=_("UUID"),
|
||||
link="horizon:masakaridashboard:vmoves:detail")
|
||||
notification_id = tables.Column(
|
||||
'notification_id', verbose_name=_("Notification UUID"),
|
||||
link="horizon:masakaridashboard:notifications:detail")
|
||||
server_id = tables.Column(
|
||||
'server_id', verbose_name=_("Server ID"))
|
||||
server_name = tables.Column(
|
||||
'server_name', verbose_name=_("Server Name"))
|
||||
type = tables.Column('type', verbose_name=_("Type"))
|
||||
source_host = tables.Column(
|
||||
'source_host', verbose_name=_("Source Host"))
|
||||
dest_host = tables.Column(
|
||||
'dest_host', verbose_name=_("Dest Host"))
|
||||
start_time = tables.Column(
|
||||
'start_time', verbose_name=_("Start Time"))
|
||||
end_time = tables.Column(
|
||||
'end_time', verbose_name=_("End Time"))
|
||||
status = tables.Column(
|
||||
'status', verbose_name=_("Status"))
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum.uuid + ',' + datum.notification_id
|
||||
|
||||
class Meta(object):
|
||||
name = "vmove"
|
||||
verbose_name = _("VMove")
|
||||
table_actions = (VMoveFilterAction,)
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright(c) 2022 Inspur
|
||||
#
|
||||
# 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 OverviewTab(tabs.Tab):
|
||||
name = _("VMoves")
|
||||
slug = "vmoves"
|
||||
template_name = ("masakaridashboard/vmoves/_detail_overview.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"vmove": self.tab_group.kwargs['vmove']}
|
||||
|
||||
|
||||
class VMoveDetailTabs(tabs.DetailTabsGroup):
|
||||
slug = "vmove_details"
|
||||
tabs = (OverviewTab,)
|
|
@ -0,0 +1,25 @@
|
|||
{% load i18n sizeformat parse_date %}
|
||||
<div class="detail">
|
||||
<h4>{% trans "VMove Detail" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "UUID" %}</dt>
|
||||
<dd>{{ vmove.uuid }}</dd>
|
||||
<dt>{% trans "Server ID" %}</dt>
|
||||
<dd>{{ vmove.server_id }}</dd>
|
||||
<dt>{% trans "Server Name" %}</dt>
|
||||
<dd>{{ vmove.server_name }}</dd>
|
||||
<dt>{% trans "Type" %}</dt>
|
||||
<dd>{{ vmove.type }}</dd>
|
||||
<dt>{% trans "Source Host" %}</dt>
|
||||
<dd>{{ vmove.source_host }}</dd>
|
||||
<dt>{% trans "Dest Host" %}</dt>
|
||||
<dd>{{ vmove.dest_host }}</dd>
|
||||
<dt>{% trans "Start Time" %}</dt>
|
||||
<dd>{{ vmove.start_time }}</dd>
|
||||
<dt>{% trans "End Time" %}</dt>
|
||||
<dd>{{ vmove.end_time }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ vmove.status }}</dd>
|
||||
</dl>
|
||||
</div>
|
|
@ -0,0 +1,38 @@
|
|||
<span class="masakari-wrapper detail-screen">
|
||||
{% extends 'masakaridashboard/default/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "VMove Detail" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("VMove Detail") %}
|
||||
{% endblock page_header %}
|
||||
{% block main %}
|
||||
{% load i18n sizeformat parse_date %}
|
||||
<div class="detail">
|
||||
<h4>{% trans "VMove Detail" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "UUID" %}</dt>
|
||||
<dd>{{ vmove.uuid }}</dd>
|
||||
<dt>{% trans "Server ID" %}</dt>
|
||||
<dd>{{ vmove.server_id }}</dd>
|
||||
<dt>{% trans "Server Name" %}</dt>
|
||||
<dd>{{ vmove.server_name }}</dd>
|
||||
<dt>{% trans "Type" %}</dt>
|
||||
<dd>{{ vmove.type }}</dd>
|
||||
<dt>{% trans "Source Host" %}</dt>
|
||||
<dd>{{ vmove.source_host }}</dd>
|
||||
<dt>{% trans "Dest Host" %}</dt>
|
||||
<dd>{{ vmove.dest_host }}</dd>
|
||||
<dt>{% trans "Start Time" %}</dt>
|
||||
<dd>{{ vmove.start_time }}</dd>
|
||||
<dt>{% trans "End Time" %}</dt>
|
||||
<dd>{{ vmove.end_time }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ vmove.status }}</dd>
|
||||
<dt>{% trans "Message" %}</dt>
|
||||
<dd>{{ vmove.message }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</span>
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'masakaridashboard/default/table.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "VMoves" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("VMoves") %}
|
||||
{% endblock page_header %}
|
|
@ -0,0 +1,51 @@
|
|||
# Copyright(c) 2022 Inspur
|
||||
#
|
||||
# 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 unittest import mock
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from masakaridashboard.test import helpers as test
|
||||
|
||||
INDEX_URL = reverse('horizon:masakaridashboard:vmoves:index')
|
||||
|
||||
|
||||
class VMoveTest(test.TestCase):
|
||||
|
||||
def test_index(self):
|
||||
vmoves = self.masakari_vmove.list()
|
||||
notifications = self.masakari_notification.list()
|
||||
with mock.patch('masakaridashboard.api.api.notification_list',
|
||||
return_value=notifications), mock.patch(
|
||||
'masakaridashboard.api.api.get_notification',
|
||||
return_value=notifications[0]), mock.patch(
|
||||
'masakaridashboard.api.api.get_vmoves_list',
|
||||
return_value=vmoves):
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'masakaridashboard/vmoves/index.html')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_detail(self):
|
||||
vmove = self.masakari_vmove.list()[0]
|
||||
id_to_update = vmove.uuid + ',' + vmove.notification_id
|
||||
detail_url = reverse('horizon:masakaridashboard:vmoves:detail',
|
||||
args=[id_to_update])
|
||||
with mock.patch('masakaridashboard.api.api.get_vmove',
|
||||
return_value=self.masakari_vmove.list()[0]):
|
||||
res = self.client.get(detail_url)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertEqual(200, res.status_code)
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
|
||||
self.assertTemplateUsed(
|
||||
res, 'masakaridashboard/vmoves/_detail_overview.html')
|
|
@ -0,0 +1,24 @@
|
|||
# Copyright(c) 2022 Inspur
|
||||
#
|
||||
# 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 re_path
|
||||
|
||||
from masakaridashboard.vmoves import views
|
||||
|
||||
|
||||
VMOVE = r'^(?P<vmove_id>[^/]+)/%s$'
|
||||
urlpatterns = [
|
||||
re_path(r'^$', views.IndexView.as_view(), name='index'),
|
||||
re_path(VMOVE % 'detail', views.DetailView.as_view(), name='detail'),
|
||||
]
|
|
@ -0,0 +1,92 @@
|
|||
# Copyright(c) 2022 Inspur
|
||||
#
|
||||
# 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.urls 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 masakaridashboard.api import api
|
||||
from masakaridashboard.vmoves import tables as masakari_tab
|
||||
from masakaridashboard.vmoves import tabs as vmove_tab
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = masakari_tab.VMoveTable
|
||||
template_name = 'masakaridashboard/vmoves/index.html'
|
||||
page_title = _("VMoves")
|
||||
|
||||
def needs_filter_first(self, table):
|
||||
return self._needs_filter_first
|
||||
|
||||
def get_data(self):
|
||||
notifications = api.notification_list(self.request)
|
||||
vmove_list = []
|
||||
filters = self.get_filters()
|
||||
self._needs_filter_first = True
|
||||
|
||||
filter_first = getattr(settings, 'FILTER_DATA_FIRST', {})
|
||||
if filter_first.get('masakaridashboard.vmoves', False) and len(
|
||||
filters) == 0:
|
||||
self._needs_filter_first = True
|
||||
self._more = False
|
||||
return vmove_list
|
||||
|
||||
for notification in notifications:
|
||||
vmove_gen = api.get_vmoves_list(
|
||||
self.request, notification.notification_uuid, filters)
|
||||
for item in vmove_gen:
|
||||
vmove_list.append(item)
|
||||
|
||||
return vmove_list
|
||||
|
||||
|
||||
class DetailView(tabs.TabbedTableView):
|
||||
tab_group_class = vmove_tab.VMoveDetailTabs
|
||||
template_name = 'horizon/common/_detail.html'
|
||||
page_title = "{{ vmove.server_name|default:vmove.server_name }}"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
vmove = self.get_data()
|
||||
table = masakari_tab.VMoveTable(self.request)
|
||||
context["vmove"] = vmove
|
||||
context["url"] = self.get_redirect_url()
|
||||
context["actions"] = table.render_row_actions(vmove)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
try:
|
||||
row_data = self.kwargs['vmove_id'].split(',')
|
||||
notification_id = row_data[1]
|
||||
vmove_id = row_data[0]
|
||||
vmove = api.get_vmove(self.request, vmove_id, notification_id)
|
||||
except Exception:
|
||||
msg = _('Unable to get vmove "%s".') % vmove_id
|
||||
redirect = reverse('horizon:masakaridashboard:vmoves:index')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
return vmove
|
||||
|
||||
def get_redirect_url(self):
|
||||
return reverse('horizon:masakaridashboard:vmoves:index')
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
vmove = self.get_data()
|
||||
return self.tab_group_class(request, vmove=vmove, **kwargs)
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Adds support for Masakari VMove API in microversion 1.3.
|
||||
`Blueprint vm-evacuations-for-host-recovery <https://blueprints.launchpad.net/masakari/+spec/vm-evacuations-for-host-recovery>`__
|
Loading…
Reference in New Issue