1
# Copyright 2014 VMware, Inc.
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5
# not use this file except in compliance with the License. You may obtain
6
# a copy of the License at
8
# http://www.apache.org/licenses/LICENSE-2.0
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
# License for the specific language governing permissions and limitations
16
from oslo.config import cfg
17
from oslo.serialization import jsonutils
18
from oslo.utils import excutils
20
from neutron.common import exceptions as exception
21
from neutron.i18n import _LE, _LI, _LW
22
from neutron.openstack.common import log
23
from neutron.plugins.vmware.api_client import exception as api_exc
24
from neutron.plugins.vmware.common import exceptions as nsx_exc
25
from neutron.plugins.vmware.common import utils
26
from neutron.plugins.vmware import nsxlib
27
from neutron.plugins.vmware.nsxlib import switch
28
from neutron.plugins.vmware.nsxlib import versioning
30
# @versioning.versioned decorator makes the apparent function body
31
# totally unrelated to the real function. This confuses pylint :(
32
# pylint: disable=assignment-from-no-return
36
HTTP_DELETE = "DELETE"
39
LROUTER_RESOURCE = "lrouter"
40
LROUTER_RESOURCE = "lrouter"
41
LROUTERPORT_RESOURCE = "lport/%s" % LROUTER_RESOURCE
42
LROUTERRIB_RESOURCE = "rib/%s" % LROUTER_RESOURCE
43
LROUTERNAT_RESOURCE = "nat/lrouter"
44
# Constants for NAT rules
45
MATCH_KEYS = ["destination_ip_addresses", "destination_port_max",
46
"destination_port_min", "source_ip_addresses",
47
"source_port_max", "source_port_min", "protocol"]
49
LOG = log.getLogger(__name__)
52
def _prepare_lrouter_body(name, neutron_router_id, tenant_id,
53
router_type, distributed=None, **kwargs):
55
"display_name": utils.check_and_truncate(name),
56
"tags": utils.get_tags(os_tid=tenant_id,
57
q_router_id=neutron_router_id),
61
"type": "LogicalRouterConfig",
62
"replication_mode": cfg.CONF.NSX.replication_mode,
64
# add the distributed key only if not None (ie: True or False)
65
if distributed is not None:
66
body['distributed'] = distributed
68
body["routing_config"].update(kwargs)
72
def _create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id,
73
display_name, nexthop, distributed=None):
74
implicit_routing_config = {
75
"default_route_next_hop": {
76
"gateway_ip_address": nexthop,
77
"type": "RouterNextHop"
80
lrouter_obj = _prepare_lrouter_body(
81
display_name, neutron_router_id, tenant_id,
82
"SingleDefaultRouteImplicitRoutingConfig",
83
distributed=distributed,
84
**implicit_routing_config)
85
return nsxlib.do_request(HTTP_POST,
86
nsxlib._build_uri_path(LROUTER_RESOURCE),
87
jsonutils.dumps(lrouter_obj), cluster=cluster)
90
def create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id,
91
display_name, nexthop):
92
"""Create a NSX logical router on the specified cluster.
94
:param cluster: The target NSX cluster
95
:param tenant_id: Identifier of the Openstack tenant for which
96
the logical router is being created
97
:param display_name: Descriptive name of this logical router
98
:param nexthop: External gateway IP address for the logical router
99
:raise NsxApiException: if there is a problem while communicating
100
with the NSX controller
102
return _create_implicit_routing_lrouter(
103
cluster, neutron_router_id, tenant_id, display_name, nexthop)
106
def create_implicit_routing_lrouter_with_distribution(
107
cluster, neutron_router_id, tenant_id, display_name,
108
nexthop, distributed=None):
109
"""Create a NSX logical router on the specified cluster.
111
This function also allows for creating distributed lrouters
112
:param cluster: The target NSX cluster
113
:param tenant_id: Identifier of the Openstack tenant for which
114
the logical router is being created
115
:param display_name: Descriptive name of this logical router
116
:param nexthop: External gateway IP address for the logical router
117
:param distributed: True for distributed logical routers
118
:raise NsxApiException: if there is a problem while communicating
119
with the NSX controller
121
return _create_implicit_routing_lrouter(
122
cluster, neutron_router_id, tenant_id,
123
display_name, nexthop, distributed)
126
def create_explicit_routing_lrouter(cluster, neutron_router_id, tenant_id,
127
display_name, nexthop, distributed=None):
128
lrouter_obj = _prepare_lrouter_body(
129
display_name, neutron_router_id, tenant_id,
130
"RoutingTableRoutingConfig", distributed=distributed)
131
router = nsxlib.do_request(HTTP_POST,
132
nsxlib._build_uri_path(LROUTER_RESOURCE),
133
jsonutils.dumps(lrouter_obj), cluster=cluster)
134
default_gw = {'prefix': '0.0.0.0/0', 'next_hop_ip': nexthop}
135
create_explicit_route_lrouter(cluster, router['uuid'], default_gw)
139
def delete_lrouter(cluster, lrouter_id):
140
nsxlib.do_request(HTTP_DELETE,
141
nsxlib._build_uri_path(LROUTER_RESOURCE,
142
resource_id=lrouter_id),
146
def get_lrouter(cluster, lrouter_id):
147
return nsxlib.do_request(HTTP_GET,
148
nsxlib._build_uri_path(
150
resource_id=lrouter_id,
151
relations='LogicalRouterStatus'),
155
def query_lrouters(cluster, fields=None, filters=None):
156
return nsxlib.get_all_query_pages(
157
nsxlib._build_uri_path(LROUTER_RESOURCE,
159
relations='LogicalRouterStatus',
164
def get_lrouters(cluster, tenant_id, fields=None, filters=None):
165
# FIXME(salv-orlando): Fields parameter is ignored in this routine
168
actual_filters.update(filters)
170
actual_filters['tag'] = tenant_id
171
actual_filters['tag_scope'] = 'os_tid'
172
lrouter_fields = "uuid,display_name,fabric_status,tags"
173
return query_lrouters(cluster, lrouter_fields, actual_filters)
176
def update_implicit_routing_lrouter(cluster, r_id, display_name, nexthop):
177
lrouter_obj = get_lrouter(cluster, r_id)
178
if not display_name and not nexthop:
181
# It seems that this is faster than the doing an if on display_name
182
lrouter_obj["display_name"] = (utils.check_and_truncate(display_name) or
183
lrouter_obj["display_name"])
185
nh_element = lrouter_obj["routing_config"].get(
186
"default_route_next_hop")
188
nh_element["gateway_ip_address"] = nexthop
189
return nsxlib.do_request(HTTP_PUT,
190
nsxlib._build_uri_path(LROUTER_RESOURCE,
192
jsonutils.dumps(lrouter_obj),
196
def get_explicit_routes_lrouter(cluster, router_id, protocol_type='static'):
197
static_filter = {'protocol': protocol_type}
198
existing_routes = nsxlib.do_request(
200
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
201
filters=static_filter,
203
parent_resource_id=router_id),
204
cluster=cluster)['results']
205
return existing_routes
208
def delete_explicit_route_lrouter(cluster, router_id, route_id):
209
nsxlib.do_request(HTTP_DELETE,
210
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
211
resource_id=route_id,
212
parent_resource_id=router_id),
216
def create_explicit_route_lrouter(cluster, router_id, route):
217
next_hop_ip = route.get("nexthop") or route.get("next_hop_ip")
218
prefix = route.get("destination") or route.get("prefix")
219
uuid = nsxlib.do_request(
221
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
222
parent_resource_id=router_id),
225
"next_hop_ip": next_hop_ip,
229
cluster=cluster)['uuid']
233
def update_explicit_routes_lrouter(cluster, router_id, routes):
234
# Update in bulk: delete them all, and add the ones specified
235
# but keep track of what is been modified to allow roll-backs
236
# in case of failures
237
nsx_routes = get_explicit_routes_lrouter(cluster, router_id)
241
# omit the default route (0.0.0.0/0) from the processing;
242
# this must be handled through the nexthop for the router
243
for route in nsx_routes:
244
prefix = route.get("destination") or route.get("prefix")
245
if prefix != '0.0.0.0/0':
246
delete_explicit_route_lrouter(cluster,
249
deleted_routes.append(route)
251
prefix = route.get("destination") or route.get("prefix")
252
if prefix != '0.0.0.0/0':
253
uuid = create_explicit_route_lrouter(cluster,
255
added_routes.append(uuid)
256
except api_exc.NsxApiException:
257
LOG.exception(_LE('Cannot update NSX routes %(routes)s for '
258
'router %(router_id)s'),
259
{'routes': routes, 'router_id': router_id})
260
# Roll back to keep NSX in consistent state
261
with excutils.save_and_reraise_exception():
264
for route in deleted_routes:
265
create_explicit_route_lrouter(cluster,
268
for route_id in added_routes:
269
delete_explicit_route_lrouter(cluster,
274
def get_default_route_explicit_routing_lrouter_v33(cluster, router_id):
275
static_filter = {"protocol": "static",
276
"prefix": "0.0.0.0/0"}
277
default_route = nsxlib.do_request(
279
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
280
filters=static_filter,
282
parent_resource_id=router_id),
283
cluster=cluster)["results"][0]
287
def get_default_route_explicit_routing_lrouter_v32(cluster, router_id):
288
# Scan all routes because 3.2 does not support query by prefix
289
all_routes = get_explicit_routes_lrouter(cluster, router_id)
290
for route in all_routes:
291
if route['prefix'] == '0.0.0.0/0':
295
def update_default_gw_explicit_routing_lrouter(cluster, router_id, next_hop):
296
default_route = get_default_route_explicit_routing_lrouter(cluster,
298
if next_hop != default_route["next_hop_ip"]:
299
new_default_route = {"action": "accept",
300
"next_hop_ip": next_hop,
301
"prefix": "0.0.0.0/0",
302
"protocol": "static"}
303
nsxlib.do_request(HTTP_PUT,
304
nsxlib._build_uri_path(
306
resource_id=default_route['uuid'],
307
parent_resource_id=router_id),
308
jsonutils.dumps(new_default_route),
312
def update_explicit_routing_lrouter(cluster, router_id,
313
display_name, next_hop, routes=None):
314
update_implicit_routing_lrouter(cluster, router_id, display_name, next_hop)
316
update_default_gw_explicit_routing_lrouter(cluster,
318
if routes is not None:
319
return update_explicit_routes_lrouter(cluster, router_id, routes)
322
def query_lrouter_lports(cluster, lr_uuid, fields="*",
323
filters=None, relations=None):
324
uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE,
325
parent_resource_id=lr_uuid,
326
fields=fields, filters=filters,
328
return nsxlib.do_request(HTTP_GET, uri, cluster=cluster)['results']
331
def create_router_lport(cluster, lrouter_uuid, tenant_id, neutron_port_id,
332
display_name, admin_status_enabled, ip_addresses,
334
"""Creates a logical port on the assigned logical router."""
336
admin_status_enabled=admin_status_enabled,
337
display_name=display_name,
338
tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id),
339
ip_addresses=ip_addresses,
340
type="LogicalRouterPortConfig"
342
# Only add the mac_address to lport_obj if present. This is because
343
# when creating the fake_ext_gw there is no mac_address present.
345
lport_obj['mac_address'] = mac_address
346
path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE,
347
parent_resource_id=lrouter_uuid)
348
result = nsxlib.do_request(HTTP_POST, path, jsonutils.dumps(lport_obj),
351
LOG.debug("Created logical port %(lport_uuid)s on "
352
"logical router %(lrouter_uuid)s",
353
{'lport_uuid': result['uuid'],
354
'lrouter_uuid': lrouter_uuid})
358
def update_router_lport(cluster, lrouter_uuid, lrouter_port_uuid,
359
tenant_id, neutron_port_id, display_name,
360
admin_status_enabled, ip_addresses):
361
"""Updates a logical port on the assigned logical router."""
363
admin_status_enabled=admin_status_enabled,
364
display_name=display_name,
365
tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id),
366
ip_addresses=ip_addresses,
367
type="LogicalRouterPortConfig"
369
# Do not pass null items to NSX
370
for key in lport_obj.keys():
371
if lport_obj[key] is None:
373
path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE,
375
parent_resource_id=lrouter_uuid)
376
result = nsxlib.do_request(HTTP_PUT, path,
377
jsonutils.dumps(lport_obj),
379
LOG.debug("Updated logical port %(lport_uuid)s on "
380
"logical router %(lrouter_uuid)s",
381
{'lport_uuid': lrouter_port_uuid, 'lrouter_uuid': lrouter_uuid})
385
def delete_router_lport(cluster, lrouter_uuid, lport_uuid):
386
"""Creates a logical port on the assigned logical router."""
387
path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lport_uuid,
389
nsxlib.do_request(HTTP_DELETE, path, cluster=cluster)
390
LOG.debug("Delete logical router port %(lport_uuid)s on "
391
"logical router %(lrouter_uuid)s",
392
{'lport_uuid': lport_uuid,
393
'lrouter_uuid': lrouter_uuid})
396
def delete_peer_router_lport(cluster, lr_uuid, ls_uuid, lp_uuid):
397
nsx_port = switch.get_port(cluster, ls_uuid, lp_uuid,
398
relations="LogicalPortAttachment")
399
relations = nsx_port.get('_relations')
401
att_data = relations.get('LogicalPortAttachment')
403
lrp_uuid = att_data.get('peer_port_uuid')
405
delete_router_lport(cluster, lr_uuid, lrp_uuid)
408
def find_router_gw_port(context, cluster, router_id):
409
"""Retrieves the external gateway port for a NSX logical router."""
411
# Find the uuid of nsx ext gw logical router port
412
# TODO(salvatore-orlando): Consider storing it in Neutron DB
413
results = query_lrouter_lports(
415
relations="LogicalPortAttachment")
416
for lport in results:
417
if '_relations' in lport:
418
attachment = lport['_relations'].get('LogicalPortAttachment')
419
if attachment and attachment.get('type') == 'L3GatewayAttachment':
423
def plug_router_port_attachment(cluster, router_id, port_id,
424
attachment_uuid, nsx_attachment_type,
425
attachment_vlan=None):
426
"""Attach a router port to the given attachment.
428
Current attachment types:
429
- PatchAttachment [-> logical switch port uuid]
430
- L3GatewayAttachment [-> L3GatewayService uuid]
431
For the latter attachment type a VLAN ID can be specified as well.
433
uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, port_id, router_id,
436
attach_obj["type"] = nsx_attachment_type
437
if nsx_attachment_type == "PatchAttachment":
438
attach_obj["peer_port_uuid"] = attachment_uuid
439
elif nsx_attachment_type == "L3GatewayAttachment":
440
attach_obj["l3_gateway_service_uuid"] = attachment_uuid
442
attach_obj['vlan_id'] = attachment_vlan
444
raise nsx_exc.InvalidAttachmentType(
445
attachment_type=nsx_attachment_type)
446
return nsxlib.do_request(
447
HTTP_PUT, uri, jsonutils.dumps(attach_obj), cluster=cluster)
450
def _create_nat_match_obj(**kwargs):
451
nat_match_obj = {'ethertype': 'IPv4'}
452
delta = set(kwargs.keys()) - set(MATCH_KEYS)
454
raise Exception(_("Invalid keys for NAT match: %s"), delta)
455
nat_match_obj.update(kwargs)
459
def _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj):
460
LOG.debug("Creating NAT rule: %s", nat_rule_obj)
461
uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE,
462
parent_resource_id=router_id)
463
return nsxlib.do_request(HTTP_POST, uri, jsonutils.dumps(nat_rule_obj),
467
def _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj):
468
return {"to_source_ip_address_min": min_src_ip,
469
"to_source_ip_address_max": max_src_ip,
470
"type": "SourceNatRule",
471
"match": nat_match_obj}
474
def create_lrouter_nosnat_rule_v2(cluster, _router_id, _match_criteria=None):
475
LOG.info(_LI("No SNAT rules cannot be applied as they are not available "
476
"in this version of the NSX platform"))
479
def create_lrouter_nodnat_rule_v2(cluster, _router_id, _match_criteria=None):
480
LOG.info(_LI("No DNAT rules cannot be applied as they are not available "
481
"in this version of the NSX platform"))
484
def create_lrouter_snat_rule_v2(cluster, router_id,
485
min_src_ip, max_src_ip, match_criteria=None):
487
nat_match_obj = _create_nat_match_obj(**match_criteria)
488
nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj)
489
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
492
def create_lrouter_dnat_rule_v2(cluster, router_id, dst_ip,
493
to_dst_port=None, match_criteria=None):
495
nat_match_obj = _create_nat_match_obj(**match_criteria)
497
"to_destination_ip_address_min": dst_ip,
498
"to_destination_ip_address_max": dst_ip,
499
"type": "DestinationNatRule",
500
"match": nat_match_obj
503
nat_rule_obj['to_destination_port'] = to_dst_port
504
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
507
def create_lrouter_nosnat_rule_v3(cluster, router_id, order=None,
508
match_criteria=None):
509
nat_match_obj = _create_nat_match_obj(**match_criteria)
511
"type": "NoSourceNatRule",
512
"match": nat_match_obj
515
nat_rule_obj['order'] = order
516
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
519
def create_lrouter_nodnat_rule_v3(cluster, router_id, order=None,
520
match_criteria=None):
521
nat_match_obj = _create_nat_match_obj(**match_criteria)
523
"type": "NoDestinationNatRule",
524
"match": nat_match_obj
527
nat_rule_obj['order'] = order
528
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
531
def create_lrouter_snat_rule_v3(cluster, router_id, min_src_ip, max_src_ip,
532
order=None, match_criteria=None):
533
nat_match_obj = _create_nat_match_obj(**match_criteria)
534
nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj)
536
nat_rule_obj['order'] = order
537
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
540
def create_lrouter_dnat_rule_v3(cluster, router_id, dst_ip, to_dst_port=None,
541
order=None, match_criteria=None):
543
nat_match_obj = _create_nat_match_obj(**match_criteria)
545
"to_destination_ip_address": dst_ip,
546
"type": "DestinationNatRule",
547
"match": nat_match_obj
550
nat_rule_obj['to_destination_port'] = to_dst_port
552
nat_rule_obj['order'] = order
553
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
556
def delete_nat_rules_by_match(cluster, router_id, rule_type,
559
raise_on_len_mismatch=True,
562
nat_rules = query_nat_rules(cluster, router_id)
565
if (r['type'] != rule_type):
568
for key, value in kwargs.iteritems():
569
if not (key in r['match'] and r['match'][key] == value):
572
to_delete_ids.append(r['uuid'])
573
num_rules_to_delete = len(to_delete_ids)
574
if (num_rules_to_delete < min_num_expected or
575
num_rules_to_delete > max_num_expected):
576
if raise_on_len_mismatch:
577
raise nsx_exc.NatRuleMismatch(actual_rules=num_rules_to_delete,
578
min_rules=min_num_expected,
579
max_rules=max_num_expected)
581
LOG.warn(_LW("Found %(actual_rule_num)d matching NAT rules, which "
582
"is not in the expected range (%(min_exp_rule_num)d,"
583
"%(max_exp_rule_num)d)"),
584
{'actual_rule_num': num_rules_to_delete,
585
'min_exp_rule_num': min_num_expected,
586
'max_exp_rule_num': max_num_expected})
588
for rule_id in to_delete_ids:
589
delete_router_nat_rule(cluster, router_id, rule_id)
590
# Return number of deleted rules - useful at least for
592
return num_rules_to_delete
595
def delete_router_nat_rule(cluster, router_id, rule_id):
596
uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id)
597
nsxlib.do_request(HTTP_DELETE, uri, cluster=cluster)
600
def query_nat_rules(cluster, router_id, fields="*", filters=None):
601
uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE,
602
parent_resource_id=router_id,
603
fields=fields, filters=filters)
604
return nsxlib.get_all_query_pages(uri, cluster)
607
# NOTE(salvatore-orlando): The following FIXME applies in general to
608
# each operation on list attributes.
609
# FIXME(salvatore-orlando): need a lock around the list of IPs on an iface
610
def update_lrouter_port_ips(cluster, lrouter_id, lport_id,
611
ips_to_add, ips_to_remove):
612
uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id)
614
port = nsxlib.do_request(HTTP_GET, uri, cluster=cluster)
615
# TODO(salvatore-orlando): Enforce ips_to_add intersection with
616
# ips_to_remove is empty
617
ip_address_set = set(port['ip_addresses'])
618
ip_address_set = ip_address_set - set(ips_to_remove)
619
ip_address_set = ip_address_set | set(ips_to_add)
620
# Set is not JSON serializable - convert to list
621
port['ip_addresses'] = list(ip_address_set)
622
nsxlib.do_request(HTTP_PUT, uri, jsonutils.dumps(port),
624
except exception.NotFound:
625
# FIXME(salv-orlando):avoid raising different exception
626
data = {'lport_id': lport_id, 'lrouter_id': lrouter_id}
627
msg = (_("Router Port %(lport_id)s not found on router "
628
"%(lrouter_id)s") % data)
630
raise nsx_exc.NsxPluginException(err_msg=msg)
631
except api_exc.NsxApiException as e:
632
msg = _("An exception occurred while updating IP addresses on a "
633
"router logical port:%s") % e
635
raise nsx_exc.NsxPluginException(err_msg=msg)
640
2: {versioning.DEFAULT_VERSION: create_implicit_routing_lrouter, },
641
3: {versioning.DEFAULT_VERSION: create_implicit_routing_lrouter,
642
1: create_implicit_routing_lrouter_with_distribution,
643
2: create_explicit_routing_lrouter, }, },
645
2: {versioning.DEFAULT_VERSION: update_implicit_routing_lrouter, },
646
3: {versioning.DEFAULT_VERSION: update_implicit_routing_lrouter,
647
2: update_explicit_routing_lrouter, }, },
648
'create_lrouter_dnat_rule': {
649
2: {versioning.DEFAULT_VERSION: create_lrouter_dnat_rule_v2, },
650
3: {versioning.DEFAULT_VERSION: create_lrouter_dnat_rule_v3, }, },
651
'create_lrouter_snat_rule': {
652
2: {versioning.DEFAULT_VERSION: create_lrouter_snat_rule_v2, },
653
3: {versioning.DEFAULT_VERSION: create_lrouter_snat_rule_v3, }, },
654
'create_lrouter_nosnat_rule': {
655
2: {versioning.DEFAULT_VERSION: create_lrouter_nosnat_rule_v2, },
656
3: {versioning.DEFAULT_VERSION: create_lrouter_nosnat_rule_v3, }, },
657
'create_lrouter_nodnat_rule': {
658
2: {versioning.DEFAULT_VERSION: create_lrouter_nodnat_rule_v2, },
659
3: {versioning.DEFAULT_VERSION: create_lrouter_nodnat_rule_v3, }, },
660
'get_default_route_explicit_routing_lrouter': {
661
3: {versioning.DEFAULT_VERSION:
662
get_default_route_explicit_routing_lrouter_v32,
663
2: get_default_route_explicit_routing_lrouter_v32, }, },
667
@versioning.versioned(ROUTER_FUNC_DICT)
668
def create_lrouter(cluster, *args, **kwargs):
669
if kwargs.get('distributed', None):
670
v = cluster.api_client.get_version()
671
if (v.major, v.minor) < (3, 1):
672
raise nsx_exc.InvalidVersion(version=v)
676
@versioning.versioned(ROUTER_FUNC_DICT)
677
def get_default_route_explicit_routing_lrouter(cluster, *args, **kwargs):
681
@versioning.versioned(ROUTER_FUNC_DICT)
682
def update_lrouter(cluster, *args, **kwargs):
683
if kwargs.get('routes', None):
684
v = cluster.api_client.get_version()
685
if (v.major, v.minor) < (3, 2):
686
raise nsx_exc.InvalidVersion(version=v)
690
@versioning.versioned(ROUTER_FUNC_DICT)
691
def create_lrouter_dnat_rule(cluster, *args, **kwargs):
695
@versioning.versioned(ROUTER_FUNC_DICT)
696
def create_lrouter_snat_rule(cluster, *args, **kwargs):
700
@versioning.versioned(ROUTER_FUNC_DICT)
701
def create_lrouter_nosnat_rule(cluster, *args, **kwargs):
705
@versioning.versioned(ROUTER_FUNC_DICT)
706
def create_lrouter_nodnat_rule(cluster, *args, **kwargs):