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
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,
functionality using NVP.
"""
- supported_extension_aliases = ["mac-learning",
+ supported_extension_aliases = ["extraroute",
+ "mac-learning",
"network-gateway",
"nvp-qos",
"port-security",
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
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):
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.
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):
# 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})
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")
# 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)
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
_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())
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
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
: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),
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
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])
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, }, },
}
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
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)
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
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')
% 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)
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'])
-
- 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'])
+ self._test_create_lrouter_dnat_rule('2.9')
+
+ def test_create_lrouter_dnat_rule_v31(self):
+ self._test_create_lrouter_dnat_rule('3.1')
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
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,
'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,
'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,
'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,
# 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,
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)