Merge "LBAAS VIP floating IP table actions"
This commit is contained in:
commit
e205c813f9
@ -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
|
||||
|
@ -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',)})
|
||||
|
@ -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 = []
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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':
|
||||
|
@ -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',
|
||||
|
@ -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()),
|
||||
|
Loading…
x
Reference in New Issue
Block a user