diff options
| author | Stefan Hanreich <s.hanreich@proxmox.com> | 2025-07-16 15:08:09 +0200 | 
|---|---|---|
| committer | Thomas Lamprecht <t.lamprecht@proxmox.com> | 2025-07-17 00:10:43 +0200 | 
| commit | 21d2ad3f7b6d37a8974127aabc07459ba5166b1f (patch) | |
| tree | 55e11efab76c40284830ba222468f3fccea7ef6e | |
| parent | 4b7a87353497457ee8f8d4093259bad5703916c9 (diff) | |
api: fabrics: add root-level module
There is one endpoint (/all) at the top-level that fetches both types
of fabric entities (fabrics & nodes) and lists them separately. This
is used for the main view, in order to avoid having to do two API
calls. It works analogous to the existing root-level SDN API calls
with the running / pending parameters.
Also, since the interfaces key is used in the node sections, we need
to add it to the function encoding the values so they are compared and
returned from the API properly, when the pending parameter is set.
Co-authored-by: Gabriel Goller <g.goller@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
Link: https://lore.proxmox.com/20250716130837.585796-49-g.goller@proxmox.com
| -rw-r--r-- | src/PVE/API2/Network/SDN.pm | 7 | ||||
| -rw-r--r-- | src/PVE/API2/Network/SDN/Fabrics.pm | 165 | ||||
| -rw-r--r-- | src/PVE/API2/Network/SDN/Makefile | 3 | ||||
| -rw-r--r-- | src/PVE/Network/SDN.pm | 2 | 
4 files changed, 175 insertions, 2 deletions
diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm index 0824410..6645f28 100644 --- a/src/PVE/API2/Network/SDN.pm +++ b/src/PVE/API2/Network/SDN.pm @@ -17,6 +17,7 @@ use PVE::API2::Network::SDN::Vnets;  use PVE::API2::Network::SDN::Zones;  use PVE::API2::Network::SDN::Ipams;  use PVE::API2::Network::SDN::Dns; +use PVE::API2::Network::SDN::Fabrics;  use base qw(PVE::RESTHandler); @@ -46,6 +47,11 @@ __PACKAGE__->register_method({  });  __PACKAGE__->register_method({ +    subclass => "PVE::API2::Network::SDN::Fabrics", +    path => 'fabrics', +}); + +__PACKAGE__->register_method({      name => 'index',      path => '',      method => 'GET', @@ -76,6 +82,7 @@ __PACKAGE__->register_method({              { id => 'controllers' },              { id => 'ipams' },              { id => 'dns' }, +            { id => 'fabrics' },          ];          return $res; diff --git a/src/PVE/API2/Network/SDN/Fabrics.pm b/src/PVE/API2/Network/SDN/Fabrics.pm new file mode 100644 index 0000000..a4a972d --- /dev/null +++ b/src/PVE/API2/Network/SDN/Fabrics.pm @@ -0,0 +1,165 @@ +package PVE::API2::Network::SDN::Fabrics; + +use strict; +use warnings; + +use PVE::Tools qw(extract_param); + +use PVE::Network::SDN; +use PVE::Network::SDN::Fabrics; + +use PVE::RESTHandler; +use base qw(PVE::RESTHandler); + +__PACKAGE__->register_method({ +    name => 'index', +    path => '', +    method => 'GET', +    permissions => { +        check => ['perm', '/sdn/fabrics', ['SDN.Audit']], +    }, +    description => "SDN Fabrics Index", +    parameters => { +        properties => {}, +    }, +    returns => { +        type => 'array', +        items => { +            type => "object", +            properties => { +                subdir => { type => 'string' }, +            }, +        }, +        links => [{ rel => 'child', href => "{subdir}" }], +    }, +    code => sub { +        my ($param) = @_; + +        my $res = [ +            { subdir => 'all' }, +        ]; + +        return $res; +    }, +}); + +__PACKAGE__->register_method({ +    name => 'list_all', +    path => 'all', +    method => 'GET', +    permissions => { +        description => +            "Only list fabrics where you have 'SDN.Audit' or 'SDN.Allocate' permissions on\n" +            . "'/sdn/fabrics/<fabric>', only list nodes where you have 'Sys.Audit' or 'Sys.Modify' on /nodes/<node_id>", +        user => 'all', +    }, +    description => "SDN Fabrics Index", +    parameters => { +        properties => { +            running => { +                type => 'boolean', +                optional => 1, +                description => "Display running config.", +            }, +            pending => { +                type => 'boolean', +                optional => 1, +                description => "Display pending config.", +            }, +        }, +    }, +    returns => { +        type => 'object', +        properties => { +            fabrics => { +                type => 'array', +                items => { +                    type => "object", +                    properties => PVE::Network::SDN::Fabrics::fabric_properties(0), +                }, +            }, +            nodes => { +                type => 'array', +                items => { +                    type => "object", +                    properties => PVE::Network::SDN::Fabrics::node_properties(0), +                }, +            }, +        }, +    }, +    code => sub { +        my ($param) = @_; + +        my $pending = extract_param($param, 'pending'); +        my $running = extract_param($param, 'running'); + +        my $digest; +        my $fabrics; +        my $nodes; + +        if ($pending) { +            my $current_config = PVE::Network::SDN::Fabrics::config(); +            my $running_config = PVE::Network::SDN::Fabrics::config(1); + +            my ($running_fabrics, $running_nodes) = $running_config->list_all(); + +            my ($current_fabrics, $current_nodes) = $current_config->list_all(); + +            my $pending_fabrics = PVE::Network::SDN::pending_config( +                { fabrics => { ids => $running_fabrics } }, +                { ids => $current_fabrics }, +                'fabrics', +            ); + +            my $pending_nodes = PVE::Network::SDN::pending_config( +                { nodes => { ids => $running_nodes } }, +                { ids => $current_nodes }, +                'nodes', +            ); + +            $digest = $current_config->digest(); +            $fabrics = $pending_fabrics->{ids}; +            $nodes = $pending_nodes->{ids}; +        } elsif ($running) { +            ($fabrics, $nodes) = PVE::Network::SDN::Fabrics::config(1)->list_all(); +        } else { +            my $current_config = PVE::Network::SDN::Fabrics::config(); + +            ($fabrics, $nodes) = $current_config->list_all(); +            $digest = $current_config->digest(); +        } + +        my $rpcenv = PVE::RPCEnvironment::get(); +        my $authuser = $rpcenv->get_user(); +        my $fabric_privs = ['SDN.Audit', 'SDN.Allocate']; +        my $node_privs = ['Sys.Audit', 'Sys.Modify']; + +        my @res_fabrics; +        for my $id (keys %$fabrics) { +            next if !$rpcenv->check_any($authuser, "/sdn/fabrics/$id", $fabric_privs, 1); + +            $fabrics->{$id}->{digest} = $digest if $digest; +            push @res_fabrics, $fabrics->{$id}; +        } + +        my @res_nodes; +        for my $node_id (keys %$nodes) { +            my $node = $nodes->{$node_id}; +            my $fabric_id = $node->{fabric_id} // $node->{pending}->{fabric_id}; + +            next if !$rpcenv->check_any($authuser, "/sdn/fabrics/$fabric_id", $fabric_privs, 1); +            next if !$rpcenv->check_any($authuser, "/nodes/$node_id", $node_privs, 1); + +            $node->{digest} = $digest if $digest; + +            push @res_nodes, $node; +        } + +        return { +            fabrics => \@res_fabrics, +            nodes => \@res_nodes, +        }; +    }, +}); + +1; diff --git a/src/PVE/API2/Network/SDN/Makefile b/src/PVE/API2/Network/SDN/Makefile index abd1bfa..08bec75 100644 --- a/src/PVE/API2/Network/SDN/Makefile +++ b/src/PVE/API2/Network/SDN/Makefile @@ -1,4 +1,4 @@ -SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm Ips.pm +SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm Ips.pm Fabrics.pm  PERL5DIR=${DESTDIR}/usr/share/perl5 @@ -7,4 +7,5 @@ PERL5DIR=${DESTDIR}/usr/share/perl5  install:  	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/SDN/$$i; done  	make -C Zones install +	make -C Fabrics install diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm index 8c584f6..66665a4 100644 --- a/src/PVE/Network/SDN.pm +++ b/src/PVE/Network/SDN.pm @@ -376,7 +376,7 @@ sub generate_dhcp_config {  sub encode_value {      my ($type, $key, $value) = @_; -    if ($key eq 'nodes' || $key eq 'exitnodes' || $key eq 'dhcp-range') { +    if ($key eq 'nodes' || $key eq 'exitnodes' || $key eq 'dhcp-range' || $key eq 'interfaces') {          if (ref($value) eq 'HASH') {              return join(',', sort keys(%$value));          } elsif (ref($value) eq 'ARRAY') {  | 
