BigSwitch: Use backend floating IP endpoint
Adds new floating IP REST calls for backend controllers that support it. Adds backend capability discovery mechanism. Implements: blueprint bsn-floating-ip-endpoints Change-Id: I2301d62a05d256867255865556625603918e84cf
This commit is contained in:
parent
a872951552
commit
6047d27013
@ -842,7 +842,11 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base,
|
||||
|
||||
# create floatingip on the network controller
|
||||
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:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(
|
||||
@ -860,7 +864,11 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base,
|
||||
self).update_floatingip(context, id, floatingip)
|
||||
|
||||
# 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
|
||||
|
||||
def delete_floatingip(self, context, id):
|
||||
@ -868,10 +876,15 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base,
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
# delete floating IP in DB
|
||||
old_fip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
||||
id)
|
||||
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
||||
|
||||
# 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):
|
||||
LOG.debug(_("NeutronRestProxyV2: diassociate_floatingips() called"))
|
||||
|
@ -45,11 +45,13 @@ from neutron.openstack.common import log as logging
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# The following are used to invoke the API on the external controller
|
||||
CAPABILITIES_PATH = "/capabilities"
|
||||
NET_RESOURCE_PATH = "/tenants/%s/networks"
|
||||
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports"
|
||||
ROUTER_RESOURCE_PATH = "/tenants/%s/routers"
|
||||
ROUTER_INTF_OP_PATH = "/tenants/%s/routers/%s/interfaces"
|
||||
NETWORKS_PATH = "/tenants/%s/networks/%s"
|
||||
FLOATINGIPS_PATH = "/tenants/%s/floatingips/%s"
|
||||
PORTS_PATH = "/tenants/%s/networks/%s/ports/%s"
|
||||
ATTACHMENT_PATH = "/tenants/%s/networks/%s/ports/%s/attachment"
|
||||
ROUTERS_PATH = "/tenants/%s/routers/%s"
|
||||
@ -81,10 +83,23 @@ class ServerProxy(object):
|
||||
self.auth = None
|
||||
self.neutron_id = neutron_id
|
||||
self.failed = False
|
||||
self.capabilities = []
|
||||
if auth:
|
||||
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
|
||||
body = json.dumps(data)
|
||||
if not headers:
|
||||
@ -180,6 +195,19 @@ class ServerPool(object):
|
||||
]
|
||||
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):
|
||||
return ServerProxy(server, port, self.ssl, self.auth, self.neutron_id,
|
||||
self.timeout, self.base_uri, self.name)
|
||||
@ -318,3 +346,18 @@ class ServerPool(object):
|
||||
# Controller has no update operation for the port endpoint
|
||||
# the create PUT method will replace
|
||||
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)
|
Loading…
Reference in New Issue
Block a user