From b4592a55ebe60dc81ba0a5d9a47fa83862a189a0 Mon Sep 17 00:00:00 2001 From: Madhu Rajanna Date: Tue, 5 Nov 2024 11:17:22 +0100 Subject: [PATCH] rbd: parse IP address The address we get from ceph contains the ip in the format of 10.244.0.1:0/2686266785 we need to extract the client IP from this address, we already have a helper to extract it, This makes the helper more generic can be reused by multiple packages in the fence controller. Signed-off-by: Madhu Rajanna --- internal/csi-addons/networkfence/fencing.go | 52 ++++++++++--------- .../csi-addons/networkfence/fencing_test.go | 42 +++++++++++++++ internal/csi-addons/rbd/network_fence.go | 12 ++++- 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/internal/csi-addons/networkfence/fencing.go b/internal/csi-addons/networkfence/fencing.go index 1f1eff72a..efa0de0fc 100644 --- a/internal/csi-addons/networkfence/fencing.go +++ b/internal/csi-addons/networkfence/fencing.go @@ -217,31 +217,7 @@ func isIPInCIDR(ctx context.Context, ip, cidr string) bool { func (ac *activeClient) fetchIP() (string, error) { // example: "inst": "client.4305 172.21.9.34:0/422650892", // then returning value will be 172.21.9.34 - clientInfo := ac.Inst - - // Attempt to extract the IP address using a regular expression - // the regular expression aims to match either a complete IPv6 - // address or a complete IPv4 address follows by any prefix (v1 or v2) - // if exists - // (?:v[0-9]+:): this allows for an optional prefix starting with "v" - // followed by one or more digits and a colon. - // The ? outside the group makes the entire prefix section optional. - // (?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}: this allows to check for - // standard IPv6 address. - // |: Alternation operator to allow matching either the IPv6 pattern - // with a prefix or the IPv4 pattern. - // '(?:\d+\.){3}\d+: This part matches a standard IPv4 address. - re := regexp.MustCompile(`(?:v[0-9]+:)?([0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}|(?:\d+\.){3}\d+)`) - ipMatches := re.FindStringSubmatch(clientInfo) - - if len(ipMatches) > 0 { - ip := net.ParseIP(ipMatches[1]) - if ip != nil { - return ip.String(), nil - } - } - - return "", fmt.Errorf("failed to extract IP address, incorrect format: %s", clientInfo) + return ParseClientIP(ac.Inst) } func (ac *activeClient) fetchID() (int, error) { @@ -526,3 +502,29 @@ func (nf *NetworkFence) parseBlocklistForCIDR(ctx context.Context, blocklist, ci return matchingHosts } + +func ParseClientIP(addr string) (string, error) { + // Attempt to extract the IP address using a regular expression + // the regular expression aims to match either a complete IPv6 + // address or a complete IPv4 address follows by any prefix (v1 or v2) + // if exists + // (?:v[0-9]+:): this allows for an optional prefix starting with "v" + // followed by one or more digits and a colon. + // The ? outside the group makes the entire prefix section optional. + // (?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}: this allows to check for + // standard IPv6 address. + // |: Alternation operator to allow matching either the IPv6 pattern + // with a prefix or the IPv4 pattern. + // '(?:\d+\.){3}\d+: This part matches a standard IPv4 address. + re := regexp.MustCompile(`(?:v[0-9]+:)?([0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}|(?:\d+\.){3}\d+)`) + ipMatches := re.FindStringSubmatch(addr) + + if len(ipMatches) > 0 { + ip := net.ParseIP(ipMatches[1]) + if ip != nil { + return ip.String(), nil + } + } + + return "", fmt.Errorf("failed to extract IP address, incorrect format: %s", addr) +} diff --git a/internal/csi-addons/networkfence/fencing_test.go b/internal/csi-addons/networkfence/fencing_test.go index 4bd7636ac..4a6914685 100644 --- a/internal/csi-addons/networkfence/fencing_test.go +++ b/internal/csi-addons/networkfence/fencing_test.go @@ -257,3 +257,45 @@ listed 1 entries`, }) } } + +func TestParseClientIP(t *testing.T) { + t.Parallel() + tests := []struct { + name string + addr string + want string + wantErr bool + }{ + { + name: "IPv4 address", + addr: "10.244.0.1:0/2686266785", + want: "10.244.0.1", + wantErr: false, + }, + { + name: "IPv6 address", + addr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334:0/2686266785", + want: "2001:db8:85a3::8a2e:370:7334", + wantErr: false, + }, + { + name: "Invalid address", + addr: "invalid", + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := ParseClientIP(tt.addr) + if (err != nil) != tt.wantErr { + t.Errorf("ParseClientIP() error = %v, wantErr %v", err, tt.wantErr) + } + + if got != tt.want { + t.Errorf("ParseClientIP() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/csi-addons/rbd/network_fence.go b/internal/csi-addons/rbd/network_fence.go index 2a258ff7e..4ff46d7d9 100644 --- a/internal/csi-addons/rbd/network_fence.go +++ b/internal/csi-addons/rbd/network_fence.go @@ -155,13 +155,23 @@ func (fcs *FenceControllerServer) GetFenceClients( return nil, status.Errorf(codes.Internal, "failed to get client address: %s", err) } + // The example address we get is 10.244.0.1:0/2686266785 from + // which we need to extract the IP address. + addr, err := nf.ParseClientIP(address) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to parse client address: %s", err) + } + + // adding /32 to the IP address to make it a CIDR block. + addr += "/32" + resp := &fence.GetFenceClientsResponse{ Clients: []*fence.ClientDetails{ { Id: fsID, Addresses: []*fence.CIDR{ { - Cidr: address, + Cidr: addr, }, }, },