Merge "blueprint quantum-lbaas"
This commit is contained in:
commit
fded5260be
@ -5,6 +5,7 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
# Copyright 2013 Big Switch Networks
|
||||
#
|
||||
# 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
|
||||
@ -32,11 +33,12 @@ In other words, Horizon developers not working on openstack_dashboard.api
|
||||
shouldn't need to understand the finer details of APIs for
|
||||
Keystone/Nova/Glance/Swift et. al.
|
||||
"""
|
||||
import base
|
||||
import cinder
|
||||
import glance
|
||||
import keystone
|
||||
import network
|
||||
import nova
|
||||
import quantum
|
||||
import swift
|
||||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api import cinder
|
||||
from openstack_dashboard.api import glance
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.api import network
|
||||
from openstack_dashboard.api import nova
|
||||
from openstack_dashboard.api import quantum
|
||||
from openstack_dashboard.api import lbaas
|
||||
from openstack_dashboard.api import swift
|
||||
|
291
openstack_dashboard/api/lbaas.py
Normal file
291
openstack_dashboard/api/lbaas.py
Normal file
@ -0,0 +1,291 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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 __future__ import absolute_import
|
||||
|
||||
from openstack_dashboard.api.quantum import QuantumAPIDictWrapper
|
||||
from openstack_dashboard.api.quantum import quantumclient
|
||||
from openstack_dashboard.api.quantum import subnet_get
|
||||
|
||||
|
||||
class Vip(QuantumAPIDictWrapper):
|
||||
"""Wrapper for quantum load balancer vip"""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(Vip, self).__init__(apiresource)
|
||||
|
||||
|
||||
class Pool(QuantumAPIDictWrapper):
|
||||
"""Wrapper for quantum load balancer pool"""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(Pool, self).__init__(apiresource)
|
||||
|
||||
class AttributeDict(dict):
|
||||
def __getattr__(self, attr):
|
||||
return self[attr]
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
self[attr] = value
|
||||
|
||||
def readable(self, request):
|
||||
pFormatted = {'id': self.id,
|
||||
'name': self.name,
|
||||
'description': self.description,
|
||||
'protocol': self.protocol}
|
||||
try:
|
||||
pFormatted['subnet_id'] = self.subnet_id
|
||||
pFormatted['subnet_name'] = subnet_get(
|
||||
request, self.subnet_id).cidr
|
||||
except:
|
||||
pFormatted['subnet_id'] = self.subnet_id
|
||||
pFormatted['subnet_name'] = self.subnet_id
|
||||
|
||||
if self.vip_id is not None:
|
||||
try:
|
||||
pFormatted['vip_id'] = self.vip_id
|
||||
pFormatted['vip_name'] = vip_get(
|
||||
request, self.vip_id).name
|
||||
except:
|
||||
pFormatted['vip_id'] = self.vip_id
|
||||
pFormatted['vip_name'] = self.vip_id
|
||||
else:
|
||||
pFormatted['vip_id'] = None
|
||||
pFormatted['vip_name'] = None
|
||||
|
||||
return self.AttributeDict(pFormatted)
|
||||
|
||||
|
||||
class Member(QuantumAPIDictWrapper):
|
||||
"""Wrapper for quantum load balancer member"""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(Member, self).__init__(apiresource)
|
||||
|
||||
class AttributeDict(dict):
|
||||
def __getattr__(self, attr):
|
||||
return self[attr]
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
self[attr] = value
|
||||
|
||||
def readable(self, request):
|
||||
mFormatted = {'id': self.id,
|
||||
'address': self.address,
|
||||
'protocol_port': self.protocol_port}
|
||||
try:
|
||||
mFormatted['pool_id'] = self.pool_id
|
||||
mFormatted['pool_name'] = pool_get(
|
||||
request, self.pool_id).name
|
||||
except:
|
||||
mFormatted['pool_id'] = self.pool_id
|
||||
mFormatted['pool_name'] = self.pool_id
|
||||
|
||||
return self.AttributeDict(mFormatted)
|
||||
|
||||
|
||||
class PoolStats(QuantumAPIDictWrapper):
|
||||
"""Wrapper for quantum load balancer pool stats"""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(PoolStats, self).__init__(apiresource)
|
||||
|
||||
|
||||
class PoolMonitor(QuantumAPIDictWrapper):
|
||||
"""Wrapper for quantum load balancer pool health monitor"""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(PoolMonitor, self).__init__(apiresource)
|
||||
|
||||
|
||||
def vip_create(request, **kwargs):
|
||||
"""Create a vip for a specified pool.
|
||||
|
||||
:param request: request context
|
||||
:param address: virtual IP address
|
||||
:param name: name for vip
|
||||
:param description: description for vip
|
||||
:param subnet_id: subnet_id for subnet of vip
|
||||
:param protocol_port: transport layer port number for vip
|
||||
:returns: Vip object
|
||||
"""
|
||||
body = {'vip': {'address': kwargs['address'],
|
||||
'name': kwargs['name'],
|
||||
'description': kwargs['description'],
|
||||
'subnet_id': kwargs['subnet_id'],
|
||||
'protocol_port': kwargs['protocol_port'],
|
||||
'protocol': kwargs['protocol'],
|
||||
'pool_id': kwargs['pool_id'],
|
||||
'session_persistence': kwargs['session_persistence'],
|
||||
'connection_limit': kwargs['connection_limit'],
|
||||
'admin_state_up': kwargs['admin_state_up']
|
||||
}}
|
||||
vip = quantumclient(request).create_vip(body).get('vip')
|
||||
return Vip(vip)
|
||||
|
||||
|
||||
def vips_get(request, **kwargs):
|
||||
vips = quantumclient(request).list_vips().get('vips')
|
||||
return [Vip(v) for v in vips]
|
||||
|
||||
|
||||
def vip_get(request, vip_id):
|
||||
vip = quantumclient(request).show_vip(vip_id).get('vip')
|
||||
return Vip(vip)
|
||||
|
||||
|
||||
# not linked to UI yet
|
||||
def vip_update(request, vip_id, **kwargs):
|
||||
vip = quantumclient(request).update_vip(vip_id, kwargs).get('vip')
|
||||
return Vip(vip)
|
||||
|
||||
|
||||
def vip_delete(request, vip_id):
|
||||
quantumclient(request).delete_vip(vip_id)
|
||||
|
||||
|
||||
def pool_create(request, **kwargs):
|
||||
"""Create a pool for specified protocol
|
||||
|
||||
:param request: request context
|
||||
:param name: name for pool
|
||||
:param description: description for pool
|
||||
:param subnet_id: subnet_id for subnet of pool
|
||||
:param protocol: load balanced protocol
|
||||
:param lb_method: load balancer method
|
||||
:param admin_state_up: admin state (default on)
|
||||
"""
|
||||
body = {'pool': {'name': kwargs['name'],
|
||||
'description': kwargs['description'],
|
||||
'subnet_id': kwargs['subnet_id'],
|
||||
'protocol': kwargs['protocol'],
|
||||
'lb_method': kwargs['lb_method'],
|
||||
'admin_state_up': kwargs['admin_state_up']
|
||||
}}
|
||||
pool = quantumclient(request).create_pool(body).get('pool')
|
||||
return Pool(pool)
|
||||
|
||||
|
||||
def pools_get(request, **kwargs):
|
||||
pools = quantumclient(request).list_pools().get('pools')
|
||||
return [Pool(p) for p in pools]
|
||||
|
||||
|
||||
def pool_get(request, pool_id):
|
||||
pool = quantumclient(request).show_pool(pool_id).get('pool')
|
||||
return Pool(pool)
|
||||
|
||||
|
||||
def pool_update(request, pool_id, **kwargs):
|
||||
pool = quantumclient(request).update_pool(pool_id, kwargs).get('pool')
|
||||
return Pool(pool)
|
||||
|
||||
|
||||
def pool_delete(request, pool):
|
||||
quantumclient(request).delete_pool(pool)
|
||||
|
||||
|
||||
# not linked to UI yet
|
||||
def pool_stats(request, pool_id, **kwargs):
|
||||
stats = quantumclient(request).retrieve_pool_stats(pool_id, **kwargs)
|
||||
return PoolStats(stats)
|
||||
|
||||
|
||||
def pool_health_monitor_create(request, **kwargs):
|
||||
"""Create a health monitor and associate with pool
|
||||
|
||||
:param request: request context
|
||||
:param type: type of monitor
|
||||
:param delay: delay of monitor
|
||||
:param timeout: timeout of monitor
|
||||
:param max_retries: max retries [1..10]
|
||||
:param http_method: http method
|
||||
:param url_path: url path
|
||||
:param expected_codes: http return code
|
||||
:param admin_state_up: admin state
|
||||
"""
|
||||
body = {'health_monitor': {'type': kwargs['type'],
|
||||
'delay': kwargs['delay'],
|
||||
'timeout': kwargs['timeout'],
|
||||
'max_retries': kwargs['max_retries'],
|
||||
'http_method': kwargs['http_method'],
|
||||
'url_path': kwargs['url_path'],
|
||||
'expected_codes': kwargs['expected_codes'],
|
||||
'admin_state_up': kwargs['admin_state_up']
|
||||
}}
|
||||
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)
|
||||
|
||||
|
||||
def pool_health_monitors_get(request, **kwargs):
|
||||
monitors = quantumclient(request
|
||||
).list_health_monitors().get('health_monitors')
|
||||
return [PoolMonitor(m) for m in monitors]
|
||||
|
||||
|
||||
def pool_health_monitor_get(request, monitor_id):
|
||||
monitor = quantumclient(request
|
||||
).show_health_monitor(monitor_id
|
||||
).get('health_monitor')
|
||||
return PoolMonitor(monitor)
|
||||
|
||||
|
||||
def pool_health_monitor_delete(request, mon_id):
|
||||
quantumclient(request).delete_health_monitor(mon_id)
|
||||
|
||||
|
||||
def member_create(request, **kwargs):
|
||||
"""Create a load balance member
|
||||
|
||||
:param request: request context
|
||||
:param pool_id: pool_id of pool for member
|
||||
:param address: IP address
|
||||
:param protocol_port: transport layer port number
|
||||
:param weight: weight for member
|
||||
:param admin_state_up: admin_state
|
||||
"""
|
||||
body = {'member': {'pool_id': kwargs['pool_id'],
|
||||
'address': kwargs['address'],
|
||||
'protocol_port': kwargs['protocol_port'],
|
||||
'weight': kwargs['weight'],
|
||||
'admin_state_up': kwargs['admin_state_up']
|
||||
}}
|
||||
member = quantumclient(request).create_member(body).get('member')
|
||||
return Member(member)
|
||||
|
||||
|
||||
def members_get(request, **kwargs):
|
||||
members = quantumclient(request).list_members().get('members')
|
||||
return [Member(m) for m in members]
|
||||
|
||||
|
||||
def member_get(request, member_id):
|
||||
member = quantumclient(request).show_member(member_id).get('member')
|
||||
return Member(member)
|
||||
|
||||
|
||||
# not linked to UI yet
|
||||
def member_update(request, member_id, **kwargs):
|
||||
member = quantumclient(request).update_member(member_id, kwargs)
|
||||
return Member(member)
|
||||
|
||||
|
||||
def member_delete(request, mem_id):
|
||||
quantumclient(request).delete_member(mem_id)
|
@ -29,7 +29,8 @@ class BasePanels(horizon.PanelGroup):
|
||||
'access_and_security',
|
||||
'networks',
|
||||
'routers',
|
||||
'network_topology')
|
||||
'network_topology',
|
||||
'loadbalancers')
|
||||
|
||||
|
||||
class ObjectStorePanels(horizon.PanelGroup):
|
||||
|
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
@ -0,0 +1,16 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.project import dashboard
|
||||
|
||||
|
||||
class LoadBalancer(horizon.Panel):
|
||||
name = _("Load Balancers")
|
||||
slug = "loadbalancers"
|
||||
permissions = ('openstack.services.network',)
|
||||
|
||||
if hasattr(settings, 'OPENSTACK_QUANTUM_NETWORK'):
|
||||
if getattr(settings, 'OPENSTACK_QUANTUM_NETWORK')['enable_lb']:
|
||||
dashboard.Project.register(LoadBalancer)
|
162
openstack_dashboard/dashboards/project/loadbalancers/tables.py
Normal file
162
openstack_dashboard/dashboards/project/loadbalancers/tables.py
Normal file
@ -0,0 +1,162 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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.
|
||||
|
||||
import logging
|
||||
|
||||
from django.utils import http
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from horizon import tables
|
||||
from openstack_dashboard import api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddPoolLink(tables.LinkAction):
|
||||
name = "addpool"
|
||||
verbose_name = _("Add Pool")
|
||||
url = "horizon:project:loadbalancers:addpool"
|
||||
classes = ("btn-addpool",)
|
||||
|
||||
|
||||
class AddVipLink(tables.LinkAction):
|
||||
name = "addvip"
|
||||
verbose_name = _("Add Vip")
|
||||
classes = ("btn-addvip",)
|
||||
|
||||
def get_link_url(self, pool):
|
||||
base_url = reverse("horizon:project:loadbalancers:addvip",
|
||||
kwargs={'pool_id': pool.id})
|
||||
return base_url
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
if datum and datum.vip_id:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class AddMemberLink(tables.LinkAction):
|
||||
name = "addmember"
|
||||
verbose_name = _("Add Member")
|
||||
url = "horizon:project:loadbalancers:addmember"
|
||||
classes = ("btn-addmember",)
|
||||
|
||||
|
||||
class AddMonitorLink(tables.LinkAction):
|
||||
name = "addmonitor"
|
||||
verbose_name = _("Add Monitor")
|
||||
url = "horizon:project:loadbalancers:addmonitor"
|
||||
classes = ("btn-addmonitor",)
|
||||
|
||||
|
||||
class DeleteVipLink(tables.DeleteAction):
|
||||
name = "deletevip"
|
||||
action_present = _("Delete")
|
||||
action_past = _("Scheduled deletion of")
|
||||
data_type_singular = _("Vip")
|
||||
data_type_plural = _("Vips")
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
if datum and not datum.vip_id:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class DeletePoolLink(tables.DeleteAction):
|
||||
name = "deletepool"
|
||||
action_present = _("Delete")
|
||||
action_past = _("Scheduled deletion of")
|
||||
data_type_singular = _("Pool")
|
||||
data_type_plural = _("Pools")
|
||||
|
||||
|
||||
class DeleteMonitorLink(tables.DeleteAction):
|
||||
name = "deletemonitor"
|
||||
action_present = _("Delete")
|
||||
action_past = _("Scheduled deletion of")
|
||||
data_type_singular = _("Monitor")
|
||||
data_type_plural = _("Monitors")
|
||||
|
||||
|
||||
class DeleteMemberLink(tables.DeleteAction):
|
||||
name = "deletemember"
|
||||
action_present = _("Delete")
|
||||
action_past = _("Scheduled deletion of")
|
||||
data_type_singular = _("Member")
|
||||
data_type_plural = _("Members")
|
||||
|
||||
|
||||
def get_vip_link(pool):
|
||||
return reverse("horizon:project:loadbalancers:vipdetails",
|
||||
args=(http.urlquote(pool.vip_id),))
|
||||
|
||||
|
||||
class PoolsTable(tables.DataTable):
|
||||
name = tables.Column("name",
|
||||
verbose_name=_("Name"),
|
||||
link="horizon:project:loadbalancers:pooldetails")
|
||||
description = tables.Column('description', verbose_name=_("Description"))
|
||||
subnet_name = tables.Column('subnet_name', verbose_name=_("Subnet"))
|
||||
protocol = tables.Column('protocol', verbose_name=_("Protocol"))
|
||||
vip_name = tables.Column('vip_name', verbose_name=_("VIP"),
|
||||
link=get_vip_link)
|
||||
|
||||
class Meta:
|
||||
name = "poolstable"
|
||||
verbose_name = _("Pools")
|
||||
table_actions = (AddPoolLink, DeletePoolLink)
|
||||
row_actions = (AddVipLink, DeleteVipLink, DeletePoolLink)
|
||||
|
||||
|
||||
def get_pool_link(member):
|
||||
return reverse("horizon:project:loadbalancers:pooldetails",
|
||||
args=(http.urlquote(member.pool_id),))
|
||||
|
||||
|
||||
def get_member_link(member):
|
||||
return reverse("horizon:project:loadbalancers:memberdetails",
|
||||
args=(http.urlquote(member.id),))
|
||||
|
||||
|
||||
class MembersTable(tables.DataTable):
|
||||
address = tables.Column('address',
|
||||
verbose_name=_("IP Address"),
|
||||
link=get_member_link)
|
||||
protocol_port = tables.Column('protocol_port',
|
||||
verbose_name=_("Protocol Port"))
|
||||
pool_name = tables.Column("pool_name",
|
||||
verbose_name=_("Pool"), link=get_pool_link)
|
||||
|
||||
class Meta:
|
||||
name = "memberstable"
|
||||
verbose_name = _("Members")
|
||||
table_actions = (AddMemberLink, DeleteMemberLink)
|
||||
row_actions = (DeleteMemberLink,)
|
||||
|
||||
|
||||
class MonitorsTable(tables.DataTable):
|
||||
id = tables.Column("id",
|
||||
verbose_name=_("ID"),
|
||||
link="horizon:project:loadbalancers:monitordetails")
|
||||
monitorType = tables.Column('type', verbose_name=_("Monitor Type"))
|
||||
|
||||
class Meta:
|
||||
name = "monitorstable"
|
||||
verbose_name = _("Monitors")
|
||||
table_actions = (AddMonitorLink, DeleteMonitorLink)
|
||||
row_actions = (DeleteMonitorLink,)
|
170
openstack_dashboard/dashboards/project/loadbalancers/tabs.py
Normal file
170
openstack_dashboard/dashboards/project/loadbalancers/tabs.py
Normal file
@ -0,0 +1,170 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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.
|
||||
|
||||
import re
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from .tables import PoolsTable, MembersTable, MonitorsTable
|
||||
|
||||
|
||||
class PoolsTab(tabs.TableTab):
|
||||
table_classes = (PoolsTable,)
|
||||
name = _("Pools")
|
||||
slug = "pools"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_poolstable_data(self):
|
||||
try:
|
||||
pools = api.lbaas.pools_get(self.tab_group.request)
|
||||
poolsFormatted = [p.readable(self.tab_group.request) for
|
||||
p in pools]
|
||||
except:
|
||||
poolsFormatted = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
_('Unable to retrieve pools list.'))
|
||||
return poolsFormatted
|
||||
|
||||
|
||||
class MembersTab(tabs.TableTab):
|
||||
table_classes = (MembersTable,)
|
||||
name = _("Members")
|
||||
slug = "members"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_memberstable_data(self):
|
||||
try:
|
||||
members = api.lbaas.members_get(self.tab_group.request)
|
||||
membersFormatted = [m.readable(self.tab_group.request) for
|
||||
m in members]
|
||||
except:
|
||||
membersFormatted = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
_('Unable to retrieve member list.'))
|
||||
return membersFormatted
|
||||
|
||||
|
||||
class MonitorsTab(tabs.TableTab):
|
||||
table_classes = (MonitorsTable,)
|
||||
name = _("Monitors")
|
||||
slug = "monitors"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_monitorstable_data(self):
|
||||
try:
|
||||
monitors = api.lbaas.pool_health_monitors_get(
|
||||
self.tab_group.request)
|
||||
except:
|
||||
monitors = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
_('Unable to retrieve monitor list.'))
|
||||
return monitors
|
||||
|
||||
|
||||
class LoadBalancerTabs(tabs.TabGroup):
|
||||
slug = "lbtabs"
|
||||
tabs = (PoolsTab, MembersTab, MonitorsTab)
|
||||
sticky = True
|
||||
|
||||
|
||||
class PoolDetailsTab(tabs.Tab):
|
||||
name = _("Pool Details")
|
||||
slug = "pooldetails"
|
||||
template_name = "project/loadbalancers/_pool_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
pid = self.tab_group.kwargs['pool_id']
|
||||
try:
|
||||
pool = api.lbaas.pool_get(request, pid)
|
||||
except:
|
||||
pool = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve pool details.'))
|
||||
return {'pool': pool}
|
||||
|
||||
|
||||
class VipDetailsTab(tabs.Tab):
|
||||
name = _("Vip Details")
|
||||
slug = "vipdetails"
|
||||
template_name = "project/loadbalancers/_vip_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
vid = self.tab_group.kwargs['vip_id']
|
||||
try:
|
||||
vip = api.lbaas.vip_get(request, vid)
|
||||
except:
|
||||
vip = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
_('Unable to retrieve vip details.'))
|
||||
return {'vip': vip}
|
||||
|
||||
|
||||
class MemberDetailsTab(tabs.Tab):
|
||||
name = _("Member Details")
|
||||
slug = "memberdetails"
|
||||
template_name = "project/loadbalancers/_member_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
mid = self.tab_group.kwargs['member_id']
|
||||
try:
|
||||
member = api.lbaas.member_get(request, mid)
|
||||
except:
|
||||
member = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
_('Unable to retrieve member details.'))
|
||||
return {'member': member}
|
||||
|
||||
|
||||
class MonitorDetailsTab(tabs.Tab):
|
||||
name = _("Monitor Details")
|
||||
slug = "monitordetails"
|
||||
template_name = "project/loadbalancers/_monitor_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
mid = self.tab_group.kwargs['monitor_id']
|
||||
try:
|
||||
monitor = api.lbaas.pool_health_monitor_get(request, mid)
|
||||
except:
|
||||
monitor = []
|
||||
exceptions.handle(self.tab_group.request,
|
||||
_('Unable to retrieve monitor details.'))
|
||||
return {'monitor': monitor}
|
||||
|
||||
|
||||
class PoolDetailsTabs(tabs.TabGroup):
|
||||
slug = "pooltabs"
|
||||
tabs = (PoolDetailsTab,)
|
||||
|
||||
|
||||
class VipDetailsTabs(tabs.TabGroup):
|
||||
slug = "viptabs"
|
||||
tabs = (VipDetailsTab,)
|
||||
|
||||
|
||||
class MemberDetailsTabs(tabs.TabGroup):
|
||||
slug = "membertabs"
|
||||
tabs = (MemberDetailsTab,)
|
||||
|
||||
|
||||
class MonitorDetailsTabs(tabs.TabGroup):
|
||||
slug = "monitortabs"
|
||||
tabs = (MonitorDetailsTab,)
|
@ -0,0 +1,30 @@
|
||||
{% load i18n sizeformat parse_date %}
|
||||
|
||||
<div class="info row-fluid detail">
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dt>{% trans "ID: " %}</dt>
|
||||
<dd>{{ member.id }}</dd>
|
||||
|
||||
<dt>{% trans "Tenant ID: " %}</dt>
|
||||
<dd>{{ member.tenant_id }}</dd>
|
||||
|
||||
<dt>{% trans "Pool ID: " %}</dt>
|
||||
<dd>{{ member.pool_id }}</dd>
|
||||
|
||||
<dt>{% trans "Address: " %}</dt>
|
||||
<dd>{{ member.address }}</dd>
|
||||
|
||||
<dt>{% trans "Protocol Port: " %}</dt>
|
||||
<dd>{{ member.protocol_port }}</dd>
|
||||
|
||||
<dt>{% trans "Weight: " %}</dt>
|
||||
<dd>{{ member.weight }}</dd>
|
||||
|
||||
<dt>{% trans "Admin State Up: " %}</dt>
|
||||
<dd>{{ member.admin_state_up }}</dd>
|
||||
|
||||
<dt>{% trans "Status: " %}</dt>
|
||||
<dd>{{ member.status }}</dd>
|
||||
</dl>
|
||||
</div>
|
@ -0,0 +1,5 @@
|
||||
{% block main %}
|
||||
|
||||
{{ table.render }}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,39 @@
|
||||
{% load i18n sizeformat parse_date %}
|
||||
|
||||
<div class="info row-fluid detail">
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dt>{% trans "ID: " %}</dt>
|
||||
<dd>{{ monitor.id }}</dd>
|
||||
|
||||
<dt>{% trans "Tenant ID: " %}</dt>
|
||||
<dd>{{ monitor.tenant_id }}</dd>
|
||||
|
||||
<dt>{% trans "Type: " %}</dt>
|
||||
<dd>{{ monitor.type }}</dd>
|
||||
|
||||
<dt>{% trans "Delay: " %}</dt>
|
||||
<dd>{{ monitor.delay }}</dd>
|
||||
|
||||
<dt>{% trans "Timeout: " %}</dt>
|
||||
<dd>{{ monitor.timeout }}</dd>
|
||||
|
||||
<dt>{% trans "Max Retries: " %}</dt>
|
||||
<dd>{{ monitor.max_retries }}</dd>
|
||||
|
||||
<dt>{% trans "HTTP Method: " %}</dt>
|
||||
<dd>{{ monitor.http_method }}</dd>
|
||||
|
||||
<dt>{% trans "URL Path: " %}</dt>
|
||||
<dd>{{ monitor.url_path }}</dd>
|
||||
|
||||
<dt>{% trans "Expected Codes: " %}</dt>
|
||||
<dd>{{ monitor.expected_codes }}</dd>
|
||||
|
||||
<dt>{% trans "Admin State Up: " %}</dt>
|
||||
<dd>{{ monitor.admin_state_up }}</dd>
|
||||
|
||||
<dt>{% trans "Status: " %}</dt>
|
||||
<dd>{{ monitor.status }}</dd>
|
||||
</dl>
|
||||
</div>
|
@ -0,0 +1,5 @@
|
||||
{% block main %}
|
||||
|
||||
{{ table.render }}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,42 @@
|
||||
{% load i18n sizeformat parse_date %}
|
||||
|
||||
<div class="info row-fluid detail">
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dt>{% trans "ID: " %}</dt>
|
||||
<dd>{{ pool.id }}</dd>
|
||||
|
||||
<dt>{% trans "Tenant ID: " %}</dt>
|
||||
<dd>{{ pool.tenant_id }}</dd>
|
||||
|
||||
<dt>{% trans "VIP ID: " %}</dt>
|
||||
<dd>{{ pool.vip_id }}</dd>
|
||||
|
||||
<dt>{% trans "Name: " %}</dt>
|
||||
<dd>{{ pool.name }}</dd>
|
||||
|
||||
<dt>{% trans "Description: " %}</dt>
|
||||
<dd>{{ pool.description }}</dd>
|
||||
|
||||
<dt>{% trans "Subnet ID: " %}</dt>
|
||||
<dd>{{ pool.subnet_id }}</dd>
|
||||
|
||||
<dt>{% trans "Protocol: " %}</dt>
|
||||
<dd>{{ pool.protocol }}</dd>
|
||||
|
||||
<dt>{% trans "Load Balancing Method: " %}</dt>
|
||||
<dd>{{ pool.lb_method }}</dd>
|
||||
|
||||
<dt>{% trans "Members: " %}</dt>
|
||||
<dd>{{ pool.members }}</dd>
|
||||
|
||||
<dt>{% trans "Health Monitors: " %}</dt>
|
||||
<dd>{{ pool.health_monitors }}</dd>
|
||||
|
||||
<dt>{% trans "Admin State Up: " %}</dt>
|
||||
<dd>{{ pool.admin_state_up }}</dd>
|
||||
|
||||
<dt>{% trans "Status: " %}</dt>
|
||||
<dd>{{ pool.status }}</dd>
|
||||
</dl>
|
||||
</div>
|
@ -0,0 +1,5 @@
|
||||
{% block main %}
|
||||
|
||||
{{ poolstable.render }}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,48 @@
|
||||
{% load i18n sizeformat parse_date %}
|
||||
|
||||
<div class="info row-fluid detail">
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dt>{% trans "ID: " %}</dt>
|
||||
<dd>{{ vip.id }}</dd>
|
||||
|
||||
<dt>{% trans "Tenant ID: " %}</dt>
|
||||
<dd>{{ vip.tenant_id }}</dd>
|
||||
|
||||
<dt>{% trans "Name: " %}</dt>
|
||||
<dd>{{ vip.name }}</dd>
|
||||
|
||||
<dt>{% trans "Description: " %}</dt>
|
||||
<dd>{{ vip.description }}</dd>
|
||||
|
||||
<dt>{% trans "Subnet ID: " %}</dt>
|
||||
<dd>{{ vip.subnet_id }}</dd>
|
||||
|
||||
<dt>{% trans "Address: " %}</dt>
|
||||
<dd>{{ vip.address }}</dd>
|
||||
|
||||
<dt>{% trans "Protocol Port: " %}</dt>
|
||||
<dd>{{ vip.protocol_port }}</dd>
|
||||
|
||||
<dt>{% trans "Protocol: " %}</dt>
|
||||
<dd>{{ vip.protocol }}</dd>
|
||||
|
||||
<dt>{% trans "Pool ID: " %}</dt>
|
||||
<dd>{{ vip.pool_id }}</dd>
|
||||
|
||||
<dt>{% trans "Session Persistence: " %}</dt>
|
||||
<dd>{% trans "Type: " %}{{ vip.session_persistence.type }}</dd>
|
||||
{% if vip.session_persistence.cookie_name %}
|
||||
<dd>{% trans "Cookie Name: " %}{{ vip.session_persistence.cookie_name }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>{% trans "Connection Limit: " %}</dt>
|
||||
<dd>{{ vip.connection_limit }}</dd>
|
||||
|
||||
<dt>{% trans "Admin State Up: " %}</dt>
|
||||
<dd>{{ vip.admin_state_up }}</dd>
|
||||
|
||||
<dt>{% trans "Status: " %}</dt>
|
||||
<dd>{{ vip.status }}</dd>
|
||||
</dl>
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add New Member" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Add New Member") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'horizon/common/_workflow.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add New Monitor" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Add New Monitor") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'horizon/common/_workflow.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add New Pool" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Add New Pool") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'horizon/common/_workflow.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Specify Vip" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Specify Vip") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'horizon/common/_workflow.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,15 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Load Balancer" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Load Balancer") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
299
openstack_dashboard/dashboards/project/loadbalancers/tests.py
Normal file
299
openstack_dashboard/dashboards/project/loadbalancers/tests.py
Normal file
@ -0,0 +1,299 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
import json
|
||||
|
||||
from mox import IsA
|
||||
from django import http
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.api.lbaas import Pool, Vip, Member, PoolMonitor
|
||||
|
||||
from .tabs import LoadBalancerTabs, MembersTab, PoolsTab, MonitorsTab
|
||||
from .workflows import AddPool, AddMember, AddMonitor, AddVip
|
||||
|
||||
|
||||
class LoadBalancerTests(test.TestCase):
|
||||
class AttributeDict(dict):
|
||||
def __getattr__(self, attr):
|
||||
return self[attr]
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
self[attr] = value
|
||||
|
||||
DASHBOARD = 'project'
|
||||
INDEX_URL = reverse('horizon:%s:loadbalancers:index' % DASHBOARD)
|
||||
|
||||
ADDPOOL_PATH = 'horizon:%s:loadbalancers:addpool' % DASHBOARD
|
||||
ADDVIP_PATH = 'horizon:%s:loadbalancers:addvip' % DASHBOARD
|
||||
ADDMEMBER_PATH = 'horizon:%s:loadbalancers:addmember' % DASHBOARD
|
||||
ADDMONITOR_PATH = 'horizon:%s:loadbalancers:addmonitor' % DASHBOARD
|
||||
|
||||
POOL_DETAIL_PATH = 'horizon:%s:loadbalancers:pooldetails' % DASHBOARD
|
||||
VIP_DETAIL_PATH = 'horizon:%s:loadbalancers:vipdetails' % DASHBOARD
|
||||
MEMBER_DETAIL_PATH = 'horizon:%s:loadbalancers:memberdetails' % DASHBOARD
|
||||
MONITOR_DETAIL_PATH = 'horizon:%s:loadbalancers:monitordetails' % DASHBOARD
|
||||
|
||||
def set_up_expect(self):
|
||||
# retrieve pools
|
||||
subnet = self.subnets.first()
|
||||
|
||||
vip1 = self.vips.first()
|
||||
|
||||
vip2 = self.vips.list()[1]
|
||||
|
||||
api.lbaas.pools_get(
|
||||
IsA(http.HttpRequest)).AndReturn(self.pools.list())
|
||||
|
||||
api.lbaas.vip_get(IsA(http.HttpRequest), vip1.id).AndReturn(vip1)
|
||||
api.lbaas.vip_get(IsA(http.HttpRequest), vip2.id).AndReturn(vip2)
|
||||
|
||||
# retrieves members
|
||||
api.lbaas.members_get(
|
||||
IsA(http.HttpRequest)).AndReturn(self.members.list())
|
||||
|
||||
pool1 = self.pools.first()
|
||||
pool2 = self.pools.list()[1]
|
||||
|
||||
api.lbaas.pool_get(IsA(http.HttpRequest),
|
||||
self.members.list()[0].pool_id).AndReturn(pool1)
|
||||
api.lbaas.pool_get(IsA(http.HttpRequest),
|
||||
self.members.list()[1].pool_id).AndReturn(pool2)
|
||||
|
||||
# retrieves monitors
|
||||
api.lbaas.pool_health_monitors_get(
|
||||
IsA(http.HttpRequest)).AndReturn(self.monitors.list())
|
||||
|
||||
def set_up_expect_with_exception(self):
|
||||
api.lbaas.pools_get(
|
||||
IsA(http.HttpRequest)).AndRaise(self.exceptions.quantum)
|
||||
api.lbaas.members_get(
|
||||
IsA(http.HttpRequest)).AndRaise(self.exceptions.quantum)
|
||||
api.lbaas.pool_health_monitors_get(
|
||||
IsA(http.HttpRequest)).AndRaise(self.exceptions.quantum)
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'vip_get',
|
||||
'members_get', 'pool_get',
|
||||
'pool_health_monitors_get'),
|
||||
api.quantum: ('subnet_get',)})
|
||||
def test_index_pools(self):
|
||||
self.set_up_expect()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, '%s/loadbalancers/details_tabs.html'
|
||||
% self.DASHBOARD)
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail_table.html')
|
||||
self.assertEqual(len(res.context['table'].data),
|
||||
len(self.pools.list()))
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'vip_get',
|
||||
'members_get', 'pool_get',
|
||||
'pool_health_monitors_get'),
|
||||
api.quantum: ('subnet_get',)})
|
||||
def test_index_members(self):
|
||||
self.set_up_expect()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL + '?tab=lbtabs__members')
|
||||
|
||||
self.assertTemplateUsed(res, '%s/loadbalancers/details_tabs.html'
|
||||
% self.DASHBOARD)
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail_table.html')
|
||||
self.assertEqual(len(res.context['memberstable_table'].data),
|
||||
len(self.members.list()))
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'vip_get',
|
||||
'pool_health_monitors_get',
|
||||
'members_get', 'pool_get'),
|
||||
api.quantum: ('subnet_get',)})
|
||||
def test_index_monitors(self):
|
||||
self.set_up_expect()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL + '?tab=lbtabs__monitors')
|
||||
|
||||
self.assertTemplateUsed(res, '%s/loadbalancers/details_tabs.html'
|
||||
% self.DASHBOARD)
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail_table.html')
|
||||
self.assertEqual(len(res.context['monitorstable_table'].data),
|
||||
len(self.monitors.list()))
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'members_get',
|
||||
'pool_health_monitors_get')})
|
||||
def test_index_exception_pools(self):
|
||||
self.set_up_expect_with_exception()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res,
|
||||
'%s/loadbalancers/details_tabs.html'
|
||||
% self.DASHBOARD)
|
||||
self.assertTemplateUsed(res,
|
||||
'horizon/common/_detail_table.html')
|
||||
self.assertEqual(len(res.context['table'].data), 0)
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'members_get',
|
||||
'pool_health_monitors_get')})
|
||||
def test_index_exception_members(self):
|
||||
self.set_up_expect_with_exception()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL + '?tab=lbtabs__members')
|
||||
|
||||
self.assertTemplateUsed(res,
|
||||
'%s/loadbalancers/details_tabs.html'
|
||||
% self.DASHBOARD)
|
||||
self.assertTemplateUsed(res,
|
||||
'horizon/common/_detail_table.html')
|
||||
self.assertEqual(len(res.context['memberstable_table'].data), 0)
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'members_get',
|
||||
'pool_health_monitors_get')})
|
||||
def test_index_exception_monitors(self):
|
||||
self.set_up_expect_with_exception()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL + '?tab=lbtabs__monitors')
|
||||
|
||||
self.assertTemplateUsed(res,
|
||||
'%s/loadbalancers/details_tabs.html'
|
||||
% self.DASHBOARD)
|
||||
self.assertTemplateUsed(res,
|
||||
'horizon/common/_detail_table.html')
|
||||
self.assertEqual(len(res.context['monitorstable_table'].data), 0)
|
||||
|
||||
@test.create_stubs({api.quantum: ('network_list_for_tenant',),
|
||||
api.lbaas: ('pool_create', )})
|
||||
def test_add_pool_post(self):
|
||||
pool = self.pools.first()
|
||||
|
||||
subnet = self.subnets.first()
|
||||
networks = [{'subnets': [subnet, ]}, ]
|
||||
|
||||
api.quantum.network_list_for_tenant(
|
||||
IsA(http.HttpRequest), subnet.tenant_id).AndReturn(networks)
|
||||
|
||||
api.lbaas.pool_create(
|
||||
IsA(http.HttpRequest),
|
||||
name=pool.name,
|
||||
description=pool.description,
|
||||
subnet_id=pool.subnet_id,
|
||||
protocol=pool.protocol,
|
||||
lb_method=pool.lb_method,
|
||||
admin_state_up=pool.admin_state_up).AndReturn(Pool(pool))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'name': pool.name,
|
||||
'description': pool.description,
|
||||
'subnet_id': pool.subnet_id,
|
||||
'protocol': pool.protocol,
|
||||
'lb_method': pool.lb_method,
|
||||
'admin_state_up': pool.admin_state_up}
|
||||
|
||||
res = self.client.post(reverse(self.ADDPOOL_PATH), form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.quantum: ('network_list_for_tenant',)})
|
||||
def test_add_pool_get(self):
|
||||
subnet = self.subnets.first()
|
||||
|
||||
networks = [{'subnets': [subnet, ]}, ]
|
||||
|
||||
api.quantum.network_list_for_tenant(
|
||||
IsA(http.HttpRequest), subnet.tenant_id).AndReturn(networks)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse(self.ADDPOOL_PATH))
|
||||
|
||||
workflow = res.context['workflow']
|
||||
self.assertTemplateUsed(res, 'project/loadbalancers/addpool.html')
|
||||
self.assertEqual(workflow.name, AddPool.name)
|
||||
|
||||
expected_objs = ['<AddPoolStep: addpoolaction>', ]
|
||||
self.assertQuerysetEqual(workflow.steps, expected_objs)
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get', 'member_create'),
|
||||
api.quantum: ('port_list',),
|
||||
api.nova: ('server_list',)})
|
||||
def test_add_member_post(self):
|
||||
member = self.members.first()
|
||||
|
||||
server1 = self.AttributeDict({'id':
|
||||
'12381d38-c3eb-4fee-9763-12de3338042e',
|
||||
'name': 'vm1'})
|
||||
server2 = self.AttributeDict({'id':
|
||||
'12381d38-c3eb-4fee-9763-12de3338043e',
|
||||
'name': 'vm2'})
|
||||
|
||||
port1 = self.AttributeDict(
|
||||
{'fixed_ips': [{'ip_address': member.address}]})
|
||||
|
||||
api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list())
|
||||
|
||||
api.nova.server_list(IsA(http.HttpRequest)).AndReturn([server1,
|
||||
server2])
|
||||
|
||||
api.quantum.port_list(IsA(http.HttpRequest),
|
||||
device_id=server1.id).AndReturn([port1, ])
|
||||
|
||||
api.lbaas.member_create(
|
||||
IsA(http.HttpRequest),
|
||||
pool_id=member.pool_id,
|
||||
address=member.address,
|
||||
protocol_port=member.protocol_port,
|
||||
weight=member.weight,
|
||||
members=[server1.id],
|
||||
admin_state_up=member.admin_state_up).AndReturn(Member(member))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'pool_id': member.pool_id,
|
||||
'address': member.address,
|
||||
'protocol_port': member.protocol_port,
|
||||
'weight': member.weight,
|
||||
'members': [server1.id],
|
||||
'admin_state_up': member.admin_state_up}
|
||||
|
||||
res = self.client.post(reverse(self.ADDMEMBER_PATH), form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.lbaas: ('pools_get',),
|
||||
api.nova: ('server_list',)})
|
||||
def test_add_member_get(self):
|
||||
server1 = self.AttributeDict({'id':
|
||||
'12381d38-c3eb-4fee-9763-12de3338042e',
|
||||
'name': 'vm1'})
|
||||
server2 = self.AttributeDict({'id':
|
||||
'12381d38-c3eb-4fee-9763-12de3338043e',
|
||||
'name': 'vm2'})
|
||||
|
||||
api.lbaas.pools_get(IsA(http.HttpRequest)).AndReturn(self.pools.list())
|
||||
api.nova.server_list(
|
||||
IsA(http.HttpRequest)).AndReturn([server1, server2])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse(self.ADDMEMBER_PATH))
|
||||
|
||||
workflow = res.context['workflow']
|
||||
self.assertTemplateUsed(res, 'project/loadbalancers/addmember.html')
|
||||
self.assertEqual(workflow.name, AddMember.name)
|
||||
|
||||
expected_objs = ['<AddMemberStep: addmemberaction>', ]
|
||||
self.assertQuerysetEqual(workflow.steps, expected_objs)
|
38
openstack_dashboard/dashboards/project/loadbalancers/urls.py
Normal file
38
openstack_dashboard/dashboards/project/loadbalancers/urls.py
Normal file
@ -0,0 +1,38 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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.conf.urls.defaults import url, patterns
|
||||
|
||||
from .views import IndexView
|
||||
from .views import AddPoolView, AddMemberView, AddMonitorView, AddVipView
|
||||
from .views import PoolDetailsView, VipDetailsView
|
||||
from .views import MemberDetailsView, MonitorDetailsView
|
||||
|
||||
urlpatterns = patterns(
|
||||
'openstack_dashboard.dashboards.project.loadbalancers.views',
|
||||
url(r'^$', IndexView.as_view(), name='index'),
|
||||
url(r'^addpool$', AddPoolView.as_view(), name='addpool'),
|
||||
url(r'^addvip/(?P<pool_id>[^/]+)/$', AddVipView.as_view(), name='addvip'),
|
||||
url(r'^addmember$', AddMemberView.as_view(), name='addmember'),
|
||||
url(r'^addmonitor$', AddMonitorView.as_view(), name='addmonitor'),
|
||||
url(r'^pool/(?P<pool_id>[^/]+)/$',
|
||||
PoolDetailsView.as_view(), name='pooldetails'),
|
||||
url(r'^vip/(?P<vip_id>[^/]+)/$',
|
||||
VipDetailsView.as_view(), name='vipdetails'),
|
||||
url(r'^member/(?P<member_id>[^/]+)/$',
|
||||
MemberDetailsView.as_view(), name='memberdetails'),
|
||||
url(r'^monitor/(?P<monitor_id>[^/]+)/$',
|
||||
MonitorDetailsView.as_view(), name='monitordetails'))
|
152
openstack_dashboard/dashboards/project/loadbalancers/views.py
Normal file
152
openstack_dashboard/dashboards/project/loadbalancers/views.py
Normal file
@ -0,0 +1,152 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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.
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from django import http
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from .workflows import AddPool, AddMember, AddMonitor, AddVip
|
||||
from .tabs import LoadBalancerTabs, PoolDetailsTabs, VipDetailsTabs
|
||||
from .tabs import MemberDetailsTabs, MonitorDetailsTabs
|
||||
from .tables import DeleteMonitorLink
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IndexView(tabs.TabView):
|
||||
tab_group_class = (LoadBalancerTabs)
|
||||
template_name = 'project/loadbalancers/details_tabs.html'
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
obj_ids = request.POST.getlist('object_ids')
|
||||
action = request.POST['action']
|
||||
m = re.search('.delete([a-z]+)', action).group(1)
|
||||
if obj_ids == []:
|
||||
obj_ids.append(re.search('([0-9a-z-]+)$', action).group(1))
|
||||
if m == 'monitor':
|
||||
for obj_id in obj_ids:
|
||||
try:
|
||||
api.lbaas.pool_health_monitor_delete(request, obj_id)
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Unable to delete monitor.'))
|
||||
if m == 'pool':
|
||||
for obj_id in obj_ids:
|
||||
try:
|
||||
api.lbaas.pool_delete(request, obj_id)
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Must delete Vip first.'))
|
||||
if m == 'member':
|
||||
for obj_id in obj_ids:
|
||||
try:
|
||||
api.lbaas.member_delete(request, obj_id)
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Unable to delete member.'))
|
||||
if m == 'vip':
|
||||
for obj_id in obj_ids:
|
||||
try:
|
||||
vip_id = api.lbaas.pool_get(request, obj_id).vip_id
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Unable to locate vip to delete.'))
|
||||
if vip_id is not None:
|
||||
try:
|
||||
api.lbaas.vip_delete(request, vip_id)
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Unable to delete vip.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AddPoolView(workflows.WorkflowView):
|
||||
workflow_class = AddPool
|
||||
template_name = "project/loadbalancers/addpool.html"
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(AddPoolView, self).get_initial()
|
||||
return initial
|
||||
|
||||
|
||||
class AddVipView(workflows.WorkflowView):
|
||||
workflow_class = AddVip
|
||||
template_name = "project/loadbalancers/addvip.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AddVipView, self).get_context_data(**kwargs)
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(AddVipView, self).get_initial()
|
||||
initial['pool_id'] = self.kwargs['pool_id']
|
||||
try:
|
||||
pool = api.lbaas.pool_get(self.request, initial['pool_id'])
|
||||
initial['subnet'] = api.quantum.subnet_get(
|
||||
self.request, pool.subnet_id).cidr
|
||||
except:
|
||||
initial['subnet'] = ''
|
||||
msg = _('Unable to retrieve pool subnet.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return initial
|
||||
|
||||
|
||||
class AddMemberView(workflows.WorkflowView):
|
||||
workflow_class = AddMember
|
||||
template_name = "project/loadbalancers/addmember.html"
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(AddMemberView, self).get_initial()
|
||||
return initial
|
||||
|
||||
|
||||
class AddMonitorView(workflows.WorkflowView):
|
||||
workflow_class = AddMonitor
|
||||
template_name = "project/loadbalancers/addmonitor.html"
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(AddMonitorView, self).get_initial()
|
||||
return initial
|
||||
|
||||
|
||||
class PoolDetailsView(tabs.TabView):
|
||||
tab_group_class = (PoolDetailsTabs)
|
||||
template_name = 'project/loadbalancers/details_tabs.html'
|
||||
|
||||
|
||||
class VipDetailsView(tabs.TabView):
|
||||
tab_group_class = (VipDetailsTabs)
|
||||
template_name = 'project/loadbalancers/details_tabs.html'
|
||||
|
||||
|
||||
class MemberDetailsView(tabs.TabView):
|
||||
tab_group_class = (MemberDetailsTabs)
|
||||
template_name = 'project/loadbalancers/details_tabs.html'
|
||||
|
||||
|
||||
class MonitorDetailsView(tabs.TabView):
|
||||
tab_group_class = (MonitorDetailsTabs)
|
||||
template_name = 'project/loadbalancers/details_tabs.html'
|
@ -0,0 +1,448 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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.
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon.utils import fields
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddPoolAction(workflows.Action):
|
||||
name = forms.CharField(max_length=80, label=_("Name"))
|
||||
description = forms.CharField(
|
||||
initial="", required=False,
|
||||
max_length=80, label=_("Description"))
|
||||
subnet_id = forms.ChoiceField(label=_("Subnet"))
|
||||
protocol = forms.ChoiceField(label=_("Protocol"))
|
||||
lb_method = forms.ChoiceField(label=_("Load Balancing Method"))
|
||||
admin_state_up = forms.BooleanField(label=_("Admin State"),
|
||||
initial=True, required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(AddPoolAction, self).__init__(request, *args, **kwargs)
|
||||
|
||||
tenant_id = request.user.tenant_id
|
||||
|
||||
subnet_id_choices = [('', _("Select a Subnet"))]
|
||||
try:
|
||||
networks = api.quantum.network_list_for_tenant(request, tenant_id)
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve networks list.'))
|
||||
for n in networks:
|
||||
for s in n['subnets']:
|
||||
subnet_id_choices.append((s.id, s.cidr))
|
||||
self.fields['subnet_id'].choices = subnet_id_choices
|
||||
|
||||
protocol_choices = [('', _("Select a Protocol"))]
|
||||
protocol_choices.append(('HTTP', 'HTTP'))
|
||||
protocol_choices.append(('HTTPS', 'HTTPS'))
|
||||
self.fields['protocol'].choices = protocol_choices
|
||||
|
||||
lb_method_choices = [('', _("Select a Protocol"))]
|
||||
lb_method_choices.append(('ROUND_ROBIN', 'ROUND_ROBIN'))
|
||||
lb_method_choices.append(('LEAST_CONNECTIONS', 'LEAST_CONNECTIONS'))
|
||||
lb_method_choices.append(('SOURCE_IP', 'SOURCE_IP'))
|
||||
self.fields['lb_method'].choices = lb_method_choices
|
||||
|
||||
class Meta:
|
||||
name = _("PoolDetails")
|
||||
permissions = ('openstack.services.network',)
|
||||
help_text = _("Create Pool for current tenant.\n\n"
|
||||
"Assign a name and description for the pool. "
|
||||
"Choose one subnet where all members of this "
|
||||
"pool must be on. "
|
||||
"Select the protocol and load balancing method "
|
||||
"for this pool. "
|
||||
"Admin State is UP (checked) by defaul.t")
|
||||
|
||||
|
||||
class AddPoolStep(workflows.Step):
|
||||
action_class = AddPoolAction
|
||||
contributes = ("name", "description", "subnet_id",
|
||||
"protocol", "lb_method", "admin_state_up")
|
||||
|
||||
def contribute(self, data, context):
|
||||
context = super(AddPoolStep, self).contribute(data, context)
|
||||
if data:
|
||||
return context
|
||||
|
||||
|
||||
class AddPool(workflows.Workflow):
|
||||
slug = "addpool"
|
||||
name = _("Add Pool")
|
||||
finalize_button_name = _("Add")
|
||||
success_message = _('Added Pool "%s".')
|
||||
failure_message = _('Unable to add Pool "%s".')
|
||||
success_url = "horizon:project:loadbalancers:index"
|
||||
default_steps = (AddPoolStep,)
|
||||
|
||||
def format_status_message(self, message):
|
||||
name = self.context.get('name')
|
||||
return message % name
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
pool = api.lbaas.pool_create(request, **context)
|
||||
context['name']
|
||||
return True
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
self.failure_message)
|
||||
return False
|
||||
|
||||
|
||||
class AddVipAction(workflows.Action):
|
||||
name = forms.CharField(max_length=80, label=_("Name"))
|
||||
description = forms.CharField(
|
||||
initial="", required=False,
|
||||
max_length=80, label=_("Description"))
|
||||
floatip_address = forms.ChoiceField(
|
||||
label=_("Vip Address from Floating IPs"),
|
||||
widget=forms.Select(attrs={'disabled': 'disabled'}),
|
||||
required=False)
|
||||
other_address = fields.IPField(required=False,
|
||||
initial="",
|
||||
version=fields.IPv4,
|
||||
mask=False)
|
||||
protocol_port = forms.CharField(max_length=80, label=_("Protocol Port"))
|
||||
protocol = forms.ChoiceField(label=_("Protocol"))
|
||||
session_persistence = forms.ChoiceField(
|
||||
required=False, initial={}, label=_("Session Persistence"))
|
||||
cookie_name = forms.CharField(
|
||||
initial="", required=False,
|
||||
max_length=80, label=_("Cookie Name"),
|
||||
help_text=_("Required for APP_COOKIE persistence;"
|
||||
" Ignored otherwise."))
|
||||
connection_limit = forms.CharField(
|
||||
max_length=80, label=_("Connection Limit"))
|
||||
admin_state_up = forms.BooleanField(
|
||||
label=_("Admin State"), initial=True, required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(AddVipAction, self).__init__(request, *args, **kwargs)
|
||||
|
||||
self.fields['other_address'].label = _("Specify a free IP address"
|
||||
" from %s" %
|
||||
args[0]['subnet'])
|
||||
|
||||
protocol_choices = [('', _("Select a Protocol"))]
|
||||
protocol_choices.append(('HTTP', 'HTTP'))
|
||||
protocol_choices.append(('HTTPS', 'HTTPS'))
|
||||
self.fields['protocol'].choices = protocol_choices
|
||||
|
||||
session_persistence_choices = [('', _("Set Session Persistence"))]
|
||||
for mode in ('SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE'):
|
||||
session_persistence_choices.append((mode, mode))
|
||||
self.fields[
|
||||
'session_persistence'].choices = session_persistence_choices
|
||||
|
||||
floatip_address_choices = [('', _("Currently Not Supported"))]
|
||||
self.fields['floatip_address'].choices = floatip_address_choices
|
||||
|
||||
class Meta:
|
||||
name = _("AddVip")
|
||||
permissions = ('openstack.services.network',)
|
||||
help_text = _("Create a vip (virtual IP) for this pool. "
|
||||
"Assign a name and description for the vip. "
|
||||
"Specify an IP address and port for the vip. "
|
||||
"Choose the protocol and session persistence "
|
||||
"method for the vip."
|
||||
"Specify the max connections allowed. "
|
||||
"Admin State is UP (checked) by default.")
|
||||
|
||||
|
||||
class AddVipStep(workflows.Step):
|
||||
action_class = AddVipAction
|
||||
depends_on = ("pool_id", "subnet")
|
||||
contributes = ("name", "description", "floatip_address",
|
||||
"other_address", "protocol_port", "protocol",
|
||||
"session_persistence", "cookie_name",
|
||||
"connection_limit", "admin_state_up")
|
||||
|
||||
def contribute(self, data, context):
|
||||
context = super(AddVipStep, self).contribute(data, context)
|
||||
return context
|
||||
|
||||
|
||||
class AddVip(workflows.Workflow):
|
||||
slug = "addvip"
|
||||
name = _("Add Vip")
|
||||
finalize_button_name = _("Add")
|
||||
success_message = _('Added Vip "%s".')
|
||||
failure_message = _('Unable to add Vip "%s".')
|
||||
success_url = "horizon:project:loadbalancers:index"
|
||||
default_steps = (AddVipStep,)
|
||||
|
||||
def format_status_message(self, message):
|
||||
name = self.context.get('name')
|
||||
return message % name
|
||||
|
||||
def handle(self, request, context):
|
||||
if context['other_address'] == '':
|
||||
context['address'] = context['floatip_address']
|
||||
else:
|
||||
if not context['floatip_address'] == '':
|
||||
self.failure_message = _('Only one address can be specified.'
|
||||
'Unable to add Vip %s.')
|
||||
return False
|
||||
else:
|
||||
context['address'] = context['other_address']
|
||||
try:
|
||||
pool = api.lbaas.pool_get(request, context['pool_id'])
|
||||
context['subnet_id'] = pool['subnet_id']
|
||||
except:
|
||||
context['subnet_id'] = None
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve pool.'))
|
||||
return False
|
||||
|
||||
if context['session_persistence']:
|
||||
stype = context['session_persistence']
|
||||
if stype == 'APP_COOKIE':
|
||||
if context['cookie_name'] == "":
|
||||
self.failure_message = _('Cookie name must be specified '
|
||||
'with APP_COOKIE persistence.')
|
||||
return False
|
||||
else:
|
||||
cookie = context['cookie_name']
|
||||
context['session_persistence'] = {'type': stype,
|
||||
'cookie_name': cookie}
|
||||
else:
|
||||
context['session_persistence'] = {'type': stype}
|
||||
else:
|
||||
context['session_persistence'] = {}
|
||||
|
||||
try:
|
||||
api.lbaas.vip_create(request, **context)
|
||||
return True
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
self.failure_message)
|
||||
return False
|
||||
|
||||
|
||||
class AddMemberAction(workflows.Action):
|
||||
pool_id = forms.ChoiceField(label=_("Pool"))
|
||||
members = forms.MultipleChoiceField(
|
||||
label=_("Member(s)"),
|
||||
required=True,
|
||||
initial=["default"],
|
||||
widget=forms.CheckboxSelectMultiple(),
|
||||
help_text=_("Select members for this pool "))
|
||||
weight = forms.CharField(max_length=80, label=_("Weight"))
|
||||
protocol_port = forms.CharField(max_length=80, label=_("Protocol Port"))
|
||||
admin_state_up = forms.BooleanField(label=_("Admin State"),
|
||||
initial=True, required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(AddMemberAction, self).__init__(request, *args, **kwargs)
|
||||
|
||||
pool_id_choices = [('', _("Select a Pool"))]
|
||||
try:
|
||||
pools = api.lbaas.pools_get(request)
|
||||
except:
|
||||
pools = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve pools list.'))
|
||||
pools = sorted(pools,
|
||||
key=lambda pool: pool.name)
|
||||
for p in pools:
|
||||
pool_id_choices.append((p.id, p.name))
|
||||
self.fields['pool_id'].choices = pool_id_choices
|
||||
|
||||
members_choices = []
|
||||
try:
|
||||
servers = api.nova.server_list(request)
|
||||
except:
|
||||
servers = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve instances list.'))
|
||||
|
||||
if len(servers) == 0:
|
||||
self.fields['members'].label = _("No servers available. "
|
||||
"Click Add to cancel.")
|
||||
self.fields['members'].required = False
|
||||
self.fields['members'].help_text = _("Select members "
|
||||
"for this pool ")
|
||||
self.fields['pool_id'].required = False
|
||||
self.fields['weight'].required = False
|
||||
self.fields['protocol_port'].required = False
|
||||
return
|
||||
|
||||
for m in servers:
|
||||
members_choices.append((m.id, m.name))
|
||||
self.fields['members'].choices = sorted(
|
||||
members_choices,
|
||||
key=lambda member: member[1])
|
||||
|
||||
class Meta:
|
||||
name = _("MemberDetails")
|
||||
permissions = ('openstack.services.network',)
|
||||
help_text = _("Add member to selected pool.\n\n"
|
||||
"Choose one or more listed instances to be "
|
||||
"added to the pool as member(s). "
|
||||
"Assign a numeric weight for this member "
|
||||
"Specify the port number the member(s) "
|
||||
"operate on; e.g., 80.")
|
||||
|
||||
|
||||
class AddMemberStep(workflows.Step):
|
||||
action_class = AddMemberAction
|
||||
contributes = ("pool_id", "members", "protocol_port", "weight",
|
||||
"admin_state_up")
|
||||
|
||||
def contribute(self, data, context):
|
||||
context = super(AddMemberStep, self).contribute(data, context)
|
||||
return context
|
||||
|
||||
|
||||
class AddMember(workflows.Workflow):
|
||||
slug = "addmember"
|
||||
name = _("Add Member")
|
||||
finalize_button_name = _("Add")
|
||||
success_message = _('Added Member "%s".')
|
||||
failure_message = _('Unable to add Member %s.')
|
||||
success_url = "horizon:project:loadbalancers:index"
|
||||
default_steps = (AddMemberStep,)
|
||||
|
||||
def format_status_message(self, message):
|
||||
member_id = self.context.get('member_id')
|
||||
return message % member_id
|
||||
|
||||
def handle(self, request, context):
|
||||
if context['members'] == []:
|
||||
self.failure_message = _('No instances available.%s')
|
||||
context['member_id'] = ''
|
||||
return False
|
||||
|
||||
for m in context['members']:
|
||||
params = {'device_id': m}
|
||||
try:
|
||||
plist = api.quantum.port_list(request, **params)
|
||||
except:
|
||||
plist = []
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve ports list.'))
|
||||
return False
|
||||
if plist:
|
||||
context['address'] = plist[0].fixed_ips[0]['ip_address']
|
||||
try:
|
||||
context['member_id'] = api.lbaas.member_create(
|
||||
request, **context).id
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
self.failure_message)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class AddMonitorAction(workflows.Action):
|
||||
pool_id = forms.ChoiceField(label=_("Pool"))
|
||||
type = forms.ChoiceField(label=_("Type"))
|
||||
delay = forms.CharField(max_length=80, label=_("Delay"))
|
||||
timeout = forms.CharField(max_length=80, label=_("Timeout"))
|
||||
max_retries = forms.CharField(max_length=80,
|
||||
label=_("Max Retries (1~10)"))
|
||||
http_method = forms.ChoiceField(
|
||||
initial="GET", required=False, label=_("HTTP Method"))
|
||||
url_path = forms.CharField(
|
||||
initial="/", required=False, max_length=80, label=_("URL"))
|
||||
expected_codes = forms.CharField(
|
||||
initial="200", required=False, max_length=80,
|
||||
label=_("Expected HTTP Status Codes"))
|
||||
admin_state_up = forms.BooleanField(label=_("Admin State"),
|
||||
initial=True, required=False)
|
||||
|
||||
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)
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve pools list.'))
|
||||
for p in pools:
|
||||
pool_id_choices.append((p.id, p.name))
|
||||
self.fields['pool_id'].choices = pool_id_choices
|
||||
|
||||
type_choices = [('', _("Select Type"))]
|
||||
type_choices.append(('PING', 'PING'))
|
||||
type_choices.append(('TCP', 'TCP'))
|
||||
type_choices.append(('HTTP', 'HTTP'))
|
||||
type_choices.append(('HTTPS', 'HTTPS'))
|
||||
self.fields['type'].choices = type_choices
|
||||
|
||||
http_method_choices = [('', _("Select HTTP Method"))]
|
||||
http_method_choices.append(('GET', 'GET'))
|
||||
self.fields['http_method'].choices = http_method_choices
|
||||
|
||||
class Meta:
|
||||
name = _("MonitorDetails")
|
||||
permissions = ('openstack.services.network',)
|
||||
help_text = _("Create a monitor for a pool.\n\n"
|
||||
"Select target pool and type of monitoring. "
|
||||
"Specify delay, timeout, and retry limits "
|
||||
"required by the monitor. "
|
||||
"Specify method, URL path, and expected "
|
||||
"HTTP codes upon success.")
|
||||
|
||||
|
||||
class AddMonitorStep(workflows.Step):
|
||||
action_class = AddMonitorAction
|
||||
contributes = ("pool_id", "type", "delay", "timeout", "max_retries",
|
||||
"http_method", "url_path", "expected_codes",
|
||||
"admin_state_up")
|
||||
|
||||
def contribute(self, data, context):
|
||||
context = super(AddMonitorStep, self).contribute(data, context)
|
||||
if data:
|
||||
return context
|
||||
|
||||
|
||||
class AddMonitor(workflows.Workflow):
|
||||
slug = "addmonitor"
|
||||
name = _("Add Monitor")
|
||||
finalize_button_name = _("Add")
|
||||
success_message = _('Added Monitor "%s".')
|
||||
failure_message = _('Unable to add Monitor "%s".')
|
||||
success_url = "horizon:project:loadbalancers:index"
|
||||
default_steps = (AddMonitorStep,)
|
||||
|
||||
def format_status_message(self, message):
|
||||
monitor_id = self.context.get('monitor_id')
|
||||
return message % monitor_id
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
context['monitor_id'] = api.lbaas.pool_health_monitor_create(
|
||||
request, **context).get('id')
|
||||
return True
|
||||
except:
|
||||
exceptions.handle(request,
|
||||
self.failure_message)
|
||||
return False
|
@ -104,6 +104,13 @@ OPENSTACK_HYPERVISOR_FEATURES = {
|
||||
'can_encrypt_volumes': False
|
||||
}
|
||||
|
||||
# The OPENSTACK_QUANTUM_NETWORK settings can be used to enable optional
|
||||
# services provided by quantum. Currently only the load balancer service
|
||||
# is available.
|
||||
OPENSTACK_QUANTUM_NETWORK = {
|
||||
'enable_lb': True
|
||||
}
|
||||
|
||||
# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints
|
||||
# in the Keystone service catalog. Use this setting when Horizon is running
|
||||
# external to the OpenStack environment. The default is 'internalURL'.
|
||||
|
363
openstack_dashboard/test/api_tests/lbaas_tests.py
Normal file
363
openstack_dashboard/test/api_tests/lbaas_tests.py
Normal file
@ -0,0 +1,363 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013, Big Switch Networks, 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 mox import IsA
|
||||
from django import http
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.api.lbaas import Vip, Pool, Member, PoolMonitor
|
||||
|
||||
from quantumclient.v2_0.client import Client as quantumclient
|
||||
|
||||
|
||||
class LbaasApiTests(test.APITestCase):
|
||||
@test.create_stubs({quantumclient: ('create_vip',)})
|
||||
def test_vip_create(self):
|
||||
vip1 = self.api_vips.first()
|
||||
form_data = {'address': vip1['address'],
|
||||
'name': vip1['name'],
|
||||
'description': vip1['description'],
|
||||
'subnet_id': vip1['subnet_id'],
|
||||
'protocol_port': vip1['protocol_port'],
|
||||
'protocol': vip1['protocol'],
|
||||
'pool_id': vip1['pool_id'],
|
||||
'session_persistence': vip1['session_persistence'],
|
||||
'connection_limit': vip1['connection_limit'],
|
||||
'admin_state_up': vip1['admin_state_up']
|
||||
}
|
||||
vip = {'vip': self.api_vips.first()}
|
||||
quantumclient.create_vip({'vip': form_data}).AndReturn(vip)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.vip_create(self.request, **form_data)
|
||||
self.assertIsInstance(ret_val, api.lbaas.Vip)
|
||||
|
||||
@test.create_stubs({quantumclient: ('list_vips',)})
|
||||
def test_vips_get(self):
|
||||
vips = {'vips': [{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.0.100',
|
||||
'name': 'vip1name',
|
||||
'description': 'vip1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol_port': '80',
|
||||
'protocol': 'HTTP',
|
||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
||||
'connection_limit': '10',
|
||||
'admin_state_up': True
|
||||
}, ]}
|
||||
quantumclient.list_vips().AndReturn(vips)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.vips_get(self.request)
|
||||
for v in ret_val:
|
||||
self.assertIsInstance(v, api.lbaas.Vip)
|
||||
|
||||
@test.create_stubs({quantumclient: ('show_vip',)})
|
||||
def test_vip_get(self):
|
||||
vip = {'vip': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.0.100',
|
||||
'name': 'vip1name',
|
||||
'description': 'vip1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol_port': '80',
|
||||
'protocol': 'HTTP',
|
||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
||||
'connection_limit': '10',
|
||||
'admin_state_up': True
|
||||
}}
|
||||
quantumclient.show_vip(vip['vip']['id']).AndReturn(vip)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.vip_get(self.request, vip['vip']['id'])
|
||||
self.assertIsInstance(ret_val, api.lbaas.Vip)
|
||||
|
||||
@test.create_stubs({quantumclient: ('update_vip',)})
|
||||
def test_vip_update(self):
|
||||
form_data = {'address': '10.0.0.100',
|
||||
'name': 'vip1name',
|
||||
'description': 'vip1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol_port': '80',
|
||||
'protocol': 'HTTP',
|
||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
||||
'connection_limit': '10',
|
||||
'admin_state_up': True
|
||||
}
|
||||
|
||||
vip = {'vip': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.0.100',
|
||||
'name': 'vip1name',
|
||||
'description': 'vip1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol_port': '80',
|
||||
'protocol': 'HTTP',
|
||||
'pool_id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
||||
'connection_limit': '10',
|
||||
'admin_state_up': True
|
||||
}}
|
||||
quantumclient.update_vip(vip['vip']['id'], form_data).AndReturn(vip)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.vip_update(self.request,
|
||||
vip['vip']['id'], **form_data)
|
||||
self.assertIsInstance(ret_val, api.lbaas.Vip)
|
||||
|
||||
@test.create_stubs({quantumclient: ('create_pool',)})
|
||||
def test_pool_create(self):
|
||||
form_data = {'name': 'pool1name',
|
||||
'description': 'pool1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol': 'HTTP',
|
||||
'lb_method': 'ROUND_ROBIN',
|
||||
'admin_state_up': True
|
||||
}
|
||||
|
||||
pool = {'pool': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'pool1name',
|
||||
'description': 'pool1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol': 'HTTP',
|
||||
'lb_method': 'ROUND_ROBIN',
|
||||
'admin_state_up': True
|
||||
}}
|
||||
quantumclient.create_pool({'pool': form_data}).AndReturn(pool)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.pool_create(self.request, **form_data)
|
||||
self.assertIsInstance(ret_val, api.lbaas.Pool)
|
||||
|
||||
@test.create_stubs({quantumclient: ('list_pools',)})
|
||||
def test_pools_get(self):
|
||||
pools = {'pools': [{
|
||||
'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'pool1name',
|
||||
'description': 'pool1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol': 'HTTP',
|
||||
'lb_method': 'ROUND_ROBIN',
|
||||
'admin_state_up': True}, ]}
|
||||
quantumclient.list_pools().AndReturn(pools)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.pools_get(self.request)
|
||||
for v in ret_val:
|
||||
self.assertIsInstance(v, api.lbaas.Pool)
|
||||
|
||||
@test.create_stubs({quantumclient: ('show_pool',)})
|
||||
def test_pool_get(self):
|
||||
pool = {'pool': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'pool1name',
|
||||
'description': 'pool1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol': 'HTTP',
|
||||
'lb_method': 'ROUND_ROBIN',
|
||||
'admin_state_up': True
|
||||
}}
|
||||
quantumclient.show_pool(pool['pool']['id']).AndReturn(pool)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.pool_get(self.request, pool['pool']['id'])
|
||||
self.assertIsInstance(ret_val, api.lbaas.Pool)
|
||||
|
||||
@test.create_stubs({quantumclient: ('update_pool',)})
|
||||
def test_pool_update(self):
|
||||
form_data = {'name': 'pool1name',
|
||||
'description': 'pool1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol': 'HTTPS',
|
||||
'lb_method': 'LEAST_CONNECTION',
|
||||
'admin_state_up': True
|
||||
}
|
||||
|
||||
pool = {'pool': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'pool1name',
|
||||
'description': 'pool1description',
|
||||
'subnet_id': '12381d38-c3eb-4fee-9763-12de3338041e',
|
||||
'protocol': 'HTTPS',
|
||||
'lb_method': 'LEAST_CONNECTION',
|
||||
'admin_state_up': True
|
||||
}}
|
||||
quantumclient.update_pool(pool['pool']['id'],
|
||||
form_data).AndReturn(pool)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.pool_update(self.request,
|
||||
pool['pool']['id'], **form_data)
|
||||
self.assertIsInstance(ret_val, api.lbaas.Pool)
|
||||
|
||||
@test.create_stubs({quantumclient: ('create_health_monitor',
|
||||
'associate_health_monitor')})
|
||||
def test_pool_health_monitor_create(self):
|
||||
form_data = {'type': 'PING',
|
||||
'delay': '10',
|
||||
'timeout': '10',
|
||||
'max_retries': '10',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/monitor',
|
||||
'expected_codes': '200',
|
||||
'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',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/monitor',
|
||||
'expected_codes': '200',
|
||||
'admin_state_up': True}
|
||||
monitor = {'health_monitor': {
|
||||
'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'type': 'PING',
|
||||
'delay': '10',
|
||||
'timeout': '10',
|
||||
'max_retries': '10',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/monitor',
|
||||
'expected_codes': '200',
|
||||
'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.assertIsInstance(ret_val, api.lbaas.PoolMonitor)
|
||||
|
||||
@test.create_stubs({quantumclient: ('list_health_monitors',)})
|
||||
def test_pool_health_monitors_get(self):
|
||||
monitors = {'health_monitors': [
|
||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'type': 'PING',
|
||||
'delay': '10',
|
||||
'timeout': '10',
|
||||
'max_retries': '10',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/monitor',
|
||||
'expected_codes': '200',
|
||||
'admin_state_up': True}, ]}
|
||||
|
||||
quantumclient.list_health_monitors().AndReturn(monitors)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.pool_health_monitors_get(self.request)
|
||||
for v in ret_val:
|
||||
self.assertIsInstance(v, api.lbaas.PoolMonitor)
|
||||
|
||||
@test.create_stubs({quantumclient: ('show_health_monitor',)})
|
||||
def test_pool_health_monitor_get(self):
|
||||
monitor = {'health_monitor':
|
||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'type': 'PING',
|
||||
'delay': '10',
|
||||
'timeout': '10',
|
||||
'max_retries': '10',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/monitor',
|
||||
'expected_codes': '200',
|
||||
'admin_state_up': True}}
|
||||
quantumclient.show_health_monitor(
|
||||
monitor['health_monitor']['id']).AndReturn(monitor)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.pool_health_monitor_get(
|
||||
self.request, monitor['health_monitor']['id'])
|
||||
self.assertIsInstance(ret_val, api.lbaas.PoolMonitor)
|
||||
|
||||
@test.create_stubs({quantumclient: ('create_member', )})
|
||||
def test_member_create(self):
|
||||
form_data = {'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.1.2',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True
|
||||
}
|
||||
|
||||
member = {'member':
|
||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.1.2',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True}}
|
||||
|
||||
quantumclient.create_member({'member': form_data}).AndReturn(member)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.member_create(self.request, **form_data)
|
||||
self.assertIsInstance(ret_val, api.lbaas.Member)
|
||||
|
||||
@test.create_stubs({quantumclient: ('list_members',)})
|
||||
def test_members_get(self):
|
||||
members = {'members': [
|
||||
{'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.1.2',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True
|
||||
}, ]}
|
||||
quantumclient.list_members().AndReturn(members)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.members_get(self.request)
|
||||
for v in ret_val:
|
||||
self.assertIsInstance(v, api.lbaas.Member)
|
||||
|
||||
@test.create_stubs({quantumclient: ('show_member',)})
|
||||
def test_member_get(self):
|
||||
member = {'member': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.1.2',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True}}
|
||||
quantumclient.show_member(member['member']['id']).AndReturn(member)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.member_get(self.request, member['member']['id'])
|
||||
self.assertIsInstance(ret_val, api.lbaas.Member)
|
||||
|
||||
@test.create_stubs({quantumclient: ('update_member',)})
|
||||
def test_member_update(self):
|
||||
form_data = {'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.1.4',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True
|
||||
}
|
||||
|
||||
member = {'member': {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'pool_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'address': '10.0.1.2',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True
|
||||
}}
|
||||
|
||||
quantumclient.update_member(member['member']['id'],
|
||||
form_data).AndReturn(member)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.lbaas.member_update(self.request,
|
||||
member['member']['id'], **form_data)
|
||||
self.assertIsInstance(ret_val, api.lbaas.Member)
|
@ -74,6 +74,10 @@ OPENSTACK_KEYSTONE_BACKEND = {
|
||||
'can_edit_project': True
|
||||
}
|
||||
|
||||
OPENSTACK_QUANTUM_NETWORK = {
|
||||
'enable_lb': True
|
||||
}
|
||||
|
||||
OPENSTACK_HYPERVISOR_FEATURES = {
|
||||
'can_set_mount_point': True,
|
||||
|
||||
|
@ -16,7 +16,8 @@ import copy
|
||||
|
||||
from openstack_dashboard.api.quantum import (Network, Subnet, Port,
|
||||
Router, FloatingIp)
|
||||
|
||||
from openstack_dashboard.api.lbaas import (Pool, Vip, Member,
|
||||
PoolMonitor)
|
||||
from .utils import TestDataContainer
|
||||
|
||||
|
||||
@ -27,6 +28,10 @@ def data(TEST):
|
||||
TEST.ports = TestDataContainer()
|
||||
TEST.routers = TestDataContainer()
|
||||
TEST.q_floating_ips = TestDataContainer()
|
||||
TEST.pools = TestDataContainer()
|
||||
TEST.vips = TestDataContainer()
|
||||
TEST.members = TestDataContainer()
|
||||
TEST.monitors = TestDataContainer()
|
||||
|
||||
# data return by quantumclient
|
||||
TEST.api_networks = TestDataContainer()
|
||||
@ -34,6 +39,10 @@ def data(TEST):
|
||||
TEST.api_ports = TestDataContainer()
|
||||
TEST.api_routers = TestDataContainer()
|
||||
TEST.api_q_floating_ips = TestDataContainer()
|
||||
TEST.api_pools = TestDataContainer()
|
||||
TEST.api_vips = TestDataContainer()
|
||||
TEST.api_members = TestDataContainer()
|
||||
TEST.api_monitors = TestDataContainer()
|
||||
|
||||
#------------------------------------------------------------
|
||||
# 1st network
|
||||
@ -235,3 +244,112 @@ def data(TEST):
|
||||
'router_id': router_dict['id']}
|
||||
TEST.api_q_floating_ips.add(fip_dict)
|
||||
TEST.q_floating_ips.add(FloatingIp(fip_dict))
|
||||
|
||||
#------------------------------------------------------------
|
||||
# LBaaS
|
||||
|
||||
# 1st pool
|
||||
pool_dict = {'id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49',
|
||||
'tenant_id': '1',
|
||||
'vip_id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'pool1',
|
||||
'description': 'pool description',
|
||||
'subnet_id': TEST.subnets.first().id,
|
||||
'protocol': 'HTTP',
|
||||
'lb_method': 'ROUND_ROBIN',
|
||||
'admin_state_up': True}
|
||||
TEST.api_pools.add(pool_dict)
|
||||
TEST.pools.add(Pool(pool_dict))
|
||||
|
||||
# 1st vip
|
||||
vip_dict = {'id': 'abcdef-c3eb-4fee-9763-12de3338041e',
|
||||
'name': 'vip1',
|
||||
'address': '10.0.0.100',
|
||||
'description': 'vip description',
|
||||
'subnet_id': TEST.subnets.first().id,
|
||||
'protocol_port': '80',
|
||||
'protocol': pool_dict['protocol'],
|
||||
'pool_id': pool_dict['id'],
|
||||
'session_persistence': {'type': 'SOURCE_IP',
|
||||
'cookie_name': 'jssessionid'},
|
||||
'connection_limit': '10',
|
||||
'admin_state_up': True}
|
||||
TEST.api_vips.add(vip_dict)
|
||||
TEST.vips.add(Vip(vip_dict))
|
||||
|
||||
# 1st member
|
||||
member_dict = {'id': '78a46e5e-eb1a-418a-88c7-0e3f5968b08',
|
||||
'tenant_id': '1',
|
||||
'pool_id': pool_dict['id'],
|
||||
'address': '10.0.0.11',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True}
|
||||
TEST.api_members.add(member_dict)
|
||||
TEST.members.add(Member(member_dict))
|
||||
|
||||
# 2nd member
|
||||
member_dict = {'id': '41ac1f8d-6d9c-49a4-a1bf-41955e651f91',
|
||||
'tenant_id': '1',
|
||||
'pool_id': pool_dict['id'],
|
||||
'address': '10.0.0.12',
|
||||
'protocol_port': '80',
|
||||
'weight': '10',
|
||||
'admin_state_up': True}
|
||||
TEST.api_members.add(member_dict)
|
||||
TEST.members.add(Member(member_dict))
|
||||
|
||||
# 2nd pool
|
||||
pool_dict = {'id': '8913dde8-4915-4b90-8d3e-b95eeedb0d50',
|
||||
'tenant_id': '1',
|
||||
'vip_id': 'f0881d38-c3eb-4fee-9763-12de3338041d',
|
||||
'name': 'pool2',
|
||||
'description': 'pool description',
|
||||
'subnet_id': TEST.subnets.first().id,
|
||||
'protocol': 'HTTPS',
|
||||
'lb_method': 'ROUND_ROBIN',
|
||||
'admin_state_up': True}
|
||||
TEST.api_pools.add(pool_dict)
|
||||
TEST.pools.add(Pool(pool_dict))
|
||||
|
||||
# 1st vip
|
||||
vip_dict = {'id': 'f0881d38-c3eb-4fee-9763-12de3338041d',
|
||||
'name': 'vip2',
|
||||
'address': '10.0.0.110',
|
||||
'description': 'vip description',
|
||||
'subnet_id': TEST.subnets.first().id,
|
||||
'protocol_port': '80',
|
||||
'protocol': pool_dict['protocol'],
|
||||
'pool_id': pool_dict['id'],
|
||||
'session_persistence': {'type': 'APP_COOKIE',
|
||||
'cookie_name': 'jssessionid'},
|
||||
'connection_limit': '10',
|
||||
'admin_state_up': True}
|
||||
TEST.api_vips.add(vip_dict)
|
||||
TEST.vips.add(Vip(vip_dict))
|
||||
|
||||
# 1st monitor
|
||||
monitor_dict = {'id': 'd4a0500f-db2b-4cc4-afcf-ec026febff96',
|
||||
'type': 'PING',
|
||||
'delay': '10',
|
||||
'timeout': '10',
|
||||
'max_retries': '10',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/',
|
||||
'expected_codes': '200',
|
||||
'admin_state_up': True}
|
||||
TEST.api_monitors.add(monitor_dict)
|
||||
TEST.monitors.add(PoolMonitor(monitor_dict))
|
||||
|
||||
# 2nd monitor
|
||||
monitor_dict = {'id': 'd4a0500f-db2b-4cc4-afcf-ec026febff97',
|
||||
'type': 'PING',
|
||||
'delay': '10',
|
||||
'timeout': '10',
|
||||
'max_retries': '10',
|
||||
'http_method': 'GET',
|
||||
'url_path': '/',
|
||||
'expected_codes': '200',
|
||||
'admin_state_up': True}
|
||||
TEST.api_monitors.add(monitor_dict)
|
||||
TEST.monitors.add(PoolMonitor(monitor_dict))
|
||||
|
Loading…
x
Reference in New Issue
Block a user