# 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(
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):
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"))
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"
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:
]
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)
# 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)
--- /dev/null
+# 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)