Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add the support for the optional route source parameter in nm provider #714

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@
- `macvlan`
- `infiniband`
- `wireless`
- `dummy`

Check warning on line 265 in README.md

View workflow job for this annotation

GitHub Actions / Detect non-inclusive language

`dummy` may be insensitive, use `placeholder`, `sample` instead

#### `type: ethernet`

Expand Down Expand Up @@ -365,9 +365,9 @@

- `password`: password for the network (required if `wpa-psk` or `sae` is used)

#### `type: dummy`

Check warning on line 368 in README.md

View workflow job for this annotation

GitHub Actions / Detect non-inclusive language

`dummy` may be insensitive, use `placeholder`, `sample` instead

Dummy network interface, `nm` (NetworkManager) is the only supported `network_provider`

Check warning on line 370 in README.md

View workflow job for this annotation

GitHub Actions / Detect non-inclusive language

`Dummy` may be insensitive, use `placeholder`, `sample` instead
for this type.

### `autoconnect`
Expand Down Expand Up @@ -596,10 +596,11 @@

Static route configuration can be specified via a list of routes given in the
`route` option. The default value is an empty list. Each route is a dictionary with
the following entries: `gateway`, `metric`, `network`, `prefix`, `table` and `type`.
`network` and `prefix` specify the destination network. `table` supports both the
numeric table and named table. In order to specify the named table, the users have to
ensure the named table is properly defined in `/etc/iproute2/rt_tables` or
the following entries: `gateway`, `metric`, `network`, `prefix`, `src`, `table` and
`type`. `network` and `prefix` specify the destination network. `src` specifies the
source IP address for a route. `table` supports both the numeric table and named
table. In order to specify the named table, the users have to ensure the named table
is properly defined in `/etc/iproute2/rt_tables` or
`/etc/iproute2/rt_tables.d/*.conf`. The optional `type` key supports the values
`blackhole`, `prohibit`, and `unreachable`.
See [man 8 ip-route](https://man7.org/linux/man-pages/man8/ip-route.8.html#DESCRIPTION)
Expand Down
4 changes: 4 additions & 0 deletions library/network_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,10 @@
NM.IPRoute.set_attribute(
new_route, "table", Util.GLib().Variant.new_uint32(r["table"])
)
if r["src"]:
NM.IPRoute.set_attribute(

Check warning on line 1267 in library/network_connections.py

View check run for this annotation

Codecov / codecov/patch

library/network_connections.py#L1266-L1267

Added lines #L1266 - L1267 were not covered by tests
new_route, "src", Util.GLib().Variant.new_string(r["src"])
)

if r["family"] == socket.AF_INET:
s_ip4.add_route(new_route)
Expand Down
19 changes: 19 additions & 0 deletions module_utils/network_lsr/argument_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,9 @@
enum_values=["blackhole", "prohibit", "unreachable"],
),
ArgValidatorRouteTable("table"),
ArgValidatorIP(
"src", family=family, default_value=None, plain_address=False
),
],
default_value=None,
)
Expand Down Expand Up @@ -716,6 +719,16 @@
elif not Util.addr_family_valid_prefix(family, prefix):
raise ValidationError(name, "invalid prefix %s in '%s'" % (prefix, value))

src = result["src"]
if src is not None:
if family != src["family"]:
raise ValidationError(
name,
"conflicting address family between network and src "
"address {0}".format(src["address"]),
)
result["src"] = src["address"]

return result


Expand Down Expand Up @@ -2627,6 +2640,12 @@
idx,
"type is not supported by initscripts",
)
if route["src"] is not None:
raise ValidationError.from_connection(

Check warning on line 2644 in module_utils/network_lsr/argument_validator.py

View check run for this annotation

Codecov / codecov/patch

module_utils/network_lsr/argument_validator.py#L2644

Added line #L2644 was not covered by tests
idx,
"configuring the route source is not supported by initscripts",
)

if connection["ip"]["routing_rule"]:
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
raise ValidationError.from_connection(
Expand Down
18 changes: 18 additions & 0 deletions tests/playbooks/tests_route_table.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
gateway: 198.51.100.6
metric: 4
table: 30200
- network: 192.0.2.64
prefix: 26
gateway: 198.51.100.8
metric: 50
table: 30200
src: 198.51.100.3

- name: Get the routes from the route table 30200
command: ip route show table 30200
Expand All @@ -65,6 +71,9 @@
that:
- route_table_30200.stdout is search("198.51.100.64/26 via
198.51.100.6 dev ethtest0 proto static metric 4")
- route_table_30200.stdout is search("192.0.2.64/26 via
198.51.100.8 dev ethtest0 proto static src 198.51.100.3
metric 50")
msg: "the route table 30200 does not exist or does not contain the
specified route"

Expand Down Expand Up @@ -111,6 +120,12 @@
gateway: 198.51.100.6
metric: 4
table: custom
- network: 192.0.2.64
prefix: 26
gateway: 198.51.100.8
metric: 50
table: custom
src: 198.51.100.3

- name: Get the routes from the named route table 'custom'
command: ip route show table custom
Expand All @@ -126,6 +141,9 @@
198.51.100.1 dev ethtest0 proto static metric 2")
- route_table_custom.stdout is search("198.51.100.64/26 via
198.51.100.6 dev ethtest0 proto static metric 4")
- route_table_custom.stdout is search("192.0.2.64/26 via
198.51.100.8 dev ethtest0 proto static src 198.51.100.3
metric 50")
msg: "the named route table 'custom' does not exist or does not contain
the specified route"

Expand Down
38 changes: 38 additions & 0 deletions tests/unit/test_network_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def assert_nm_connection_routes_expected(self, connection, route_list_expected):
"metric": int(r.get_metric()),
"type": r.get_attribute("type"),
"table": r.get_attribute("table"),
"src": r.get_attribute("src"),
}
for r in route_list_new
]
Expand Down Expand Up @@ -295,6 +296,12 @@ def do_connections_validate_nm(self, input_connections, **kwargs):
"table",
Util.GLib().Variant.new_uint32(r["table"]),
)
if r["src"]:
NM.IPRoute.set_attribute(
new_route,
"src",
Util.GLib().Variant.new_uint32(r["src"]),
)
if r["family"] == socket.AF_INET:
s4.add_route(new_route)
else:
Expand Down Expand Up @@ -1144,6 +1151,7 @@ def test_routes(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
}
],
"routing_rule": [],
Expand Down Expand Up @@ -1485,6 +1493,7 @@ def test_vlan(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
}
],
"routing_rule": [],
Expand Down Expand Up @@ -1635,6 +1644,7 @@ def test_macvlan(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
}
],
"routing_rule": [],
Expand Down Expand Up @@ -1698,6 +1708,7 @@ def test_macvlan(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
}
],
"routing_rule": [],
Expand Down Expand Up @@ -2661,6 +2672,7 @@ def test_route_metric_prefix(self):
"metric": 545,
"type": None,
"table": None,
"src": None,
},
{
"family": socket.AF_INET,
Expand All @@ -2670,6 +2682,7 @@ def test_route_metric_prefix(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
},
],
"routing_rule": [],
Expand Down Expand Up @@ -2767,6 +2780,7 @@ def test_route_v6(self):
"metric": 545,
"type": None,
"table": None,
"src": None,
},
{
"family": socket.AF_INET,
Expand All @@ -2776,6 +2790,7 @@ def test_route_v6(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
},
{
"family": socket.AF_INET6,
Expand All @@ -2785,6 +2800,7 @@ def test_route_v6(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
},
],
"routing_rule": [],
Expand Down Expand Up @@ -2923,6 +2939,7 @@ def test_route_without_interface_name(self):
"metric": 545,
"type": None,
"table": None,
"src": None,
},
{
"family": socket.AF_INET,
Expand All @@ -2932,6 +2949,7 @@ def test_route_without_interface_name(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
},
{
"family": socket.AF_INET6,
Expand All @@ -2941,6 +2959,7 @@ def test_route_without_interface_name(self):
"metric": -1,
"type": None,
"table": None,
"src": None,
},
],
"routing_rule": [],
Expand Down Expand Up @@ -5001,6 +5020,25 @@ def test_type_route_with_gateway(self):
self.test_connections,
)

def test_route_with_source_address(self):
"""
Test setting the route with src address specified
"""
self.test_connections[0]["ip"]["route"][0]["src"] = "2001:db8::2"
self.assertRaisesRegex(
ValidationError,
"conflicting address family between network and src "
"address {0}".format(
self.test_connections[0]["ip"]["route"][0]["src"],
),
self.validator.validate,
self.test_connections,
)

self.test_connections[0]["ip"]["route"][0]["src"] = "198.51.100.3"
result = self.validator.validate(self.test_connections)
self.assertEqual(result[0]["ip"]["route"][0]["src"], "198.51.100.3")


class TestValidatorRoutingRules(Python26CompatTestCase):
def setUp(self):
Expand Down
Loading