Merge "LBAAS VIP floating IP table actions"

This commit is contained in:
Jenkins 2015-07-16 16:16:44 +00:00 committed by Gerrit Code Review
commit e205c813f9
8 changed files with 172 additions and 7 deletions

View File

@ -478,6 +478,7 @@ class FloatingIpManager(network_base.FloatingIpManager):
continue
target = {'name': '%s: %s' % (server_name, ip['ip_address']),
'id': '%s_%s' % (port_id, ip['ip_address']),
'port_id': port_id,
'instance_id': p.device_id}
targets.append(FloatingIpTarget(target))
return targets

View File

@ -77,6 +77,27 @@ class FloatingIpViewTests(test.TestCase):
# Verify that our "associated" floating IP isn't in the choices list.
self.assertTrue(self.floating_ips.first() not in choices)
@test.create_stubs({api.network: ('floating_ip_target_list',
'tenant_floating_ip_list',)})
def test_associate_with_port_id(self):
targets = [api.nova.FloatingIpTarget(s) for s in self.servers.list()]
targets[0].port_id = '101'
api.network.floating_ip_target_list(IsA(http.HttpRequest)) \
.AndReturn(targets)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
self.mox.ReplayAll()
base_url = reverse('%s:associate' % NAMESPACE)
params = urlencode({'port_id': '101'})
url = '?'.join([base_url, params])
res = self.client.get(url)
self.assertTemplateUsed(res, views.WorkflowView.template_name)
workflow = res.context['workflow']
choices = dict(workflow.steps[0].action.fields['ip_id'].choices)
# Verify that our "associated" floating IP isn't in the choices list.
self.assertTrue(self.floating_ips.first() not in choices)
@test.create_stubs({api.network: ('floating_ip_associate',
'floating_ip_target_list',
'tenant_floating_ip_list',)})

View File

@ -55,11 +55,19 @@ class AssociateIPAction(workflows.Action):
# to get an association target based on a received instance_id
# and set the initial value of instance_id ChoiceField.
q_instance_id = self.request.GET.get('instance_id')
q_port_id = self.request.GET.get('port_id')
if q_instance_id:
targets = self._get_target_list()
target_id = api.network.floating_ip_target_get_by_instance(
self.request, q_instance_id, targets)
self.initial['instance_id'] = target_id
elif q_port_id:
targets = self._get_target_list()
for target in targets:
if (hasattr(target, 'port_id') and
target.port_id == q_port_id):
self.initial['instance_id'] = target.id
break
def populate_ip_id_choices(self, request, context):
ips = []

View File

@ -14,17 +14,23 @@
from django.core.urlresolvers import reverse
from django import shortcuts
from django import template
from django.template import defaultfilters as filters
from django.utils import http
from django.utils.http import urlencode
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import conf
from horizon import exceptions
from horizon import messages
from horizon import tables
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.access_and_security.floating_ips \
import workflows
from openstack_dashboard import policy
@ -259,6 +265,85 @@ class DeletePMAssociationLink(policy.PolicyTargetMixin,
return True
class AddVIPFloatingIP(policy.PolicyTargetMixin, tables.LinkAction):
"""Add floating ip to VIP
This class is extremely similar to AssociateIP from
the instances page
"""
name = "associate"
verbose_name = _("Associate Floating IP")
url = "horizon:project:access_and_security:floating_ips:associate"
classes = ("ajax-modal",)
icon = "link"
policy_rules = (("compute", "network:associate_floating_ip"),)
def allowed(self, request, pool):
if not api.network.floating_ip_supported(request):
return False
if api.network.floating_ip_simple_associate_supported(request):
return False
if hasattr(pool, "vip") and pool.vip:
vip = pool.vip
return not (hasattr(vip, "fip") and vip.fip)
return True
def get_link_url(self, datum):
base_url = reverse(self.url)
next_url = self.table.get_full_url()
params = {
workflows.IPAssociationWorkflow.redirect_param_name: next_url}
if hasattr(datum, "vip") and datum.vip:
vip = datum.vip
params['port_id'] = vip.port_id
params = urlencode(params)
return "?".join([base_url, params])
class RemoveVIPFloatingIP(policy.PolicyTargetMixin, tables.Action):
"""Remove floating IP from VIP
This class is extremely similar to the project instance table
SimpleDisassociateIP feature, but just different enough to not
be able to share much code
"""
name = "disassociate"
preempt = True
icon = "unlink"
verbose_name = _("Disassociate Floating IP")
classes = ("btn-danger", "btn-disassociate",)
policy_rules = (("compute", "network:disassociate_floating_ip"),)
def allowed(self, request, pool):
if not api.network.floating_ip_supported(request):
return False
if not conf.HORIZON_CONFIG["simple_ip_management"]:
return False
if hasattr(pool, "vip") and pool.vip:
vip = pool.vip
return (hasattr(vip, "fip") and vip.fip)
return False
def single(self, table, request, pool_id):
try:
pool = api.lbaas.pool_get(request, pool_id)
fips = api.network.tenant_floating_ip_list(request)
vip_fips = [fip for fip in fips
if fip.port_id == pool.vip.port_id]
if not vip_fips:
messages.info(request, _("No floating IPs to disassociate."))
else:
api.network.floating_ip_disassociate(request,
vip_fips[0].id)
messages.success(request,
_("Successfully disassociated "
"floating IP: %s") % fip.ip)
except Exception:
exceptions.handle(request,
_("Unable to disassociate floating IP."))
return shortcuts.redirect(request.get_full_path())
class UpdatePoolsRow(tables.Row):
ajax = True
@ -358,7 +443,8 @@ class PoolsTable(tables.DataTable):
table_actions = (AddPoolLink, DeletePoolLink)
row_actions = (UpdatePoolLink, AddVipLink, UpdateVipLink,
DeleteVipLink, AddPMAssociationLink,
DeletePMAssociationLink, DeletePoolLink)
DeletePMAssociationLink, DeletePoolLink,
AddVIPFloatingIP, RemoveVIPFloatingIP)
def get_pool_link(member):

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from mox3.mox import IgnoreArg # noqa
from mox3.mox import IsA # noqa
from django.core.urlresolvers import reverse
@ -69,6 +70,12 @@ class LoadBalancerTests(test.TestCase):
IsA(http.HttpRequest), tenant_id=self.tenant.id).MultipleTimes() \
.AndReturn(self.monitors.list())
api.network.floating_ip_supported(IgnoreArg()).MultipleTimes() \
.AndReturn(True)
api.network.floating_ip_simple_associate_supported(IgnoreArg()).MultipleTimes() \
.AndReturn(True)
def set_up_expect_with_exception(self):
api.lbaas.pool_list(
IsA(http.HttpRequest), tenant_id=self.tenant.id) \
@ -81,7 +88,10 @@ class LoadBalancerTests(test.TestCase):
.AndRaise(self.exceptions.neutron)
@test.create_stubs({api.lbaas: ('pool_list', 'member_list',
'pool_health_monitor_list')})
'pool_health_monitor_list'),
api.network: ('floating_ip_supported',
'floating_ip_simple_associate_supported')
})
def test_index_pools(self):
self.set_up_expect()
@ -96,7 +106,10 @@ class LoadBalancerTests(test.TestCase):
len(self.pools.list()))
@test.create_stubs({api.lbaas: ('pool_list', 'member_list',
'pool_health_monitor_list')})
'pool_health_monitor_list'),
api.network: ('floating_ip_supported',
'floating_ip_simple_associate_supported')
})
def test_index_members(self):
self.set_up_expect()
@ -111,7 +124,10 @@ class LoadBalancerTests(test.TestCase):
len(self.members.list()))
@test.create_stubs({api.lbaas: ('pool_list', 'member_list',
'pool_health_monitor_list')})
'pool_health_monitor_list'),
api.network: ('floating_ip_supported',
'floating_ip_simple_associate_supported')
})
def test_index_monitors(self):
self.set_up_expect()
@ -903,6 +919,21 @@ class LoadBalancerTests(test.TestCase):
self.assertNoFormErrors(res)
@test.create_stubs({api.lbaas: ('pool_get', ),
api.network: ('tenant_floating_ip_list',
'floating_ip_disassociate', )})
def test_disassociate_vip_fip(self):
pool = self.pools.first()
fips = self.floating_ips.list()
api.lbaas.pool_get(IsA(http.HttpRequest), pool.id).AndReturn(pool)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)).\
AndReturn(fips)
api.network.floating_ip_disassociate(IsA(http.HttpRequest), 3)
self.mox.ReplayAll()
form_data = {"action": "poolstable__disassociate__%s" % pool.id}
res = self.client.post(self.INDEX_URL, form_data)
self.assertNoFormErrors(res)
@test.create_stubs({api.lbaas: ('member_list', 'member_delete')})
def test_delete_member(self):
member = self.members.first()

View File

@ -43,9 +43,17 @@ class IndexView(tabs.TabbedTableView):
page_title = _("Load Balancer")
def post(self, request, *args, **kwargs):
"""This method is messy because table actions
were not implemented correctly. ideally,
this code can be refactored to move items into
table actions
"""
obj_ids = request.POST.getlist('object_ids')
action = request.POST['action']
m = re.search('.delete([a-z]+)', action).group(1)
results = re.search('.delete([a-z]+)', action)
if not results:
return super(IndexView, self).post(request, *args, **kwargs)
m = results.group(1)
if obj_ids == []:
obj_ids.append(re.search('([0-9a-z-]+)$', action).group(1))
if m == 'monitor':

View File

@ -586,6 +586,7 @@ def data(TEST):
'admin_state_up': True}
TEST.api_vips.add(vip_dict)
TEST.vips.add(lbaas.Vip(vip_dict))
setattr(TEST.pools.first(), 'vip', TEST.vips.first())
# 2nd vip.
vip_dict = {'id': 'f0881d38-c3eb-4fee-9763-12de3338041d',

View File

@ -516,10 +516,19 @@ def data(TEST):
'instance_id': None,
'ip': '58.58.58.58',
'pool': 'pool2'}
TEST.api_floating_ips.add(generate_fip(fip_1), generate_fip(fip_2))
# this floating ip is for lbaas tests
fip_3 = {'id': 3,
'fixed_ip': '10.0.0.5',
# the underlying class maps the instance id to port id
'instance_id': '063cf7f3-ded1-4297-bc4c-31eae876cc91',
'ip': '58.58.58.58',
'pool': 'pool2'}
TEST.api_floating_ips.add(generate_fip(fip_1), generate_fip(fip_2),
generate_fip(fip_3))
TEST.floating_ips.add(nova.FloatingIp(generate_fip(fip_1)),
nova.FloatingIp(generate_fip(fip_2)))
nova.FloatingIp(generate_fip(fip_2)),
nova.FloatingIp(generate_fip(fip_3)))
# Floating IP with UUID id (for Floating IP with Neutron Proxy)
fip_3 = {'id': str(uuid.uuid4()),