Skip to content

Commit

Permalink
Add support for the extra route extension in the NVP plugin.
Browse files Browse the repository at this point in the history
The underlying feature is available in NVP 3.2, which introduces a
new type of router. Therefore, create_lrouter needs to be made
version 'aware'.

This led to a number of fixes in the nvplib, especially around how
version is retrieved and how version-dependent methods are called.

Implements blueprint nvp-extra-route-extension

Change-Id: Ie4e2d93f70e1948a62563c8523aea61bb2194c84
  • Loading branch information
armando-migliaccio committed Jul 12, 2013
1 parent bd18990 commit ff7bb5f
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 81 deletions.
42 changes: 34 additions & 8 deletions neutron/plugins/nicira/NeutronPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@
from neutron.db import api as db
from neutron.db import db_base_plugin_v2
from neutron.db import dhcp_rpc_base
from neutron.db import extraroute_db
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.db import portsecurity_db
from neutron.db import quota_db # noqa
from neutron.db import securitygroups_db
from neutron.extensions import extraroute
from neutron.extensions import l3
from neutron.extensions import portsecurity as psec
from neutron.extensions import providernet as pnet
Expand Down Expand Up @@ -124,7 +126,7 @@ def create_rpc_dispatcher(self):


class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
l3_db.L3_NAT_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
portsecurity_db.PortSecurityDbMixin,
securitygroups_db.SecurityGroupDbMixin,
mac_db.MacLearningDbMixin,
Expand All @@ -139,7 +141,8 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
functionality using NVP.
"""

supported_extension_aliases = ["mac-learning",
supported_extension_aliases = ["extraroute",
"mac-learning",
"network-gateway",
"nvp-qos",
"port-security",
Expand Down Expand Up @@ -1458,7 +1461,7 @@ def create_router(self, context, router):
self._update_router_gw_info(context, router_db['id'], gw_info)
return self._make_router_dict(router_db)

def update_router(self, context, id, router):
def update_router(self, context, router_id, router):
# Either nexthop is updated or should be kept as it was before
r = router['router']
nexthop = None
Expand All @@ -1479,22 +1482,45 @@ def update_router(self, context, id, router):
ext_subnet = ext_net.subnets[0]
nexthop = ext_subnet.gateway_ip
try:
nvplib.update_lrouter(self.cluster, id,
router['router'].get('name'), nexthop)
for route in r.get('routes', []):
if route['destination'] == '0.0.0.0/0':
msg = _("'routes' cannot contain route '0.0.0.0/0', "
"this must be updated through the default "
"gateway attribute")
raise q_exc.BadRequest(resource='router', msg=msg)
previous_routes = nvplib.update_lrouter(
self.cluster, router_id, r.get('name'),
nexthop, routes=r.get('routes'))
# NOTE(salv-orlando): The exception handling below is not correct, but
# unfortunately nvplib raises a neutron notfound exception when an
# object is not found in the underlying backend
except q_exc.NotFound:
# Put the router in ERROR status
with context.session.begin(subtransactions=True):
router_db = self._get_router(context, id)
router_db = self._get_router(context, router_id)
router_db['status'] = constants.NET_STATUS_ERROR
raise nvp_exc.NvpPluginException(
err_msg=_("Logical router %s not found on NVP Platform") % id)
err_msg=_("Logical router %s not found "
"on NVP Platform") % router_id)
except NvpApiClient.NvpApiException:
raise nvp_exc.NvpPluginException(
err_msg=_("Unable to update logical router on NVP Platform"))
return super(NvpPluginV2, self).update_router(context, id, router)
except nvp_exc.NvpInvalidVersion:
msg = _("Request cannot contain 'routes' with the NVP "
"platform currently in execution. Please, try "
"without specifying the static routes.")
LOG.exception(msg)
raise q_exc.BadRequest(resource='router', msg=msg)
try:
return super(NvpPluginV2, self).update_router(context,
router_id, router)
except (extraroute.InvalidRoutes,
extraroute.RouterInterfaceInUseByRoute,
extraroute.RoutesExhausted):
with excutils.save_and_reraise_exception():
# revert changes made to NVP
nvplib.update_explicit_routes_lrouter(
self.cluster, router_id, previous_routes)

def delete_router(self, context, id):
with context.session.begin(subtransactions=True):
Expand Down
25 changes: 20 additions & 5 deletions neutron/plugins/nicira/NvpApiClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,24 @@ def _find_nvp_version_in_headers(headers):
for (header_name, header_value) in (headers or ()):
try:
if header_name == 'server':
return header_value.split('/')[1]
return NVPVersion(header_value.split('/')[1])
except IndexError:
LOG.warning(_("Unable to fetch NVP version from response "
"headers:%s"), headers)


class NVPVersion(object):
"""Abstracts NVP version by exposing major and minor."""

def __init__(self, nvp_version):
self.full_version = nvp_version.split('.')
self.major = int(self.full_version[0])
self.minor = int(self.full_version[1])

def __str__(self):
return '.'.join(self.full_version)


class NVPApiHelper(client_eventlet.NvpApiClientEventlet):
'''API helper class.
Expand Down Expand Up @@ -153,10 +165,13 @@ def request(self, method, url, body="", content_type="application/json"):

def get_nvp_version(self):
if not self._nvp_version:
# generate a simple request (/ws.v1/log)
# this will cause nvp_version to be fetched
# don't bother about response
self.request('GET', '/ws.v1/log')
# Determine the NVP version by querying the control
# cluster nodes. Currently, the version will be the
# one of the server that responds.
self.request('GET', '/ws.v1/control-cluster/node')
if not self._nvp_version:
LOG.error(_('Unable to determine NVP version. '
'Plugin might not work as expected.'))
return self._nvp_version

def fourZeroFour(self):
Expand Down
5 changes: 3 additions & 2 deletions neutron/plugins/nicira/api_client/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,9 @@ def _issue_request(self):
# the conn to be released with is_conn_error == True
# which puts the conn on the back of the client's priority
# queue.
if response.status >= 500:
LOG.warn(_("[%(rid)d] Request '%(method) %(url)s' "
if (response.status == httplib.INTERNAL_SERVER_ERROR and
response.status > httplib.NOT_IMPLEMENTED):
LOG.warn(_("[%(rid)d] Request '%(method)s %(url)s' "
"received: %(status)s"),
{'rid': self._rid(), 'method': self._method,
'url': self._url, 'status': response.status})
Expand Down
4 changes: 4 additions & 0 deletions neutron/plugins/nicira/common/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class NvpPluginException(q_exc.NeutronException):
message = _("An unexpected error occurred in the NVP Plugin:%(err_msg)s")


class NvpInvalidVersion(NvpPluginException):
message = _("Unable to fulfill request with version %(version)s.")


class NvpInvalidConnection(NvpPluginException):
message = _("Invalid NVP connection parameters: %(conn_params)s")

Expand Down

0 comments on commit ff7bb5f

Please sign in to comment.