| #!/usr/bin/env python3 |
| # SPDX-License-Identifier: GPL-2.0 |
| |
| """ |
| Tests for the nlctrl genetlink family (family info and policy dumps). |
| """ |
| |
| from lib.py import ksft_run, ksft_exit |
| from lib.py import ksft_eq, ksft_ge, ksft_true, ksft_in, ksft_not_in |
| from lib.py import NetdevFamily, EthtoolFamily, NlctrlFamily |
| |
| |
| def getfamily_do(ctrl) -> None: |
| """Query a single family by name and validate its ops.""" |
| fam = ctrl.getfamily({'family-name': 'netdev'}) |
| ksft_eq(fam['family-name'], 'netdev') |
| ksft_true(fam['family-id'] > 0) |
| |
| # The format of ops is quite odd, [{$idx: {"id"...}}, {$idx: {"id"...}}] |
| # Discard the indices and re-key by command id. |
| ops_by_id = {v['id']: v for op in fam['ops'] for v in op.values()} |
| ksft_eq(len(ops_by_id), len(fam['ops'])) |
| |
| # All ops should have a policy (either do or dump has one) |
| for op in ops_by_id.values(): |
| ksft_in('cmd-cap-haspol', op['flags'], |
| comment=f"op {op['id']} missing haspol") |
| |
| # dev-get (id 1) should support both do and dump |
| ksft_in('cmd-cap-do', ops_by_id[1]['flags']) |
| ksft_in('cmd-cap-dump', ops_by_id[1]['flags']) |
| |
| # qstats-get (id 12) is dump-only |
| ksft_not_in('cmd-cap-do', ops_by_id[12]['flags']) |
| ksft_in('cmd-cap-dump', ops_by_id[12]['flags']) |
| |
| # napi-set (id 14) is do-only and requires admin |
| ksft_in('cmd-cap-do', ops_by_id[14]['flags']) |
| ksft_not_in('cmd-cap-dump', ops_by_id[14]['flags']) |
| ksft_in('admin-perm', ops_by_id[14]['flags']) |
| |
| # Notification-only commands (dev-add/del/change-ntf etc.) must |
| # not appear in the ops list since they have no do/dump handlers. |
| for ntf_id in [2, 3, 4, 6, 7, 8]: |
| ksft_not_in(ntf_id, ops_by_id, |
| comment=f"ntf-only cmd {ntf_id} should not be in ops") |
| |
| |
| def getfamily_dump(ctrl) -> None: |
| """Dump all families and verify expected entries.""" |
| families = ctrl.getfamily({}, dump=True) |
| ksft_ge(len(families), 2) |
| |
| names = [f['family-name'] for f in families] |
| ksft_in('nlctrl', names, comment="nlctrl not found in family dump") |
| ksft_in('netdev', names, comment="netdev not found in family dump") |
| |
| |
| def getpolicy_dump(_ctrl) -> None: |
| """Dump policies for ops using get_policy() and validate results. |
| |
| Test with netdev (split ops) where do and dump can have different |
| policies, and with ethtool (full ops) where they always share one. |
| """ |
| # -- netdev (split ops) -- |
| ndev = NetdevFamily() |
| |
| # dev-get: do has a real policy with ifindex, dump has no policy |
| # (only the reject-all policy with maxattr=0) |
| pol = ndev.get_policy('dev-get', 'do') |
| ksft_in('ifindex', pol, comment="dev-get do policy should have ifindex") |
| ksft_eq(pol['ifindex'].type, 'u32') |
| |
| pol_dump = ndev.get_policy('dev-get', 'dump') |
| ksft_eq(len(pol_dump), 0, comment="dev-get should not accept any attrs") |
| |
| # napi-get: both do and dump have real policies |
| pol_do = ndev.get_policy('napi-get', 'do') |
| ksft_ge(len(pol_do), 1) |
| |
| pol_dump = ndev.get_policy('napi-get', 'dump') |
| ksft_ge(len(pol_dump), 1) |
| |
| # -- ethtool (full ops) -- |
| et = EthtoolFamily() |
| |
| # strset-get (has both do and dump, full ops share policy) |
| pol_do = et.get_policy('strset-get', 'do') |
| ksft_ge(len(pol_do), 1, comment="strset-get should have a do policy") |
| |
| pol_dump = et.get_policy('strset-get', 'dump') |
| ksft_ge(len(pol_dump), 1, comment="strset-get should have a dump policy") |
| |
| # Same policy means same attribute names |
| ksft_eq(set(pol_do.keys()), set(pol_dump.keys())) |
| |
| # linkinfo-set is do-only (SET command), no dump |
| pol_do = et.get_policy('linkinfo-set', 'do') |
| ksft_ge(len(pol_do), 1, comment="linkinfo-set should have a do policy") |
| |
| pol_dump = et.get_policy('linkinfo-set', 'dump') |
| ksft_eq(pol_dump, None, |
| comment="linkinfo-set should not have a dump policy") |
| |
| |
| def getpolicy_by_op(_ctrl) -> None: |
| """Query policy for specific ops, check attr names are resolved.""" |
| ndev = NetdevFamily() |
| |
| # dev-get do policy should have named attributes from the spec |
| pol = ndev.get_policy('dev-get', 'do') |
| ksft_ge(len(pol), 1) |
| # All attr names should be resolved (no 'attr-N' fallbacks) |
| for name in pol: |
| ksft_true(not name.startswith('attr-'), |
| comment=f"unresolved attr name: {name}") |
| |
| |
| def main() -> None: |
| """ Ksft boiler plate main """ |
| ctrl = NlctrlFamily() |
| ksft_run([getfamily_do, |
| getfamily_dump, |
| getpolicy_dump, |
| getpolicy_by_op], |
| args=(ctrl, )) |
| ksft_exit() |
| |
| |
| if __name__ == "__main__": |
| main() |