diff --git a/main.go b/main.go index dcfa3d03..8a2e8fcc 100644 --- a/main.go +++ b/main.go @@ -106,4 +106,7 @@ const usage = ` X_CSI_POWERMAX_ENDPOINT X_CSI_POWERMAX_DEBUG Turns on debugging of the PowerMax (REST interface to Unisphere) layer + + X_CSI_IFACE_EXCLUDE_FILTER + Regular expression of interface names to exclude. ` diff --git a/service/envvars.go b/service/envvars.go index 491e872b..0cbef4e5 100644 --- a/service/envvars.go +++ b/service/envvars.go @@ -165,4 +165,7 @@ const ( // EnvPodmonArrayConnectivityPollRate indicates the polling frequency to check array connectivity EnvPodmonArrayConnectivityPollRate = "X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE" + + // EnvIfaceExcludeFilter is an env variable with a regex of interface names to exclude + EnvIfaceExcludeFilter = "X_CSI_IFACE_EXCLUDE_FILTER" ) diff --git a/service/node.go b/service/node.go index ab4f22bc..4fd82fb6 100644 --- a/service/node.go +++ b/service/node.go @@ -1589,7 +1589,7 @@ func (s *service) nodeStartup(ctx context.Context) error { // setVMHost create client for the vCenter func (s *service) setVMHost() error { // Create a VM host - host, err := NewVMHost(true, s.opts.VCenterHostURL, s.opts.VCenterHostUserName, s.opts.VCenterHostPassword) + host, err := NewVMHost(true, s.opts.VCenterHostURL, s.opts.VCenterHostUserName, s.opts.VCenterHostPassword, s.opts.IfaceExcludeFilter) if err != nil { log.Errorf("can not create VM host object: (%s)", err.Error()) return fmt.Errorf("Can not create VM host object: (%s)", err.Error()) diff --git a/service/service.go b/service/service.go index 745a814a..9a2db9bd 100644 --- a/service/service.go +++ b/service/service.go @@ -21,6 +21,7 @@ import ( "math/rand" "net" "os" + "regexp" "strconv" "strings" "sync" @@ -126,21 +127,22 @@ type Opts struct { NonDefaultRetries bool // Indicates if non-default retry values to be used for deletion worker, only for unit testing NodeNameTemplate string ModifyHostName bool - ReplicationContextPrefix string // Enables sidecars to read required information from volume context - ReplicationPrefix string // Used as a prefix to find out if replication is enabled - IsHealthMonitorEnabled bool // used to check if health monitor for volume is enabled - IsTopologyControlEnabled bool // used to filter topology keys based on user config - IsVsphereEnabled bool // used to check if vSphere is enabled - VSpherePortGroup string // port group for vsphere - VSphereHostName string // host (initiator group) for vsphere - VCenterHostURL string // vCenter host url - VCenterHostUserName string // vCenter host username - VCenterHostPassword string // vCenter password - MaxVolumesPerNode int64 // to specify volume limits - KubeConfigPath string // to specify k8s configuration to be used CSI driver - IsPodmonEnabled bool // used to indicate that podmon is enabled - PodmonPort string // to indicates the port to be used for exposing podmon API health - PodmonPollingFreq string // indicates the polling frequency to check array connectivity + ReplicationContextPrefix string // Enables sidecars to read required information from volume context + ReplicationPrefix string // Used as a prefix to find out if replication is enabled + IsHealthMonitorEnabled bool // used to check if health monitor for volume is enabled + IsTopologyControlEnabled bool // used to filter topology keys based on user config + IsVsphereEnabled bool // used to check if vSphere is enabled + VSpherePortGroup string // port group for vsphere + VSphereHostName string // host (initiator group) for vsphere + VCenterHostURL string // vCenter host url + VCenterHostUserName string // vCenter host username + VCenterHostPassword string // vCenter password + MaxVolumesPerNode int64 // to specify volume limits + KubeConfigPath string // to specify k8s configuration to be used CSI driver + IsPodmonEnabled bool // used to indicate that podmon is enabled + PodmonPort string // to indicates the port to be used for exposing podmon API health + PodmonPollingFreq string // indicates the polling frequency to check array connectivity + IfaceExcludeFilter *regexp.Regexp // regex of interface names to exclude from consideration } // NodeConfig defines rules for given node @@ -427,6 +429,9 @@ func (s *service) BeforeServe( if replicationPrefix, ok := csictx.LookupEnv(ctx, EnvReplicationPrefix); ok { opts.ReplicationPrefix = replicationPrefix } + if ifaceExcludeFilter, ok := csictx.LookupEnv(ctx, EnvIfaceExcludeFilter); ok { + opts.IfaceExcludeFilter = regexp.MustCompile(ifaceExcludeFilter) + } if MaxVolumesPerNode, ok := csictx.LookupEnv(ctx, EnvMaxVolumesPerNode); ok { val, err := strconv.ParseInt(MaxVolumesPerNode, 10, 64) diff --git a/service/service_unit_test.go b/service/service_unit_test.go index b2cb7041..a69f89d0 100644 --- a/service/service_unit_test.go +++ b/service/service_unit_test.go @@ -17,9 +17,12 @@ package service import ( "context" + "errors" "fmt" "math/rand" + "net" "os" + "regexp" "strconv" "strings" "sync" @@ -635,3 +638,160 @@ func TestSetArrayConfigEnvs(t *testing.T) { err := setArrayConfigEnvs(ctx) assert.Equal(t, nil, err) } + +func TestGetLocalMACFunc(t *testing.T) { + orderedIfs := []net.Interface{ + { + Index: 1, + MTU: 65536, + Name: "lo", + HardwareAddr: nil, + Flags: 0x25, + }, + { + Index: 2, + MTU: 1500, + Name: "ens192", + HardwareAddr: net.HardwareAddr{0x0, 0x1, 0x2, 0x3, 0x4, 0x5}, + Flags: 0x33, + }, + { + Index: 5, + MTU: 1450, + Name: "vxlan.calico", + HardwareAddr: net.HardwareAddr{0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + Flags: 0x33, + }, + { + Index: 8, + MTU: 1450, + Name: "cali1d87cc7ab3f", + HardwareAddr: net.HardwareAddr{0xee, 0xee, 0xee, 0xee, 0xee, 0xee}, + Flags: 0x33, + }, + { + Index: 13, + MTU: 1450, + Name: "cali06df89a6f82", + HardwareAddr: net.HardwareAddr{0xee, 0xee, 0xee, 0xee, 0xee, 0xee}, + Flags: 0x33, + }, + } + unorderedIfs := []net.Interface{ + { + Index: 257, + MTU: 1450, + Name: "cali24aa7dc293b", + HardwareAddr: net.HardwareAddr{0xee, 0xee, 0xee, 0xee, 0xee, 0xee}, + Flags: 0x33, + }, + { + Index: 1, + MTU: 65536, + Name: "lo", + HardwareAddr: nil, + Flags: 0x25, + }, + { + Index: 2, + MTU: 1500, + Name: "ens192", + HardwareAddr: net.HardwareAddr{0x10, 0x11, 0x12, 0x13, 0x14, 0x15}, + Flags: 0x33, + }, + { + Index: 259, + MTU: 1450, + Name: "cali1d87cc7ab3f", + HardwareAddr: net.HardwareAddr{0xee, 0xee, 0xee, 0xee, 0xee, 0xee}, + Flags: 0x33, + }, + { + Index: 7, + MTU: 1450, + Name: "vxlan.calico", + HardwareAddr: net.HardwareAddr{0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b}, + Flags: 0x33, + }, + { + Index: 11, + MTU: 1450, + Name: "calibbe3ea81e70", + HardwareAddr: net.HardwareAddr{0xee, 0xee, 0xee, 0xee, 0xee, 0xee}, + Flags: 0x33, + }, + } + onlyLoIfs := []net.Interface{ + { + Index: 1, + MTU: 65536, + Name: "lo", + HardwareAddr: nil, + Flags: 0x25, + }, + } + + tests := []struct { + expected string + expectErr bool + ifaceFunc func() ([]net.Interface, error) + ifaceExcludeFilter *regexp.Regexp + testName string + }{ + { + testName: "basic ordered IFs test", + expectErr: false, + expected: "00:01:02:03:04:05", + ifaceFunc: func() ([]net.Interface, error) { + return orderedIfs, nil + }, + ifaceExcludeFilter: nil, + }, + { + testName: "basic unordered IFs test", + expectErr: false, + expected: "ee:ee:ee:ee:ee:ee", + ifaceFunc: func() ([]net.Interface, error) { + return unorderedIfs, nil + }, + ifaceExcludeFilter: nil, + }, + { + testName: "expected error from only lo", + expectErr: true, + ifaceFunc: func() ([]net.Interface, error) { + return onlyLoIfs, nil + }, + ifaceExcludeFilter: nil, + }, + { + testName: "expected error from error in ifaceFunc", + expectErr: true, + ifaceFunc: func() ([]net.Interface, error) { + return []net.Interface{}, errors.New("kaboom") + }, + ifaceExcludeFilter: nil, + }, + { + testName: "exclude cali interfaces", + expectErr: false, + expected: "10:11:12:13:14:15", + ifaceFunc: func() ([]net.Interface, error) { + return unorderedIfs, nil + }, + ifaceExcludeFilter: regexp.MustCompile("^cali.+"), + }, + } + + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + returnedIf, err := getLocalMAC(tt.ifaceFunc, tt.ifaceExcludeFilter) + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, returnedIf) + } + }) + } +} diff --git a/service/vsphere.go b/service/vsphere.go index 8bd29955..55eedf70 100644 --- a/service/vsphere.go +++ b/service/vsphere.go @@ -23,6 +23,7 @@ import ( "net" "net/url" "reflect" + "regexp" "strings" log "github.com/sirupsen/logrus" @@ -51,7 +52,7 @@ type VMHost struct { // NewVMHost connects to a ESXi or vCenter instance and returns a *VMHost // This method is referenced from https://github.com/codedellemc/govmax/blob/master/api/v1/vmomi.go -func NewVMHost(insecure bool, hostURLparam, user, pass string) (*VMHost, error) { +func NewVMHost(insecure bool, hostURLparam, user, pass string, ifaceExcludeFilter *regexp.Regexp) (*VMHost, error) { ctx, _ := context.WithCancel(context.Background()) protocol := "https://" if useHTTP { @@ -65,7 +66,7 @@ func NewVMHost(insecure bool, hostURLparam, user, pass string) (*VMHost, error) return nil, err } - mac, err := getLocalMAC() + mac, err := getLocalMAC(net.Interfaces, ifaceExcludeFilter) if err != nil { return nil, err } @@ -97,17 +98,26 @@ func (vmh *VMHost) getMACAddressOfVM(vm *object.VirtualMachine) (string, error) return vmDeviceList.PrimaryMacAddress(), nil } -func getLocalMAC() (string, error) { - ifs, err := net.Interfaces() +func getLocalMAC(ifaceListFunc func() ([]net.Interface, error), ifaceExcludeFilter *regexp.Regexp) (string, error) { + ifs, err := ifaceListFunc() if err != nil { return "", err } for _, v := range ifs { - if v.HardwareAddr.String() != "" { - return v.HardwareAddr.String(), nil + if ifaceExcludeFilter != nil { + if ifaceExcludeFilter.MatchString(v.Name) { + continue + } + if v.HardwareAddr.String() != "" { + return v.HardwareAddr.String(), nil + } + } else { + if v.HardwareAddr.String() != "" { + return v.HardwareAddr.String(), nil + } } } - return "", errors.New("No network interface found") + return "", errors.New("no network interface found") } ///////////////////////////////////////////////////////////////////