Merge "BigSwitch: Use backend floating IP endpoint"
This commit is contained in:
@@ -843,7 +843,11 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base,
|
|||||||
|
|
||||||
# create floatingip on the network controller
|
# create floatingip on the network controller
|
||||||
try:
|
try:
|
||||||
self._send_floatingip_update(context)
|
if 'floatingip' in self.servers.get_capabilities():
|
||||||
|
self.servers.rest_create_floatingip(
|
||||||
|
new_fl_ip['tenant_id'], new_fl_ip)
|
||||||
|
else:
|
||||||
|
self._send_floatingip_update(context)
|
||||||
except servermanager.RemoteRestError as e:
|
except servermanager.RemoteRestError as e:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error(
|
LOG.error(
|
||||||
@@ -861,7 +865,11 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base,
|
|||||||
self).update_floatingip(context, id, floatingip)
|
self).update_floatingip(context, id, floatingip)
|
||||||
|
|
||||||
# update network on network controller
|
# update network on network controller
|
||||||
self._send_floatingip_update(context)
|
if 'floatingip' in self.servers.get_capabilities():
|
||||||
|
self.servers.rest_update_floatingip(new_fl_ip['tenant_id'],
|
||||||
|
new_fl_ip, id)
|
||||||
|
else:
|
||||||
|
self._send_floatingip_update(context)
|
||||||
return new_fl_ip
|
return new_fl_ip
|
||||||
|
|
||||||
def delete_floatingip(self, context, id):
|
def delete_floatingip(self, context, id):
|
||||||
@@ -869,10 +877,15 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base,
|
|||||||
|
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
# delete floating IP in DB
|
# delete floating IP in DB
|
||||||
|
old_fip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
||||||
|
id)
|
||||||
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
||||||
|
|
||||||
# update network on network controller
|
# update network on network controller
|
||||||
self._send_floatingip_update(context)
|
if 'floatingip' in self.servers.get_capabilities():
|
||||||
|
self.servers.rest_delete_floatingip(old_fip['tenant_id'], id)
|
||||||
|
else:
|
||||||
|
self._send_floatingip_update(context)
|
||||||
|
|
||||||
def disassociate_floatingips(self, context, port_id):
|
def disassociate_floatingips(self, context, port_id):
|
||||||
LOG.debug(_("NeutronRestProxyV2: diassociate_floatingips() called"))
|
LOG.debug(_("NeutronRestProxyV2: diassociate_floatingips() called"))
|
||||||
|
|||||||
@@ -45,11 +45,13 @@ from neutron.openstack.common import log as logging
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
# The following are used to invoke the API on the external controller
|
# The following are used to invoke the API on the external controller
|
||||||
|
CAPABILITIES_PATH = "/capabilities"
|
||||||
NET_RESOURCE_PATH = "/tenants/%s/networks"
|
NET_RESOURCE_PATH = "/tenants/%s/networks"
|
||||||
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports"
|
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports"
|
||||||
ROUTER_RESOURCE_PATH = "/tenants/%s/routers"
|
ROUTER_RESOURCE_PATH = "/tenants/%s/routers"
|
||||||
ROUTER_INTF_OP_PATH = "/tenants/%s/routers/%s/interfaces"
|
ROUTER_INTF_OP_PATH = "/tenants/%s/routers/%s/interfaces"
|
||||||
NETWORKS_PATH = "/tenants/%s/networks/%s"
|
NETWORKS_PATH = "/tenants/%s/networks/%s"
|
||||||
|
FLOATINGIPS_PATH = "/tenants/%s/floatingips/%s"
|
||||||
PORTS_PATH = "/tenants/%s/networks/%s/ports/%s"
|
PORTS_PATH = "/tenants/%s/networks/%s/ports/%s"
|
||||||
ATTACHMENT_PATH = "/tenants/%s/networks/%s/ports/%s/attachment"
|
ATTACHMENT_PATH = "/tenants/%s/networks/%s/ports/%s/attachment"
|
||||||
ROUTERS_PATH = "/tenants/%s/routers/%s"
|
ROUTERS_PATH = "/tenants/%s/routers/%s"
|
||||||
@@ -81,10 +83,23 @@ class ServerProxy(object):
|
|||||||
self.auth = None
|
self.auth = None
|
||||||
self.neutron_id = neutron_id
|
self.neutron_id = neutron_id
|
||||||
self.failed = False
|
self.failed = False
|
||||||
|
self.capabilities = []
|
||||||
if auth:
|
if auth:
|
||||||
self.auth = 'Basic ' + base64.encodestring(auth).strip()
|
self.auth = 'Basic ' + base64.encodestring(auth).strip()
|
||||||
|
|
||||||
def rest_call(self, action, resource, data, headers):
|
def get_capabilities(self):
|
||||||
|
try:
|
||||||
|
body = self.rest_call('GET', CAPABILITIES_PATH)[3]
|
||||||
|
self.capabilities = json.loads(body)
|
||||||
|
except Exception:
|
||||||
|
LOG.error(_("Couldn't retrieve capabilities. "
|
||||||
|
"Newer API calls won't be supported."))
|
||||||
|
LOG.info(_("The following capabilities were received "
|
||||||
|
"for %(server)s: %(cap)s"), {'server': self.server,
|
||||||
|
'cap': self.capabilities})
|
||||||
|
return self.capabilities
|
||||||
|
|
||||||
|
def rest_call(self, action, resource, data='', headers=None):
|
||||||
uri = self.base_uri + resource
|
uri = self.base_uri + resource
|
||||||
body = json.dumps(data)
|
body = json.dumps(data)
|
||||||
if not headers:
|
if not headers:
|
||||||
@@ -180,6 +195,19 @@ class ServerPool(object):
|
|||||||
]
|
]
|
||||||
LOG.debug(_("ServerPool: initialization done"))
|
LOG.debug(_("ServerPool: initialization done"))
|
||||||
|
|
||||||
|
def get_capabilities(self):
|
||||||
|
# lookup on first try
|
||||||
|
try:
|
||||||
|
return self.capabilities
|
||||||
|
except AttributeError:
|
||||||
|
# each server should return a list of capabilities it supports
|
||||||
|
# e.g. ['floatingip']
|
||||||
|
capabilities = [set(server.get_capabilities())
|
||||||
|
for server in self.servers]
|
||||||
|
# Pool only supports what all of the servers support
|
||||||
|
self.capabilities = set.intersection(*capabilities)
|
||||||
|
return self.capabilities
|
||||||
|
|
||||||
def server_proxy_for(self, server, port):
|
def server_proxy_for(self, server, port):
|
||||||
return ServerProxy(server, port, self.ssl, self.auth, self.neutron_id,
|
return ServerProxy(server, port, self.ssl, self.auth, self.neutron_id,
|
||||||
self.timeout, self.base_uri, self.name)
|
self.timeout, self.base_uri, self.name)
|
||||||
@@ -318,3 +346,18 @@ class ServerPool(object):
|
|||||||
# Controller has no update operation for the port endpoint
|
# Controller has no update operation for the port endpoint
|
||||||
# the create PUT method will replace
|
# the create PUT method will replace
|
||||||
self.rest_create_port(tenant_id, net_id, port)
|
self.rest_create_port(tenant_id, net_id, port)
|
||||||
|
|
||||||
|
def rest_create_floatingip(self, tenant_id, floatingip):
|
||||||
|
resource = FLOATINGIPS_PATH % (tenant_id, floatingip['id'])
|
||||||
|
errstr = _("Unable to create floating IP: %s")
|
||||||
|
self.rest_action('PUT', resource, errstr=errstr)
|
||||||
|
|
||||||
|
def rest_update_floatingip(self, tenant_id, floatingip, oldid):
|
||||||
|
resource = FLOATINGIPS_PATH % (tenant_id, oldid)
|
||||||
|
errstr = _("Unable to update floating IP: %s")
|
||||||
|
self.rest_action('PUT', resource, errstr=errstr)
|
||||||
|
|
||||||
|
def rest_delete_floatingip(self, tenant_id, oldid):
|
||||||
|
resource = FLOATINGIPS_PATH % (tenant_id, oldid)
|
||||||
|
errstr = _("Unable to delete floating IP: %s")
|
||||||
|
self.rest_action('DELETE', resource, errstr=errstr)
|
||||||
|
|||||||
65
neutron/tests/unit/bigswitch/test_capabilities.py
Normal file
65
neutron/tests/unit/bigswitch/test_capabilities.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
# Copyright 2014 Big Switch Networks, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# @author Kevin Benton
|
||||||
|
|
||||||
|
from contextlib import nested
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from neutron.tests.unit.bigswitch import test_router_db
|
||||||
|
|
||||||
|
PLUGIN = 'neutron.plugins.bigswitch.plugin'
|
||||||
|
SERVERMANAGER = PLUGIN + '.servermanager'
|
||||||
|
SERVERPOOL = SERVERMANAGER + '.ServerPool'
|
||||||
|
SERVERRESTCALL = SERVERMANAGER + '.ServerProxy.rest_call'
|
||||||
|
|
||||||
|
|
||||||
|
class CapabilitiesTests(test_router_db.RouterDBTestCase):
|
||||||
|
|
||||||
|
def test_floating_ip_capability(self):
|
||||||
|
with nested(
|
||||||
|
mock.patch(SERVERRESTCALL,
|
||||||
|
return_value=(200, None, None, '["floatingip"]')),
|
||||||
|
mock.patch(SERVERPOOL + '.rest_create_floatingip',
|
||||||
|
return_value=(200, None, None, None)),
|
||||||
|
mock.patch(SERVERPOOL + '.rest_delete_floatingip',
|
||||||
|
return_value=(200, None, None, None))
|
||||||
|
) as (mock_rest, mock_create, mock_delete):
|
||||||
|
with self.floatingip_with_assoc() as fip:
|
||||||
|
pass
|
||||||
|
mock_create.assert_has_calls(
|
||||||
|
[mock.call(fip['floatingip']['tenant_id'], fip['floatingip'])]
|
||||||
|
)
|
||||||
|
mock_delete.assert_has_calls(
|
||||||
|
[mock.call(fip['floatingip']['tenant_id'],
|
||||||
|
fip['floatingip']['id'])]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_floating_ip_capability_neg(self):
|
||||||
|
with nested(
|
||||||
|
mock.patch(SERVERRESTCALL,
|
||||||
|
return_value=(200, None, None, '[""]')),
|
||||||
|
mock.patch(SERVERPOOL + '.rest_update_network',
|
||||||
|
return_value=(200, None, None, None))
|
||||||
|
) as (mock_rest, mock_netupdate):
|
||||||
|
with self.floatingip_with_assoc() as fip:
|
||||||
|
pass
|
||||||
|
updates = [call[0][2]['floatingips']
|
||||||
|
for call in mock_netupdate.call_args_list]
|
||||||
|
all_floats = [f['floating_ip_address']
|
||||||
|
for floats in updates for f in floats]
|
||||||
|
self.assertIn(fip['floatingip']['floating_ip_address'], all_floats)
|
||||||
Reference in New Issue
Block a user