Remove pool selection from "Add Monitor" page

Allow one to create a health monitor object without
any association. Add buttons "Add Health Monitor" and
"Delete Health Monitor" on pools table page.

fixes bug 1158602

Change-Id: I4d9abe378dc1ef2cf410f801ec1f480060de015a
This commit is contained in:
Tatiana Mazur 2013-04-12 14:42:06 +04:00
parent 7c29e06f92
commit 0d1e40cc0b
8 changed files with 338 additions and 59 deletions

View File

@ -45,7 +45,8 @@ class Pool(QuantumAPIDictWrapper):
pFormatted = {'id': self.id,
'name': self.name,
'description': self.description,
'protocol': self.protocol}
'protocol': self.protocol,
'health_monitors': self.health_monitors}
try:
pFormatted['subnet_id'] = self.subnet_id
pFormatted['subnet_name'] = subnet_get(
@ -204,7 +205,7 @@ def pool_stats(request, pool_id, **kwargs):
def pool_health_monitor_create(request, **kwargs):
"""Create a health monitor and associate with pool
"""Create a health monitor
:param request: request context
:param type: type of monitor
@ -229,9 +230,7 @@ def pool_health_monitor_create(request, **kwargs):
body['health_monitor']['expected_codes'] = kwargs['expected_codes']
mon = quantumclient(request).create_health_monitor(body).get(
'health_monitor')
body = {'health_monitor': {'id': mon['id']}}
quantumclient(request).associate_health_monitor(
kwargs['pool_id'], body)
return PoolMonitor(mon)
@ -294,3 +293,29 @@ def member_update(request, member_id, **kwargs):
def member_delete(request, mem_id):
quantumclient(request).delete_member(mem_id)
def pool_monitor_association_create(request, **kwargs):
"""Associate a health monitor with pool
:param request: request context
:param monitor_id: id of monitor
:param pool_id: id of pool
"""
body = {'health_monitor': {'id': kwargs['monitor_id'], }}
quantumclient(request).associate_health_monitor(
kwargs['pool_id'], body)
def pool_monitor_association_delete(request, **kwargs):
"""Disassociate a health monitor from pool
:param request: request context
:param monitor_id: id of monitor
:param pool_id: id of pool
"""
quantumclient(request).disassociate_health_monitor(
kwargs['pool_id'], kwargs['monitor_id'])

View File

@ -19,8 +19,11 @@ from django.core.urlresolvers import reverse
from django.utils import http
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from openstack_dashboard import api
import logging
@ -155,6 +158,36 @@ def get_vip_link(pool):
args=(http.urlquote(pool.vip_id),))
class AddPMAssociationLink(tables.LinkAction):
name = "addassociation"
verbose_name = _("Add Health Monitor")
url = "horizon:project:loadbalancers:addassociation"
classes = ("btn-add",)
def allowed(self, request, datum=None):
try:
monitors = api.lbaas.pool_health_monitors_get(request)
for m in monitors:
if m.id not in datum['health_monitors']:
return True
except:
exceptions.handle(request,
_('Failed to retrieve health monitors.'))
return False
class DeletePMAssociationLink(tables.LinkAction):
name = "deleteassociation"
verbose_name = _("Delete Health Monitor")
url = "horizon:project:loadbalancers:deleteassociation"
classes = ("btn-delete", "btn-danger")
def allowed(self, request, datum=None):
if datum and not datum['health_monitors']:
return False
return True
class PoolsTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
@ -170,7 +203,8 @@ class PoolsTable(tables.DataTable):
verbose_name = _("Pools")
table_actions = (AddPoolLink, DeletePoolLink)
row_actions = (UpdatePoolLink, AddVipLink, UpdateVipLink,
DeleteVipLink, DeletePoolLink)
DeleteVipLink, AddPMAssociationLink,
DeletePMAssociationLink, DeletePoolLink)
def get_pool_link(member):

View File

@ -19,8 +19,10 @@ from openstack_dashboard.test import helpers as test
from .workflows import AddMember
from .workflows import AddMonitor
from .workflows import AddPMAssociation
from .workflows import AddPool
from .workflows import AddVip
from .workflows import DeletePMAssociation
class LoadBalancerTests(test.TestCase):
@ -49,6 +51,9 @@ class LoadBalancerTests(test.TestCase):
UPDATEMEMBER_PATH = 'horizon:%s:loadbalancers:updatemember' % DASHBOARD
UPDATEMONITOR_PATH = 'horizon:%s:loadbalancers:updatemonitor' % DASHBOARD
ADDASSOC_PATH = 'horizon:%s:loadbalancers:addassociation' % DASHBOARD
DELETEASSOC_PATH = 'horizon:%s:loadbalancers:deleteassociation' % DASHBOARD
def set_up_expect(self):
# retrieve pools
subnet = self.subnets.first()
@ -346,17 +351,13 @@ class LoadBalancerTests(test.TestCase):
expected_objs = ['<AddVipStep: addvipaction>', ]
self.assertQuerysetEqual(workflow.steps, expected_objs)
@test.create_stubs({api.lbaas: ('pools_get',
'pool_health_monitor_create')})
@test.create_stubs({api.lbaas: ('pool_health_monitor_create', )})
def test_add_monitor_post(self):
monitor = self.monitors.first()
api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list())
api.lbaas.pool_health_monitor_create(
IsA(http.HttpRequest),
type=monitor.type,
pool_id=monitor.pool_id,
delay=monitor.delay,
timeout=monitor.timeout,
max_retries=monitor.max_retries,
@ -369,7 +370,6 @@ class LoadBalancerTests(test.TestCase):
self.mox.ReplayAll()
form_data = {'type': monitor.type,
'pool_id': monitor.pool_id,
'delay': monitor.delay,
'timeout': monitor.timeout,
'max_retries': monitor.max_retries,
@ -383,16 +383,10 @@ class LoadBalancerTests(test.TestCase):
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api.lbaas: ('pools_get',)})
def test_add_monitor_post_with_error(self):
monitor = self.monitors.first()
api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list())
self.mox.ReplayAll()
form_data = {'type': monitor.type,
'pool_id': monitor.pool_id,
'delay': 0,
'timeout': 0,
'max_retries': 11,
@ -405,16 +399,10 @@ class LoadBalancerTests(test.TestCase):
self.assertFormErrors(res, 3)
@test.create_stubs({api.lbaas: ('pools_get',)})
def test_add_monitor_post_with_httpmethod_error(self):
monitor = self.monitors.first()
api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list())
self.mox.ReplayAll()
form_data = {'type': 'http',
'pool_id': monitor.pool_id,
'delay': monitor.delay,
'timeout': monitor.timeout,
'max_retries': monitor.max_retries,
@ -427,12 +415,7 @@ class LoadBalancerTests(test.TestCase):
self.assertFormErrors(res, 3)
@test.create_stubs({api.lbaas: ('pools_get',)})
def test_add_monitor_get(self):
api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list())
self.mox.ReplayAll()
res = self.client.get(reverse(self.ADDMONITOR_PATH))
workflow = res.context['workflow']
@ -708,3 +691,101 @@ class LoadBalancerTests(test.TestCase):
self.assertTemplateUsed(
res, 'project/loadbalancers/updatemonitor.html')
@test.create_stubs({api.lbaas: ('pool_get', 'pool_health_monitors_get',
'pool_monitor_association_create')})
def test_add_pool_monitor_association_post(self):
pool = self.pools.first()
monitors = self.monitors.list()
monitor = self.monitors.list()[1]
api.lbaas.pool_get(IsA(http.HttpRequest), pool.id).AndReturn(pool)
api.lbaas.pool_health_monitors_get(
IsA(http.HttpRequest)).AndReturn(monitors)
api.lbaas.pool_monitor_association_create(
IsA(http.HttpRequest),
monitor_id=monitor.id,
pool_id=pool.id,
pool_monitors=pool.health_monitors,
pool_name=pool.name).AndReturn(None)
self.mox.ReplayAll()
form_data = {'monitor_id': monitor.id,
'pool_id': pool.id,
'pool_monitors': pool.health_monitors,
'pool_name': pool.name}
res = self.client.post(
reverse(self.ADDASSOC_PATH, args=(pool.id,)), form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api.lbaas: ('pool_get', 'pool_health_monitors_get')})
def test_add_pool_monitor_association_get(self):
pool = self.pools.first()
monitors = self.monitors.list()
api.lbaas.pool_get(IsA(http.HttpRequest), pool.id).AndReturn(pool)
api.lbaas.pool_health_monitors_get(
IsA(http.HttpRequest)).AndReturn(monitors)
self.mox.ReplayAll()
res = self.client.get(reverse(self.ADDASSOC_PATH, args=(pool.id,)))
workflow = res.context['workflow']
self.assertTemplateUsed(res, WorkflowView.template_name)
self.assertEqual(workflow.name, AddPMAssociation.name)
expected_objs = ['<AddPMAssociationStep: addpmassociationaction>', ]
self.assertQuerysetEqual(workflow.steps, expected_objs)
@test.create_stubs({api.lbaas: ('pool_get',
'pool_monitor_association_delete')})
def test_delete_pool_monitor_association_post(self):
pool = self.pools.first()
monitor = self.monitors.first()
api.lbaas.pool_get(IsA(http.HttpRequest), pool.id).AndReturn(pool)
api.lbaas.pool_monitor_association_delete(
IsA(http.HttpRequest),
monitor_id=monitor.id,
pool_id=pool.id,
pool_monitors=pool.health_monitors,
pool_name=pool.name).AndReturn(None)
self.mox.ReplayAll()
form_data = {'monitor_id': monitor.id,
'pool_id': pool.id,
'pool_monitors': pool.health_monitors,
'pool_name': pool.name}
res = self.client.post(
reverse(self.DELETEASSOC_PATH, args=(pool.id,)), form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api.lbaas: ('pool_get',)})
def test_delete_pool_monitor_association_get(self):
pool = self.pools.first()
api.lbaas.pool_get(IsA(http.HttpRequest), pool.id).AndReturn(pool)
self.mox.ReplayAll()
res = self.client.get(
reverse(self.DELETEASSOC_PATH, args=(pool.id,)))
workflow = res.context['workflow']
self.assertTemplateUsed(res, WorkflowView.template_name)
self.assertEqual(workflow.name, DeletePMAssociation.name)
expected_objs = [
'<DeletePMAssociationStep: deletepmassociationaction>', ]
self.assertQuerysetEqual(workflow.steps, expected_objs)

View File

@ -25,6 +25,10 @@ from openstack_dashboard.dashboards.project.loadbalancers.views import \
AddPoolView
from openstack_dashboard.dashboards.project.loadbalancers.views import \
AddVipView
from openstack_dashboard.dashboards.project.loadbalancers.views import \
AddPMAssociationView
from openstack_dashboard.dashboards.project.loadbalancers.views import \
DeletePMAssociationView
from openstack_dashboard.dashboards.project.loadbalancers.views import \
IndexView
from openstack_dashboard.dashboards.project.loadbalancers.views import \
@ -59,6 +63,10 @@ urlpatterns = patterns(
url(r'^addmonitor$', AddMonitorView.as_view(), name='addmonitor'),
url(r'^updatemonitor/(?P<monitor_id>[^/]+)/$',
UpdateMonitorView.as_view(), name='updatemonitor'),
url(r'^association/add/(?P<pool_id>[^/]+)/$',
AddPMAssociationView.as_view(), name='addassociation'),
url(r'^association/delete/(?P<pool_id>[^/]+)/$',
DeletePMAssociationView.as_view(), name='deleteassociation'),
url(r'^pool/(?P<pool_id>[^/]+)/$',
PoolDetailsView.as_view(), name='pooldetails'),
url(r'^vip/(?P<vip_id>[^/]+)/$',

View File

@ -47,10 +47,14 @@ from openstack_dashboard.dashboards.project.loadbalancers.workflows import \
AddMember
from openstack_dashboard.dashboards.project.loadbalancers.workflows import \
AddMonitor
from openstack_dashboard.dashboards.project.loadbalancers.workflows import \
AddPMAssociation
from openstack_dashboard.dashboards.project.loadbalancers.workflows import \
AddPool
from openstack_dashboard.dashboards.project.loadbalancers.workflows import \
AddVip
from openstack_dashboard.dashboards.project.loadbalancers.workflows import \
DeletePMAssociation
import re
@ -300,3 +304,35 @@ class UpdateMonitorView(forms.ModalFormView):
'timeout': monitor['timeout'],
'max_retries': monitor['max_retries'],
'admin_state_up': monitor['admin_state_up']}
class AddPMAssociationView(workflows.WorkflowView):
workflow_class = AddPMAssociation
def get_initial(self):
initial = super(AddPMAssociationView, self).get_initial()
initial['pool_id'] = self.kwargs['pool_id']
try:
pool = api.lbaas.pool_get(self.request, initial['pool_id'])
initial['pool_name'] = pool.name
initial['pool_monitors'] = pool.health_monitors
except:
msg = _('Unable to retrieve pool.')
exceptions.handle(self.request, msg)
return initial
class DeletePMAssociationView(workflows.WorkflowView):
workflow_class = DeletePMAssociation
def get_initial(self):
initial = super(DeletePMAssociationView, self).get_initial()
initial['pool_id'] = self.kwargs['pool_id']
try:
pool = api.lbaas.pool_get(self.request, initial['pool_id'])
initial['pool_name'] = pool.name
initial['pool_monitors'] = pool.health_monitors
except:
msg = _('Unable to retrieve pool.')
exceptions.handle(self.request, msg)
return initial

View File

@ -358,7 +358,6 @@ class AddMember(workflows.Workflow):
class AddMonitorAction(workflows.Action):
pool_id = forms.ChoiceField(label=_("Pool"))
type = forms.ChoiceField(
label=_("Type"),
choices=[('ping', _('PING')),
@ -428,16 +427,6 @@ class AddMonitorAction(workflows.Action):
def __init__(self, request, *args, **kwargs):
super(AddMonitorAction, self).__init__(request, *args, **kwargs)
pool_id_choices = [('', _("Select a Pool"))]
try:
pools = api.lbaas.pools_get(request)
for p in pools:
pool_id_choices.append((p.id, p.name))
except:
exceptions.handle(request,
_('Unable to retrieve pools list.'))
self.fields['pool_id'].choices = pool_id_choices
def clean(self):
cleaned_data = super(AddMonitorAction, self).clean()
type_opt = cleaned_data.get('type')
@ -463,8 +452,8 @@ class AddMonitorAction(workflows.Action):
class Meta:
name = _("Add New Monitor")
permissions = ('openstack.services.network',)
help_text = _("Create a monitor for a pool.\n\n"
"Select target pool and type of monitoring. "
help_text = _("Create a monitor template.\n\n"
"Select type of monitoring. "
"Specify delay, timeout, and retry limits "
"required by the monitor. "
"Specify method, URL path, and expected "
@ -473,7 +462,7 @@ class AddMonitorAction(workflows.Action):
class AddMonitorStep(workflows.Step):
action_class = AddMonitorAction
contributes = ("pool_id", "type", "delay", "timeout", "max_retries",
contributes = ("type", "delay", "timeout", "max_retries",
"http_method", "url_path", "expected_codes",
"admin_state_up")
@ -500,3 +489,121 @@ class AddMonitor(workflows.Workflow):
except:
exceptions.handle(request, _("Unable to add monitor."))
return False
class AddPMAssociationAction(workflows.Action):
monitor_id = forms.ChoiceField(label=_("Monitor"))
def __init__(self, request, *args, **kwargs):
super(AddPMAssociationAction, self).__init__(request, *args, **kwargs)
def populate_monitor_id_choices(self, request, context):
self.fields['monitor_id'].label = _("Select a monitor template "
"for %s" % context['pool_name'])
monitor_id_choices = [('', _("Select a Monitor"))]
try:
monitors = api.lbaas.pool_health_monitors_get(request)
for m in monitors:
if m.id not in context['pool_monitors']:
monitor_id_choices.append((m.id, m.id))
except:
exceptions.handle(request,
_('Unable to retrieve monitors list.'))
self.fields['monitor_id'].choices = monitor_id_choices
return monitor_id_choices
class Meta:
name = _("Association Details")
permissions = ('openstack.services.network',)
help_text = _("Associate a health monitor with target pool.")
class AddPMAssociationStep(workflows.Step):
action_class = AddPMAssociationAction
depends_on = ("pool_id", "pool_name", "pool_monitors")
contributes = ("monitor_id",)
def contribute(self, data, context):
context = super(AddPMAssociationStep, self).contribute(data, context)
if data:
return context
class AddPMAssociation(workflows.Workflow):
slug = "addassociation"
name = _("Add Association")
finalize_button_name = _("Add")
success_message = _('Added association.')
failure_message = _('Unable to add association.')
success_url = "horizon:project:loadbalancers:index"
default_steps = (AddPMAssociationStep,)
def handle(self, request, context):
try:
context['monitor_id'] = api.lbaas.pool_monitor_association_create(
request, **context)
return True
except:
exceptions.handle(request, _("Unable to add association."))
return False
class DeletePMAssociationAction(workflows.Action):
monitor_id = forms.ChoiceField(label=_("Monitor"))
def __init__(self, request, *args, **kwargs):
super(DeletePMAssociationAction, self).__init__(
request, *args, **kwargs)
def populate_monitor_id_choices(self, request, context):
self.fields['monitor_id'].label = _("Select a health monitor of %s" %
context['pool_name'])
monitor_id_choices = [('', _("Select a Monitor"))]
try:
for m_id in context['pool_monitors']:
monitor_id_choices.append((m_id, m_id))
except:
exceptions.handle(request,
_('Unable to retrieve monitors list.'))
self.fields['monitor_id'].choices = monitor_id_choices
return monitor_id_choices
class Meta:
name = _("Association Details")
permissions = ('openstack.services.network',)
help_text = _("Disassociate a health monitor from target pool. ")
class DeletePMAssociationStep(workflows.Step):
action_class = DeletePMAssociationAction
depends_on = ("pool_id", "pool_name", "pool_monitors")
contributes = ("monitor_id",)
def contribute(self, data, context):
context = super(DeletePMAssociationStep, self).contribute(
data, context)
if data:
return context
class DeletePMAssociation(workflows.Workflow):
slug = "deleteassociation"
name = _("Delete Association")
finalize_button_name = _("Delete")
success_message = _('Deleted association.')
failure_message = _('Unable to delete association.')
success_url = "horizon:project:loadbalancers:index"
default_steps = (DeletePMAssociationStep,)
def handle(self, request, context):
try:
context['monitor_id'] = api.lbaas.pool_monitor_association_delete(
request, **context)
return True
except:
exceptions.handle(request, _("Unable to delete association."))
return False

View File

@ -196,8 +196,7 @@ class LbaasApiTests(test.APITestCase):
pool['pool']['id'], **form_data)
self.assertIsInstance(ret_val, api.lbaas.Pool)
@test.create_stubs({quantumclient: ('create_health_monitor',
'associate_health_monitor')})
@test.create_stubs({quantumclient: ('create_health_monitor',)})
def test_pool_health_monitor_create(self):
form_data = {'type': 'PING',
'delay': '10',
@ -205,13 +204,6 @@ class LbaasApiTests(test.APITestCase):
'max_retries': '10',
'admin_state_up': True
}
form_data_with_pool_id = {
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
'type': 'PING',
'delay': '10',
'timeout': '10',
'max_retries': '10',
'admin_state_up': True}
monitor = {'health_monitor': {
'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
'type': 'PING',
@ -219,16 +211,12 @@ class LbaasApiTests(test.APITestCase):
'timeout': '10',
'max_retries': '10',
'admin_state_up': True}}
monitor_id = {'health_monitor': {
'id': 'abcdef-c3eb-4fee-9763-12de3338041e'}}
quantumclient.create_health_monitor({
'health_monitor': form_data}).AndReturn(monitor)
quantumclient.associate_health_monitor(
form_data_with_pool_id['pool_id'], monitor_id)
self.mox.ReplayAll()
ret_val = api.lbaas.pool_health_monitor_create(
self.request, **form_data_with_pool_id)
self.request, **form_data)
self.assertIsInstance(ret_val, api.lbaas.PoolMonitor)
@test.create_stubs({quantumclient: ('list_health_monitors',)})

View File

@ -264,6 +264,7 @@ def data(TEST):
'subnet_id': TEST.subnets.first().id,
'protocol': 'HTTP',
'lb_method': 'ROUND_ROBIN',
'health_monitors': ['d4a0500f-db2b-4cc4-afcf-ec026febff96'],
'admin_state_up': True}
TEST.api_pools.add(pool_dict)
TEST.pools.add(Pool(pool_dict))
@ -337,6 +338,7 @@ def data(TEST):
'subnet_id': TEST.subnets.first().id,
'protocol': 'HTTPS',
'lb_method': 'ROUND_ROBIN',
'health_monitors': ['d4a0500f-db2b-4cc4-afcf-ec026febff97'],
'admin_state_up': True}
TEST.api_pools.add(pool_dict)
TEST.pools.add(Pool(pool_dict))
@ -344,7 +346,6 @@ def data(TEST):
# 1st monitor
monitor_dict = {'id': 'd4a0500f-db2b-4cc4-afcf-ec026febff96',
'type': 'ping',
'pool_id': pool_dict['id'],
'delay': 10,
'timeout': 10,
'max_retries': 10,
@ -358,7 +359,6 @@ def data(TEST):
# 2nd monitor
monitor_dict = {'id': 'd4a0500f-db2b-4cc4-afcf-ec026febff97',
'type': 'ping',
'pool_id': pool_dict['id'],
'delay': 10,
'timeout': 10,
'max_retries': 10,