Add support for the extra route extension in the NVP plugin.
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
This commit is contained in:
parent
2890f4c956
commit
3c881595d7
@ -42,11 +42,13 @@ from neutron.db import agentschedulers_db
|
||||
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
|
||||
@ -124,7 +126,7 @@ class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
||||
|
||||
|
||||
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,
|
||||
@ -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",
|
||||
@ -1458,7 +1461,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
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
|
||||
@ -1479,22 +1482,45 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
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):
|
||||
|
@ -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.
|
||||
|
||||
@ -153,10 +165,13 @@ class NVPApiHelper(client_eventlet.NvpApiClientEventlet):
|
||||
|
||||
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):
|
||||
|
@ -190,8 +190,9 @@ class NvpApiRequest(object):
|
||||
# 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})
|
||||
|
@ -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")
|
||||
|
||||
|
@ -30,6 +30,7 @@ from oslo.config import cfg
|
||||
# no neutron-specific logic in it
|
||||
from neutron.common import constants
|
||||
from neutron.common import exceptions as exception
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.nicira.common import (
|
||||
exceptions as nvp_exc)
|
||||
@ -48,11 +49,12 @@ URI_PREFIX = "/ws.v1"
|
||||
LSWITCH_RESOURCE = "lswitch"
|
||||
LSWITCHPORT_RESOURCE = "lport/%s" % LSWITCH_RESOURCE
|
||||
LROUTER_RESOURCE = "lrouter"
|
||||
# Current neutron version
|
||||
LROUTERPORT_RESOURCE = "lport/%s" % LROUTER_RESOURCE
|
||||
LROUTERRIB_RESOURCE = "rib/%s" % LROUTER_RESOURCE
|
||||
LROUTERNAT_RESOURCE = "nat/lrouter"
|
||||
LQUEUE_RESOURCE = "lqueue"
|
||||
GWSERVICE_RESOURCE = "gateway-service"
|
||||
# Current neutron version
|
||||
NEUTRON_VERSION = "2013.1"
|
||||
# Other constants for NVP resource
|
||||
MAX_DISPLAY_NAME_LEN = 40
|
||||
@ -74,16 +76,29 @@ taken_context_ids = []
|
||||
_lqueue_cache = {}
|
||||
|
||||
|
||||
def version_dependent(func):
|
||||
func_name = func.__name__
|
||||
def version_dependent(wrapped_func):
|
||||
func_name = wrapped_func.__name__
|
||||
|
||||
def dispatch_version_dependent_function(cluster, *args, **kwargs):
|
||||
nvp_ver = cluster.api_client.get_nvp_version()
|
||||
if nvp_ver:
|
||||
ver_major = int(nvp_ver.split('.')[0])
|
||||
real_func = NVPLIB_FUNC_DICT[func_name][ver_major]
|
||||
# Call the wrapper function, in case we need to
|
||||
# run validation checks regarding versions. It
|
||||
# should return the NVP version
|
||||
v = (wrapped_func(cluster, *args, **kwargs) or
|
||||
cluster.api_client.get_nvp_version())
|
||||
if v:
|
||||
func = (NVPLIB_FUNC_DICT[func_name][v.major].get(v.minor) or
|
||||
NVPLIB_FUNC_DICT[func_name][v.major]['default'])
|
||||
if func is None:
|
||||
LOG.error(_('NVP version %(ver)s does not support method '
|
||||
'%(fun)s.') % {'ver': v, 'fun': func_name})
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
raise NvpApiClient.ServiceUnavailable('NVP version is not set. '
|
||||
'Unable to complete request'
|
||||
'correctly. Check log for '
|
||||
'NVP communication errors.')
|
||||
func_kwargs = kwargs
|
||||
arg_spec = inspect.getargspec(real_func)
|
||||
arg_spec = inspect.getargspec(func)
|
||||
if not arg_spec.keywords and not arg_spec.varargs:
|
||||
# drop args unknown to function from func_args
|
||||
arg_set = set(func_kwargs.keys())
|
||||
@ -91,7 +106,7 @@ def version_dependent(func):
|
||||
del func_kwargs[arg]
|
||||
# NOTE(salvatore-orlando): shall we fail here if a required
|
||||
# argument is not passed, or let the called function raise?
|
||||
real_func(cluster, *args, **func_kwargs)
|
||||
return func(cluster, *args, **func_kwargs)
|
||||
|
||||
return dispatch_version_dependent_function
|
||||
|
||||
@ -284,7 +299,22 @@ def create_l2_gw_service(cluster, tenant_id, display_name, devices):
|
||||
json.dumps(gwservice_obj), cluster=cluster)
|
||||
|
||||
|
||||
def create_lrouter(cluster, tenant_id, display_name, nexthop):
|
||||
def _prepare_lrouter_body(name, tenant_id, router_type, **kwargs):
|
||||
body = {
|
||||
"display_name": _check_and_truncate_name(name),
|
||||
"tags": [{"tag": tenant_id, "scope": "os_tid"},
|
||||
{"tag": NEUTRON_VERSION, "scope": "quantum"}],
|
||||
"routing_config": {
|
||||
"type": router_type
|
||||
},
|
||||
"type": "LogicalRouterConfig"
|
||||
}
|
||||
if kwargs:
|
||||
body["routing_config"].update(kwargs)
|
||||
return body
|
||||
|
||||
|
||||
def create_implicit_routing_lrouter(cluster, tenant_id, display_name, nexthop):
|
||||
"""Create a NVP logical router on the specified cluster.
|
||||
|
||||
:param cluster: The target NVP cluster
|
||||
@ -295,25 +325,36 @@ def create_lrouter(cluster, tenant_id, display_name, nexthop):
|
||||
:raise NvpApiException: if there is a problem while communicating
|
||||
with the NVP controller
|
||||
"""
|
||||
display_name = _check_and_truncate_name(display_name)
|
||||
tags = [{"tag": tenant_id, "scope": "os_tid"},
|
||||
{"tag": NEUTRON_VERSION, "scope": "quantum"}]
|
||||
lrouter_obj = {
|
||||
"display_name": display_name,
|
||||
"tags": tags,
|
||||
"routing_config": {
|
||||
"default_route_next_hop": {
|
||||
"gateway_ip_address": nexthop,
|
||||
"type": "RouterNextHop"
|
||||
},
|
||||
"type": "SingleDefaultRouteImplicitRoutingConfig"
|
||||
implicit_routing_config = {
|
||||
"default_route_next_hop": {
|
||||
"gateway_ip_address": nexthop,
|
||||
"type": "RouterNextHop"
|
||||
},
|
||||
"type": "LogicalRouterConfig"
|
||||
}
|
||||
lrouter_obj = _prepare_lrouter_body(
|
||||
display_name, tenant_id,
|
||||
"SingleDefaultRouteImplicitRoutingConfig",
|
||||
**implicit_routing_config)
|
||||
return do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE),
|
||||
json.dumps(lrouter_obj), cluster=cluster)
|
||||
|
||||
|
||||
def create_explicit_routing_lrouter(cluster, tenant_id,
|
||||
display_name, nexthop):
|
||||
lrouter_obj = _prepare_lrouter_body(
|
||||
display_name, tenant_id, "RoutingTableRoutingConfig")
|
||||
router = do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE),
|
||||
json.dumps(lrouter_obj), cluster=cluster)
|
||||
default_gw = {'prefix': '0.0.0.0/0', 'next_hop_ip': nexthop}
|
||||
create_explicit_route_lrouter(cluster, router['uuid'], default_gw)
|
||||
return router
|
||||
|
||||
|
||||
@version_dependent
|
||||
def create_lrouter(cluster, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def delete_lrouter(cluster, lrouter_id):
|
||||
do_request(HTTP_DELETE, _build_uri_path(LROUTER_RESOURCE,
|
||||
resource_id=lrouter_id),
|
||||
@ -381,8 +422,8 @@ def update_l2_gw_service(cluster, gateway_id, display_name):
|
||||
json.dumps(gwservice_obj), cluster=cluster)
|
||||
|
||||
|
||||
def update_lrouter(cluster, lrouter_id, display_name, nexthop):
|
||||
lrouter_obj = get_lrouter(cluster, lrouter_id)
|
||||
def update_implicit_routing_lrouter(cluster, r_id, display_name, nexthop):
|
||||
lrouter_obj = get_lrouter(cluster, r_id)
|
||||
if not display_name and not nexthop:
|
||||
# Nothing to update
|
||||
return lrouter_obj
|
||||
@ -395,11 +436,150 @@ def update_lrouter(cluster, lrouter_id, display_name, nexthop):
|
||||
if nh_element:
|
||||
nh_element["gateway_ip_address"] = nexthop
|
||||
return do_request(HTTP_PUT, _build_uri_path(LROUTER_RESOURCE,
|
||||
resource_id=lrouter_id),
|
||||
resource_id=r_id),
|
||||
json.dumps(lrouter_obj),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def get_explicit_routes_lrouter(cluster, router_id, protocol_type='static'):
|
||||
static_filter = {'protocol': protocol_type}
|
||||
existing_routes = do_request(
|
||||
HTTP_GET,
|
||||
_build_uri_path(LROUTERRIB_RESOURCE,
|
||||
filters=static_filter,
|
||||
fields="*",
|
||||
parent_resource_id=router_id),
|
||||
cluster=cluster)['results']
|
||||
return existing_routes
|
||||
|
||||
|
||||
def delete_explicit_route_lrouter(cluster, router_id, route_id):
|
||||
do_request(HTTP_DELETE,
|
||||
_build_uri_path(LROUTERRIB_RESOURCE,
|
||||
resource_id=route_id,
|
||||
parent_resource_id=router_id),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def create_explicit_route_lrouter(cluster, router_id, route):
|
||||
next_hop_ip = route.get("nexthop") or route.get("next_hop_ip")
|
||||
prefix = route.get("destination") or route.get("prefix")
|
||||
uuid = do_request(
|
||||
HTTP_POST,
|
||||
_build_uri_path(LROUTERRIB_RESOURCE,
|
||||
parent_resource_id=router_id),
|
||||
json.dumps({
|
||||
"action": "accept",
|
||||
"next_hop_ip": next_hop_ip,
|
||||
"prefix": prefix,
|
||||
"protocol": "static"
|
||||
}),
|
||||
cluster=cluster)['uuid']
|
||||
return uuid
|
||||
|
||||
|
||||
def update_explicit_routes_lrouter(cluster, router_id, routes):
|
||||
# Update in bulk: delete them all, and add the ones specified
|
||||
# but keep track of what is been modified to allow roll-backs
|
||||
# in case of failures
|
||||
nvp_routes = get_explicit_routes_lrouter(cluster, router_id)
|
||||
try:
|
||||
deleted_routes = []
|
||||
added_routes = []
|
||||
# omit the default route (0.0.0.0/0) from the processing;
|
||||
# this must be handled through the nexthop for the router
|
||||
for route in nvp_routes:
|
||||
prefix = route.get("destination") or route.get("prefix")
|
||||
if prefix != '0.0.0.0/0':
|
||||
delete_explicit_route_lrouter(cluster,
|
||||
router_id,
|
||||
route['uuid'])
|
||||
deleted_routes.append(route)
|
||||
for route in routes:
|
||||
prefix = route.get("destination") or route.get("prefix")
|
||||
if prefix != '0.0.0.0/0':
|
||||
uuid = create_explicit_route_lrouter(cluster,
|
||||
router_id, route)
|
||||
added_routes.append(uuid)
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_('Cannot update NVP routes %(routes)s for'
|
||||
'router %(router_id)s') % {'routes': routes,
|
||||
'router_id': router_id})
|
||||
# Roll back to keep NVP in consistent state
|
||||
with excutils.save_and_reraise_exception():
|
||||
if nvp_routes:
|
||||
if deleted_routes:
|
||||
for route in deleted_routes:
|
||||
create_explicit_route_lrouter(cluster,
|
||||
router_id, route)
|
||||
if added_routes:
|
||||
for route_id in added_routes:
|
||||
delete_explicit_route_lrouter(cluster,
|
||||
router_id, route_id)
|
||||
return nvp_routes
|
||||
|
||||
|
||||
@version_dependent
|
||||
def get_default_route_explicit_routing_lrouter(cluster, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def get_default_route_explicit_routing_lrouter_v33(cluster, router_id):
|
||||
static_filter = {"protocol": "static",
|
||||
"prefix": "0.0.0.0/0"}
|
||||
default_route = do_request(
|
||||
HTTP_GET,
|
||||
_build_uri_path(LROUTERRIB_RESOURCE,
|
||||
filters=static_filter,
|
||||
fields="*",
|
||||
parent_resource_id=router_id),
|
||||
cluster=cluster)["results"][0]
|
||||
return default_route
|
||||
|
||||
|
||||
def get_default_route_explicit_routing_lrouter_v32(cluster, router_id):
|
||||
# Scan all routes because 3.2 does not support query by prefix
|
||||
all_routes = get_explicit_routes_lrouter(cluster, router_id)
|
||||
for route in all_routes:
|
||||
if route['prefix'] == '0.0.0.0/0':
|
||||
return route
|
||||
|
||||
|
||||
def update_default_gw_explicit_routing_lrouter(cluster, router_id, next_hop):
|
||||
default_route = get_default_route_explicit_routing_lrouter(cluster,
|
||||
router_id)
|
||||
if next_hop != default_route["next_hop_ip"]:
|
||||
new_default_route = {"action": "accept",
|
||||
"next_hop_ip": next_hop,
|
||||
"prefix": "0.0.0.0/0",
|
||||
"protocol": "static"}
|
||||
do_request(HTTP_PUT,
|
||||
_build_uri_path(LROUTERRIB_RESOURCE,
|
||||
resource_id=default_route['uuid'],
|
||||
parent_resource_id=router_id),
|
||||
json.dumps(new_default_route),
|
||||
cluster=cluster)
|
||||
|
||||
|
||||
def update_explicit_routing_lrouter(cluster, router_id,
|
||||
display_name, next_hop, routes=None):
|
||||
update_implicit_routing_lrouter(cluster, router_id, display_name, next_hop)
|
||||
if next_hop:
|
||||
update_default_gw_explicit_routing_lrouter(cluster,
|
||||
router_id, next_hop)
|
||||
if routes:
|
||||
return update_explicit_routes_lrouter(cluster, router_id, routes)
|
||||
|
||||
|
||||
@version_dependent
|
||||
def update_lrouter(cluster, *args, **kwargs):
|
||||
if kwargs.get('routes', None):
|
||||
v = cluster.api_client.get_nvp_version()
|
||||
if (v.major < 3) or (v.major >= 3 and v.minor < 2):
|
||||
raise nvp_exc.NvpInvalidVersion(version=v)
|
||||
return v
|
||||
|
||||
|
||||
def delete_network(cluster, net_id, lswitch_id):
|
||||
delete_networks(cluster, net_id, [lswitch_id])
|
||||
|
||||
@ -1027,14 +1207,27 @@ def update_lrouter_port_ips(cluster, lrouter_id, lport_id,
|
||||
raise nvp_exc.NvpPluginException(err_msg=msg)
|
||||
|
||||
|
||||
# TODO(salvatore-orlando): Also handle changes in minor versions
|
||||
NVPLIB_FUNC_DICT = {
|
||||
'create_lrouter_dnat_rule': {2: create_lrouter_dnat_rule_v2,
|
||||
3: create_lrouter_dnat_rule_v3},
|
||||
'create_lrouter_snat_rule': {2: create_lrouter_snat_rule_v2,
|
||||
3: create_lrouter_snat_rule_v3},
|
||||
'create_lrouter_nosnat_rule': {2: create_lrouter_nosnat_rule_v2,
|
||||
3: create_lrouter_nosnat_rule_v3}
|
||||
'create_lrouter': {
|
||||
2: {'default': create_implicit_routing_lrouter, },
|
||||
3: {'default': create_implicit_routing_lrouter,
|
||||
2: create_explicit_routing_lrouter, }, },
|
||||
'update_lrouter': {
|
||||
2: {'default': update_implicit_routing_lrouter, },
|
||||
3: {'default': update_implicit_routing_lrouter,
|
||||
2: update_explicit_routing_lrouter, }, },
|
||||
'create_lrouter_dnat_rule': {
|
||||
2: {'default': create_lrouter_dnat_rule_v2, },
|
||||
3: {'default': create_lrouter_dnat_rule_v3, }, },
|
||||
'create_lrouter_snat_rule': {
|
||||
2: {'default': create_lrouter_snat_rule_v2, },
|
||||
3: {'default': create_lrouter_snat_rule_v3, }, },
|
||||
'create_lrouter_nosnat_rule': {
|
||||
2: {'default': create_lrouter_nosnat_rule_v2, },
|
||||
3: {'default': create_lrouter_nosnat_rule_v3, }, },
|
||||
'get_default_route_explicit_routing_lrouter': {
|
||||
3: {2: get_default_route_explicit_routing_lrouter_v32,
|
||||
3: get_default_route_explicit_routing_lrouter_v33, }, },
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@ from neutron import context
|
||||
from neutron.extensions import agent
|
||||
from neutron.openstack.common import log as logging
|
||||
import neutron.plugins.nicira as nvp_plugin
|
||||
from neutron.plugins.nicira.NvpApiClient import NVPVersion
|
||||
from neutron.tests.unit.nicira import fake_nvpapiclient
|
||||
from neutron.tests.unit import test_db_plugin
|
||||
|
||||
@ -84,7 +85,7 @@ class MacLearningDBTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
||||
return self.fc.fake_request(*args, **kwargs)
|
||||
|
||||
# Emulate tests against NVP 2.x
|
||||
instance.return_value.get_nvp_version.return_value = "2.999"
|
||||
instance.return_value.get_nvp_version.return_value = NVPVersion("3.0")
|
||||
instance.return_value.request.side_effect = _fake_request
|
||||
cfg.CONF.set_override('metadata_mode', None, 'NVP')
|
||||
self.addCleanup(self.fc.reset_all)
|
||||
|
@ -34,6 +34,7 @@ from neutron.plugins.nicira.extensions import nvp_networkgw
|
||||
from neutron.plugins.nicira.extensions import nvp_qos as ext_qos
|
||||
from neutron.plugins.nicira import NeutronPlugin
|
||||
from neutron.plugins.nicira import NvpApiClient
|
||||
from neutron.plugins.nicira.NvpApiClient import NVPVersion
|
||||
from neutron.plugins.nicira import nvplib
|
||||
from neutron.tests.unit.nicira import fake_nvpapiclient
|
||||
import neutron.tests.unit.nicira.test_networkgw as test_l2_gw
|
||||
@ -86,7 +87,7 @@ class NiciraPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
return self.fc.fake_request(*args, **kwargs)
|
||||
|
||||
# Emulate tests against NVP 2.x
|
||||
instance.return_value.get_nvp_version.return_value = "2.999"
|
||||
instance.return_value.get_nvp_version.return_value = NVPVersion("2.9")
|
||||
instance.return_value.request.side_effect = _fake_request
|
||||
super(NiciraPluginV2TestCase, self).setUp(self._plugin_name)
|
||||
cfg.CONF.set_override('metadata_mode', None, 'NVP')
|
||||
|
@ -44,6 +44,9 @@ class NvplibTestCase(base.BaseTestCase):
|
||||
% NICIRA_PKG_PATH, autospec=True)
|
||||
instance = self.mock_nvpapi.start()
|
||||
instance.return_value.login.return_value = "the_cookie"
|
||||
fake_version = getattr(self, 'fake_version', "2.9")
|
||||
instance.return_value.get_nvp_version.return_value = (
|
||||
NvpApiClient.NVPVersion(fake_version))
|
||||
|
||||
def _fake_request(*args, **kwargs):
|
||||
return self.fc.fake_request(*args, **kwargs)
|
||||
@ -69,35 +72,32 @@ class NvplibTestCase(base.BaseTestCase):
|
||||
|
||||
class TestNvplibNatRules(NvplibTestCase):
|
||||
|
||||
def _test_create_lrouter_dnat_rule(self, func):
|
||||
tenant_id = 'pippo'
|
||||
lrouter = nvplib.create_lrouter(self.fake_cluster,
|
||||
tenant_id,
|
||||
'fake_router',
|
||||
'192.168.0.1')
|
||||
nat_rule = func(self.fake_cluster, lrouter['uuid'], '10.0.0.99',
|
||||
match_criteria={'destination_ip_addresses':
|
||||
'192.168.0.5'})
|
||||
uri = nvplib._build_uri_path(nvplib.LROUTERNAT_RESOURCE,
|
||||
nat_rule['uuid'],
|
||||
lrouter['uuid'])
|
||||
return nvplib.do_request("GET", uri, cluster=self.fake_cluster)
|
||||
def _test_create_lrouter_dnat_rule(self, version):
|
||||
with mock.patch.object(self.fake_cluster.api_client,
|
||||
'get_nvp_version',
|
||||
new=lambda: NvpApiClient.NVPVersion(version)):
|
||||
tenant_id = 'pippo'
|
||||
lrouter = nvplib.create_lrouter(self.fake_cluster,
|
||||
tenant_id,
|
||||
'fake_router',
|
||||
'192.168.0.1')
|
||||
nat_rule = nvplib.create_lrouter_dnat_rule(
|
||||
self.fake_cluster, lrouter['uuid'], '10.0.0.99',
|
||||
match_criteria={'destination_ip_addresses':
|
||||
'192.168.0.5'})
|
||||
uri = nvplib._build_uri_path(nvplib.LROUTERNAT_RESOURCE,
|
||||
nat_rule['uuid'],
|
||||
lrouter['uuid'])
|
||||
resp_obj = nvplib.do_request("GET", uri, cluster=self.fake_cluster)
|
||||
self.assertEqual('DestinationNatRule', resp_obj['type'])
|
||||
self.assertEqual('192.168.0.5',
|
||||
resp_obj['match']['destination_ip_addresses'])
|
||||
|
||||
def test_create_lrouter_dnat_rule_v2(self):
|
||||
resp_obj = self._test_create_lrouter_dnat_rule(
|
||||
nvplib.create_lrouter_dnat_rule_v2)
|
||||
self.assertEqual('DestinationNatRule', resp_obj['type'])
|
||||
self.assertEqual('192.168.0.5',
|
||||
resp_obj['match']['destination_ip_addresses'])
|
||||
self._test_create_lrouter_dnat_rule('2.9')
|
||||
|
||||
def test_create_lrouter_dnat_rule_v3(self):
|
||||
resp_obj = self._test_create_lrouter_dnat_rule(
|
||||
nvplib.create_lrouter_dnat_rule_v2)
|
||||
# TODO(salvatore-orlando): Extend FakeNVPApiClient to deal with
|
||||
# different versions of NVP API
|
||||
self.assertEqual('DestinationNatRule', resp_obj['type'])
|
||||
self.assertEqual('192.168.0.5',
|
||||
resp_obj['match']['destination_ip_addresses'])
|
||||
def test_create_lrouter_dnat_rule_v31(self):
|
||||
self._test_create_lrouter_dnat_rule('3.1')
|
||||
|
||||
|
||||
class NvplibNegativeTests(base.BaseTestCase):
|
||||
@ -110,6 +110,10 @@ class NvplibNegativeTests(base.BaseTestCase):
|
||||
% NICIRA_PKG_PATH, autospec=True)
|
||||
instance = self.mock_nvpapi.start()
|
||||
instance.return_value.login.return_value = "the_cookie"
|
||||
# Choose 2.9, but the version is irrelevant for the aim of
|
||||
# these tests as calls are throwing up errors anyway
|
||||
self.fake_version = NvpApiClient.NVPVersion('2.9')
|
||||
instance.return_value.get_nvp_version.return_value = self.fake_version
|
||||
|
||||
def _faulty_request(*args, **kwargs):
|
||||
raise nvplib.NvpApiClient.NvpApiException
|
||||
@ -365,6 +369,187 @@ class TestNvplibLogicalSwitches(NvplibTestCase):
|
||||
self.fake_cluster, 'whatever', ['whatever'])
|
||||
|
||||
|
||||
class TestNvplibExplicitLRouters(NvplibTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.fake_version = '3.2'
|
||||
super(TestNvplibExplicitLRouters, self).setUp()
|
||||
|
||||
def _get_lrouter(self, tenant_id, router_name, router_id, relations=None):
|
||||
schema = '/ws.v1/schema/RoutingTableRoutingConfig'
|
||||
|
||||
router = {'display_name': router_name,
|
||||
'uuid': router_id,
|
||||
'tags': [{'scope': 'quantum', 'tag': '2013.1'},
|
||||
{'scope': 'os_tid', 'tag': '%s' % tenant_id}],
|
||||
'distributed': False,
|
||||
'routing_config': {'type': 'RoutingTableRoutingConfig',
|
||||
'_schema': schema},
|
||||
'_schema': schema,
|
||||
'nat_synchronization_enabled': True,
|
||||
'replication_mode': 'service',
|
||||
'type': 'LogicalRouterConfig',
|
||||
'_href': '/ws.v1/lrouter/%s' % router_id, }
|
||||
if relations:
|
||||
router['_relations'] = relations
|
||||
return router
|
||||
|
||||
def _get_single_route(self, router_id, route_id='fake_route_id_0',
|
||||
prefix='0.0.0.0/0', next_hop_ip='1.1.1.1'):
|
||||
return {'protocol': 'static',
|
||||
'_href': '/ws.v1/lrouter/%s/rib/%s' % (router_id, route_id),
|
||||
'prefix': prefix,
|
||||
'_schema': '/ws.v1/schema/RoutingTableEntry',
|
||||
'next_hop_ip': next_hop_ip,
|
||||
'action': 'accept',
|
||||
'uuid': route_id}
|
||||
|
||||
def test_prepare_body_with_implicit_routing_config(self):
|
||||
router_name = 'fake_router_name'
|
||||
tenant_id = 'fake_tenant_id'
|
||||
router_type = 'SingleDefaultRouteImplicitRoutingConfig'
|
||||
route_config = {
|
||||
'default_route_next_hop': {'gateway_ip_address': 'fake_address',
|
||||
'type': 'RouterNextHop'}, }
|
||||
body = nvplib._prepare_lrouter_body(router_name, tenant_id,
|
||||
router_type, **route_config)
|
||||
expected = {'display_name': 'fake_router_name',
|
||||
'routing_config': {
|
||||
'default_route_next_hop':
|
||||
{'gateway_ip_address': 'fake_address',
|
||||
'type': 'RouterNextHop'},
|
||||
'type': 'SingleDefaultRouteImplicitRoutingConfig'},
|
||||
'tags': [{'scope': 'os_tid', 'tag': 'fake_tenant_id'},
|
||||
{'scope': 'quantum', 'tag': '2013.1'}],
|
||||
'type': 'LogicalRouterConfig'}
|
||||
self.assertEqual(expected, body)
|
||||
|
||||
def test_prepare_body_without_routing_config(self):
|
||||
router_name = 'fake_router_name'
|
||||
tenant_id = 'fake_tenant_id'
|
||||
router_type = 'RoutingTableRoutingConfig'
|
||||
body = nvplib._prepare_lrouter_body(router_name, tenant_id,
|
||||
router_type)
|
||||
expected = {'display_name': 'fake_router_name',
|
||||
'routing_config': {'type': 'RoutingTableRoutingConfig'},
|
||||
'tags': [{'scope': 'os_tid', 'tag': 'fake_tenant_id'},
|
||||
{'scope': 'quantum', 'tag': '2013.1'}],
|
||||
'type': 'LogicalRouterConfig'}
|
||||
self.assertEqual(expected, body)
|
||||
|
||||
def test_get_lrouter(self):
|
||||
tenant_id = 'fake_tenant_id'
|
||||
router_name = 'fake_router_name'
|
||||
router_id = 'fake_router_id'
|
||||
relations = {
|
||||
'LogicalRouterStatus':
|
||||
{'_href': '/ws.v1/lrouter/%s/status' % router_id,
|
||||
'lport_admin_up_count': 1,
|
||||
'_schema': '/ws.v1/schema/LogicalRouterStatus',
|
||||
'lport_count': 1,
|
||||
'fabric_status': True,
|
||||
'type': 'LogicalRouterStatus',
|
||||
'lport_link_up_count': 0, }, }
|
||||
|
||||
with mock.patch(_nicira_method('do_request'),
|
||||
return_value=self._get_lrouter(tenant_id,
|
||||
router_name,
|
||||
router_id,
|
||||
relations)):
|
||||
lrouter = nvplib.get_lrouter(self.fake_cluster, router_id)
|
||||
self.assertTrue(
|
||||
lrouter['_relations']['LogicalRouterStatus']['fabric_status'])
|
||||
|
||||
def test_create_lrouter(self):
|
||||
tenant_id = 'fake_tenant_id'
|
||||
router_name = 'fake_router_name'
|
||||
router_id = 'fake_router_id'
|
||||
nexthop_ip = '10.0.0.1'
|
||||
with mock.patch(_nicira_method('do_request'),
|
||||
return_value=self._get_lrouter(tenant_id,
|
||||
router_name,
|
||||
router_id)):
|
||||
lrouter = nvplib.create_lrouter(self.fake_cluster, tenant_id,
|
||||
router_name, nexthop_ip)
|
||||
self.assertEqual(lrouter['routing_config']['type'],
|
||||
'RoutingTableRoutingConfig')
|
||||
self.assertNotIn('default_route_next_hop',
|
||||
lrouter['routing_config'])
|
||||
|
||||
def test_update_lrouter_nvp_with_no_routes(self):
|
||||
router_id = 'fake_router_id'
|
||||
new_routes = [{"nexthop": "10.0.0.2",
|
||||
"destination": "169.254.169.0/30"}, ]
|
||||
|
||||
nvp_routes = [self._get_single_route(router_id)]
|
||||
with mock.patch(_nicira_method('get_explicit_routes_lrouter'),
|
||||
return_value=nvp_routes):
|
||||
with mock.patch(_nicira_method('create_explicit_route_lrouter'),
|
||||
return_value='fake_uuid'):
|
||||
old_routes = nvplib.update_explicit_routes_lrouter(
|
||||
self.fake_cluster, router_id, new_routes)
|
||||
self.assertEqual(old_routes, nvp_routes)
|
||||
|
||||
def test_update_lrouter_nvp_with_no_routes_raise_nvp_exception(self):
|
||||
router_id = 'fake_router_id'
|
||||
new_routes = [{"nexthop": "10.0.0.2",
|
||||
"destination": "169.254.169.0/30"}, ]
|
||||
|
||||
nvp_routes = [self._get_single_route(router_id)]
|
||||
with mock.patch(_nicira_method('get_explicit_routes_lrouter'),
|
||||
return_value=nvp_routes):
|
||||
with mock.patch(_nicira_method('create_explicit_route_lrouter'),
|
||||
side_effect=NvpApiClient.NvpApiException):
|
||||
self.assertRaises(NvpApiClient.NvpApiException,
|
||||
nvplib.update_explicit_routes_lrouter,
|
||||
self.fake_cluster, router_id, new_routes)
|
||||
|
||||
def test_update_lrouter_with_routes(self):
|
||||
router_id = 'fake_router_id'
|
||||
new_routes = [{"next_hop_ip": "10.0.0.2",
|
||||
"prefix": "169.254.169.0/30"}, ]
|
||||
|
||||
nvp_routes = [self._get_single_route(router_id),
|
||||
self._get_single_route(router_id, 'fake_route_id_1',
|
||||
'0.0.0.1/24', '10.0.0.3'),
|
||||
self._get_single_route(router_id, 'fake_route_id_2',
|
||||
'0.0.0.2/24', '10.0.0.4'), ]
|
||||
|
||||
with mock.patch(_nicira_method('get_explicit_routes_lrouter'),
|
||||
return_value=nvp_routes):
|
||||
with mock.patch(_nicira_method('delete_explicit_route_lrouter'),
|
||||
return_value=None):
|
||||
with mock.patch(_nicira_method(
|
||||
'create_explicit_route_lrouter'),
|
||||
return_value='fake_uuid'):
|
||||
old_routes = nvplib.update_explicit_routes_lrouter(
|
||||
self.fake_cluster, router_id, new_routes)
|
||||
self.assertEqual(old_routes, nvp_routes)
|
||||
|
||||
def test_update_lrouter_with_routes_raises_nvp_expception(self):
|
||||
router_id = 'fake_router_id'
|
||||
new_routes = [{"nexthop": "10.0.0.2",
|
||||
"destination": "169.254.169.0/30"}, ]
|
||||
|
||||
nvp_routes = [self._get_single_route(router_id),
|
||||
self._get_single_route(router_id, 'fake_route_id_1',
|
||||
'0.0.0.1/24', '10.0.0.3'),
|
||||
self._get_single_route(router_id, 'fake_route_id_2',
|
||||
'0.0.0.2/24', '10.0.0.4'), ]
|
||||
|
||||
with mock.patch(_nicira_method('get_explicit_routes_lrouter'),
|
||||
return_value=nvp_routes):
|
||||
with mock.patch(_nicira_method('delete_explicit_route_lrouter'),
|
||||
side_effect=NvpApiClient.NvpApiException):
|
||||
with mock.patch(
|
||||
_nicira_method('create_explicit_route_lrouter'),
|
||||
return_value='fake_uuid'):
|
||||
self.assertRaises(
|
||||
NvpApiClient.NvpApiException,
|
||||
nvplib.update_explicit_routes_lrouter,
|
||||
self.fake_cluster, router_id, new_routes)
|
||||
|
||||
|
||||
class TestNvplibLogicalRouters(NvplibTestCase):
|
||||
|
||||
def _verify_lrouter(self, res_lrouter,
|
||||
@ -733,7 +918,7 @@ class TestNvplibLogicalRouters(NvplibTestCase):
|
||||
'10.0.0.1')
|
||||
with mock.patch.object(self.fake_cluster.api_client,
|
||||
'get_nvp_version',
|
||||
new=lambda: version):
|
||||
new=lambda: NvpApiClient.NVPVersion(version)):
|
||||
nvplib.create_lrouter_snat_rule(
|
||||
self.fake_cluster, lrouter['uuid'],
|
||||
'10.0.0.2', '10.0.0.2', order=200,
|
||||
@ -754,7 +939,7 @@ class TestNvplibLogicalRouters(NvplibTestCase):
|
||||
'10.0.0.1')
|
||||
with mock.patch.object(self.fake_cluster.api_client,
|
||||
'get_nvp_version',
|
||||
return_value=version):
|
||||
return_value=NvpApiClient.NVPVersion(version)):
|
||||
nvplib.create_lrouter_dnat_rule(
|
||||
self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200,
|
||||
dest_port=dest_port,
|
||||
@ -797,7 +982,7 @@ class TestNvplibLogicalRouters(NvplibTestCase):
|
||||
'10.0.0.1')
|
||||
with mock.patch.object(self.fake_cluster.api_client,
|
||||
'get_nvp_version',
|
||||
new=lambda: version):
|
||||
new=lambda: NvpApiClient.NVPVersion(version)):
|
||||
nvplib.create_lrouter_nosnat_rule(
|
||||
self.fake_cluster, lrouter['uuid'],
|
||||
order=100,
|
||||
@ -820,7 +1005,7 @@ class TestNvplibLogicalRouters(NvplibTestCase):
|
||||
# v2 or v3 makes no difference for this test
|
||||
with mock.patch.object(self.fake_cluster.api_client,
|
||||
'get_nvp_version',
|
||||
new=lambda: '2.0'):
|
||||
new=lambda: NvpApiClient.NVPVersion('2.0')):
|
||||
nvplib.create_lrouter_snat_rule(
|
||||
self.fake_cluster, lrouter['uuid'],
|
||||
'10.0.0.2', '10.0.0.2', order=220,
|
||||
@ -1164,3 +1349,7 @@ class TestNvplibClusterVersion(NvplibTestCase):
|
||||
with mock.patch.object(nvplib, 'do_request', new=fakedorequest):
|
||||
version = nvplib.get_cluster_version('whatever')
|
||||
self.assertIsNone(version)
|
||||
|
||||
|
||||
def _nicira_method(method_name, module_name='nvplib'):
|
||||
return '%s.%s.%s' % ('neutron.plugins.nicira', module_name, method_name)
|
||||
|
Loading…
Reference in New Issue
Block a user