Skip to content

Commit 7519b1b

Browse files
iPraveenPariharceph-csi-bot
authored andcommitted
cephfs: remove redundant client eviction in fencing workflow
The MDS automatically evicts clients whose IPs appear in the OSD blocklist, making explicit `client ls` and `client evict` commands redundant. These MDS admin socket commands also require wildcard (`*`) caps that cannot be narrowed down, causing EACCES errors when the provisioner client lacks full MDS permissions. Remove the redundant eviction step and use only OSD blocklisting for CephFS fencing, consistent with how RBD fencing already works. Signed-off-by: Praveen M <m.praveen@ibm.com>
1 parent b42a8a5 commit 7519b1b

3 files changed

Lines changed: 3 additions & 226 deletions

File tree

internal/csi-addons/cephfs/network_fence.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ func validateNetworkFenceReq(fenceClients []*fence.CIDR, options map[string]stri
6464
}
6565

6666
// FenceClusterNetwork blocks access to a CIDR block by creating a network fence.
67-
// It evicts the IP addresses of clients, which are in CIDR block.
67+
// The MDS automatically evicts clients whose IPs appear in the OSD blocklist,
68+
// so explicit client eviction via MDS commands is not needed.
6869
func (fcs *FenceControllerServer) FenceClusterNetwork(
6970
ctx context.Context,
7071
req *fence.FenceClusterNetworkRequest,
@@ -85,7 +86,7 @@ func (fcs *FenceControllerServer) FenceClusterNetwork(
8586
return nil, status.Error(codes.Internal, err.Error())
8687
}
8788

88-
err = nwFence.AddClientEviction(ctx)
89+
err = nwFence.AddNetworkFence(ctx)
8990
if err != nil {
9091
return nil, status.Errorf(codes.Internal, "failed to fence CIDR block %q: %s", nwFence.Cidr, err.Error())
9192
}

internal/csi-addons/networkfence/fencing.go

Lines changed: 0 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ package networkfence
1818

1919
import (
2020
"context"
21-
"encoding/json"
2221
"errors"
2322
"fmt"
2423
"net"
25-
"strconv"
2624
"strings"
2725
"time"
2826

@@ -42,8 +40,6 @@ const (
4240
// TODO: Make this configurable.
4341
blockListCoolDownPeriod = 5 * time.Minute
4442
invalidCommandStr = "invalid command"
45-
// we can always use mds rank 0, since all the clients have a session with rank-0.
46-
mdsRank = 0
4743
)
4844

4945
// NetworkFence contains the CIDR blocks to be blocked.
@@ -53,11 +49,6 @@ type NetworkFence struct {
5349
cr *util.Credentials
5450
}
5551

56-
// activeClient represents the structure of an active client.
57-
type activeClient struct {
58-
Inst string `json:"inst"`
59-
}
60-
6152
// NewNetworkFence returns a networkFence struct object from the Network fence/unfence request.
6253
func NewNetworkFence(
6354
ctx context.Context,
@@ -88,51 +79,6 @@ func NewNetworkFence(
8879
return nwFence, nil
8980
}
9081

91-
// AddClientEviction blocks access for all the IPs in the CIDR block
92-
// using client eviction, it also blocks the entire CIDR.
93-
func (nf *NetworkFence) AddClientEviction(ctx context.Context) error {
94-
evictedIPs := make(map[string]bool)
95-
// fetch active clients
96-
activeClients, err := nf.listActiveClients(ctx)
97-
if err != nil {
98-
return err
99-
}
100-
// iterate through CIDR blocks and check if any active client matches
101-
for _, cidr := range nf.Cidr {
102-
for _, client := range activeClients {
103-
var clientIP string
104-
clientIP, err = client.fetchIP()
105-
if err != nil {
106-
return fmt.Errorf("error fetching client IP: %w", err)
107-
}
108-
// check if the clientIP is in the CIDR block
109-
if isIPInCIDR(ctx, clientIP, cidr) {
110-
var clientID int
111-
clientID, err = client.fetchID()
112-
if err != nil {
113-
return fmt.Errorf("error fetching client ID: %w", err)
114-
}
115-
// evict the client
116-
err = nf.evictCephFSClient(ctx, clientID)
117-
if err != nil {
118-
return fmt.Errorf("error evicting client %d: %w", clientID, err)
119-
}
120-
log.DebugLog(ctx, "client %d has been evicted\n", clientID)
121-
// add the CIDR to the list of blocklisted IPs
122-
evictedIPs[clientIP] = true
123-
}
124-
}
125-
}
126-
127-
// add the range based blocklist for CIDR
128-
err = nf.AddNetworkFence(ctx)
129-
if err != nil {
130-
return err
131-
}
132-
133-
return nil
134-
}
135-
13682
// RemoveNetworkFence unblocks access for all the IPs in the IP range mentioned via the CIDR block
13783
// using a network fence.
13884
// Unfencing one of the protocols(CephFS or RBD) suggests the node is expected to be recovered, so
@@ -220,91 +166,6 @@ func (nf *NetworkFence) addCephBlocklist(ctx context.Context, ip string, useRang
220166
return util.AddCephBlocklist(ctx, nf.Monitors, nf.cr, ip, useRange)
221167
}
222168

223-
func (nf *NetworkFence) listActiveClients(ctx context.Context) ([]activeClient, error) {
224-
arg := []string{
225-
"--id", nf.cr.ID,
226-
"--keyfile=" + nf.cr.KeyFile,
227-
"-m", nf.Monitors,
228-
}
229-
// FIXME: replace the ceph command with go-ceph API in future
230-
cmd := []string{"tell", fmt.Sprintf("mds.%d", mdsRank), "client", "ls"}
231-
cmd = append(cmd, arg...)
232-
stdout, stdErr, err := util.ExecCommandWithTimeout(ctx, 2*time.Minute, "ceph", cmd...)
233-
if err != nil {
234-
return nil, fmt.Errorf("failed to list active clients: %w, stderr: %q", err, stdErr)
235-
}
236-
237-
var activeClients []activeClient
238-
if err := json.Unmarshal([]byte(stdout), &activeClients); err != nil {
239-
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
240-
}
241-
242-
return activeClients, nil
243-
}
244-
245-
func (nf *NetworkFence) evictCephFSClient(ctx context.Context, clientID int) error {
246-
arg := []string{
247-
"--id", nf.cr.ID,
248-
"--keyfile=" + nf.cr.KeyFile,
249-
"-m", nf.Monitors,
250-
}
251-
// FIXME: replace the ceph command with go-ceph API in future
252-
cmd := []string{"tell", fmt.Sprintf("mds.%d", mdsRank), "client", "evict", fmt.Sprintf("id=%d", clientID)}
253-
cmd = append(cmd, arg...)
254-
_, stdErr, err := util.ExecCommandWithTimeout(ctx, 2*time.Minute, "ceph", cmd...)
255-
if err != nil {
256-
return fmt.Errorf("failed to evict client %d: %w, stderr: %q", clientID, err, stdErr)
257-
}
258-
log.DebugLog(ctx, "client %s has been evicted from CephFS\n", clientID)
259-
260-
return nil
261-
}
262-
263-
func isIPInCIDR(ctx context.Context, ip, cidr string) bool {
264-
// Parse the CIDR block
265-
_, ipCidr, err := net.ParseCIDR(cidr)
266-
if err != nil {
267-
log.ErrorLog(ctx, "error parsing CIDR block %s: %w\n", cidr, err)
268-
269-
return false
270-
}
271-
272-
// Parse the IP address
273-
ipAddress := net.ParseIP(ip)
274-
if ipAddress == nil {
275-
log.ErrorLog(ctx, "error parsing IP address %s\n", ip)
276-
277-
return false
278-
}
279-
280-
// Check if the IP address is within the CIDR block
281-
return ipCidr.Contains(ipAddress)
282-
}
283-
284-
func (ac *activeClient) fetchIP() (string, error) {
285-
// example: "inst": "client.4305 172.21.9.34:0/422650892",
286-
// then returning value will be 172.21.9.34
287-
return util.ParseClientIP(ac.Inst)
288-
}
289-
290-
func (ac *activeClient) fetchID() (int, error) {
291-
// example: "inst": "client.4305 172.21.9.34:0/422650892",
292-
// then returning value will be 4305
293-
clientInfo := ac.Inst
294-
parts := strings.Fields(clientInfo)
295-
if len(parts) >= 1 {
296-
clientIDStr := strings.TrimPrefix(parts[0], "client.")
297-
clientID, err := strconv.Atoi(clientIDStr)
298-
if err != nil {
299-
return 0, fmt.Errorf("failed to convert client ID to int: %w", err)
300-
}
301-
302-
return clientID, nil
303-
}
304-
305-
return 0, fmt.Errorf("failed to extract client ID, incorrect format: %s", clientInfo)
306-
}
307-
308169
// getIPRange returns a list of IPs from the IP range
309170
// corresponding to a CIDR block.
310171
func getIPRange(cidr string) ([]string, error) {

internal/csi-addons/networkfence/fencing_test.go

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -58,91 +58,6 @@ func TestGetIPRange(t *testing.T) {
5858
}
5959
}
6060

61-
func TestFetchIP(t *testing.T) {
62-
t.Parallel()
63-
64-
tests := []struct {
65-
clientInfo string
66-
expectedIP string
67-
expectedErr bool
68-
}{
69-
{
70-
clientInfo: "client.4305 172.21.9.34:0/422650892",
71-
expectedIP: "172.21.9.34",
72-
expectedErr: false,
73-
},
74-
{
75-
clientInfo: "client.4305 [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:0/422650892",
76-
expectedIP: "2001:db8:85a3::8a2e:370:7334",
77-
expectedErr: false,
78-
},
79-
{
80-
clientInfo: "client.24152 v1:100.64.0.7:0/3658550259",
81-
expectedIP: "100.64.0.7",
82-
expectedErr: false,
83-
},
84-
{
85-
clientInfo: "",
86-
expectedIP: "",
87-
expectedErr: true,
88-
},
89-
}
90-
91-
for _, tt := range tests {
92-
t.Run(tt.clientInfo, func(t *testing.T) {
93-
t.Parallel()
94-
95-
client := activeClient{Inst: tt.clientInfo}
96-
ip, actualErr := client.fetchIP()
97-
98-
if (actualErr != nil) != tt.expectedErr {
99-
t.Errorf("expected error %v but got %v", tt.expectedErr, actualErr)
100-
}
101-
102-
if ip != tt.expectedIP {
103-
t.Errorf("expected IP %s but got %s", tt.expectedIP, ip)
104-
}
105-
})
106-
}
107-
}
108-
109-
func TestFetchID(t *testing.T) {
110-
t.Parallel()
111-
112-
tests := []struct {
113-
clientInfo string
114-
expectedID int
115-
expectedErr bool
116-
}{
117-
{
118-
clientInfo: "client.4305 172.21.9.34:0/422650892",
119-
expectedID: 4305,
120-
expectedErr: false,
121-
},
122-
{
123-
clientInfo: "",
124-
expectedID: 0,
125-
expectedErr: true,
126-
},
127-
}
128-
129-
for _, tt := range tests {
130-
t.Run(tt.clientInfo, func(t *testing.T) {
131-
t.Parallel()
132-
ac := &activeClient{Inst: tt.clientInfo}
133-
actualID, actualErr := ac.fetchID()
134-
135-
if (actualErr != nil) != tt.expectedErr {
136-
t.Errorf("expected error %v but got %v", tt.expectedErr, actualErr)
137-
}
138-
139-
if actualID != tt.expectedID {
140-
t.Errorf("expected ID %d but got %d", tt.expectedID, actualID)
141-
}
142-
})
143-
}
144-
}
145-
14661
func Test_containsMatchingBlockListEntry(t *testing.T) {
14762
t.Parallel()
14863
type args struct {

0 commit comments

Comments
 (0)