// SPDX-License-Identifier: GPL-3.0-or-later package match import ( "fmt" "strings" rs "github.com/netdata/netdata/go/go.d.plugin/modules/vsphere/resources" "github.com/netdata/netdata/go/go.d.plugin/pkg/matcher" ) type HostMatcher interface { Match(*rs.Host) bool } type VMMatcher interface { Match(*rs.VM) bool } type ( hostDCMatcher struct{ m matcher.Matcher } hostClusterMatcher struct{ m matcher.Matcher } hostHostMatcher struct{ m matcher.Matcher } vmDCMatcher struct{ m matcher.Matcher } vmClusterMatcher struct{ m matcher.Matcher } vmHostMatcher struct{ m matcher.Matcher } vmVMMatcher struct{ m matcher.Matcher } orHostMatcher struct{ lhs, rhs HostMatcher } orVMMatcher struct{ lhs, rhs VMMatcher } andHostMatcher struct{ lhs, rhs HostMatcher } andVMMatcher struct{ lhs, rhs VMMatcher } ) func (m hostDCMatcher) Match(host *rs.Host) bool { return m.m.MatchString(host.Hier.DC.Name) } func (m hostClusterMatcher) Match(host *rs.Host) bool { return m.m.MatchString(host.Hier.Cluster.Name) } func (m hostHostMatcher) Match(host *rs.Host) bool { return m.m.MatchString(host.Name) } func (m vmDCMatcher) Match(vm *rs.VM) bool { return m.m.MatchString(vm.Hier.DC.Name) } func (m vmClusterMatcher) Match(vm *rs.VM) bool { return m.m.MatchString(vm.Hier.Cluster.Name) } func (m vmHostMatcher) Match(vm *rs.VM) bool { return m.m.MatchString(vm.Hier.Host.Name) } func (m vmVMMatcher) Match(vm *rs.VM) bool { return m.m.MatchString(vm.Name) } func (m orHostMatcher) Match(host *rs.Host) bool { return m.lhs.Match(host) || m.rhs.Match(host) } func (m orVMMatcher) Match(vm *rs.VM) bool { return m.lhs.Match(vm) || m.rhs.Match(vm) } func (m andHostMatcher) Match(host *rs.Host) bool { return m.lhs.Match(host) && m.rhs.Match(host) } func (m andVMMatcher) Match(vm *rs.VM) bool { return m.lhs.Match(vm) && m.rhs.Match(vm) } func newAndHostMatcher(lhs, rhs HostMatcher, others ...HostMatcher) andHostMatcher { m := andHostMatcher{lhs: lhs, rhs: rhs} switch len(others) { case 0: return m default: return newAndHostMatcher(m, others[0], others[1:]...) } } func newAndVMMatcher(lhs, rhs VMMatcher, others ...VMMatcher) andVMMatcher { m := andVMMatcher{lhs: lhs, rhs: rhs} switch len(others) { case 0: return m default: return newAndVMMatcher(m, others[0], others[1:]...) } } func newOrHostMatcher(lhs, rhs HostMatcher, others ...HostMatcher) orHostMatcher { m := orHostMatcher{lhs: lhs, rhs: rhs} switch len(others) { case 0: return m default: return newOrHostMatcher(m, others[0], others[1:]...) } } func newOrVMMatcher(lhs, rhs VMMatcher, others ...VMMatcher) orVMMatcher { m := orVMMatcher{lhs: lhs, rhs: rhs} switch len(others) { case 0: return m default: return newOrVMMatcher(m, others[0], others[1:]...) } } type ( VMIncludes []string HostIncludes []string ) func (vi VMIncludes) Parse() (VMMatcher, error) { var ms []VMMatcher for _, v := range vi { m, err := parseVMInclude(v) if err != nil { return nil, err } if m == nil { continue } ms = append(ms, m) } switch len(ms) { case 0: return nil, nil case 1: return ms[0], nil default: return newOrVMMatcher(ms[0], ms[1], ms[2:]...), nil } } func (hi HostIncludes) Parse() (HostMatcher, error) { var ms []HostMatcher for _, v := range hi { m, err := parseHostInclude(v) if err != nil { return nil, err } if m == nil { continue } ms = append(ms, m) } switch len(ms) { case 0: return nil, nil case 1: return ms[0], nil default: return newOrHostMatcher(ms[0], ms[1], ms[2:]...), nil } } const ( datacenterIdx = iota clusterIdx hostIdx vmIdx ) func cleanInclude(include string) string { return strings.Trim(include, "/") } func parseHostInclude(include string) (HostMatcher, error) { if !isIncludeFormatValid(include) { return nil, fmt.Errorf("bad include format: %s", include) } include = cleanInclude(include) parts := strings.Split(include, "/") // /dc/clusterIdx/hostIdx var ms []HostMatcher for i, v := range parts { m, err := parseSubInclude(v) if err != nil { return nil, err } switch i { case datacenterIdx: ms = append(ms, hostDCMatcher{m}) case clusterIdx: ms = append(ms, hostClusterMatcher{m}) case hostIdx: ms = append(ms, hostHostMatcher{m}) } } switch len(ms) { case 0: return nil, nil case 1: return ms[0], nil default: return newAndHostMatcher(ms[0], ms[1], ms[2:]...), nil } } func parseVMInclude(include string) (VMMatcher, error) { if !isIncludeFormatValid(include) { return nil, fmt.Errorf("bad include format: %s", include) } include = cleanInclude(include) parts := strings.Split(include, "/") // /dc/clusterIdx/hostIdx/vmIdx var ms []VMMatcher for i, v := range parts { m, err := parseSubInclude(v) if err != nil { return nil, err } switch i { case datacenterIdx: ms = append(ms, vmDCMatcher{m}) case clusterIdx: ms = append(ms, vmClusterMatcher{m}) case hostIdx: ms = append(ms, vmHostMatcher{m}) case vmIdx: ms = append(ms, vmVMMatcher{m}) } } switch len(ms) { case 0: return nil, nil case 1: return ms[0], nil default: return newAndVMMatcher(ms[0], ms[1], ms[2:]...), nil } } func parseSubInclude(sub string) (matcher.Matcher, error) { sub = strings.TrimSpace(sub) if sub == "" || sub == "!*" { return matcher.FALSE(), nil } if sub == "*" { return matcher.TRUE(), nil } return matcher.NewSimplePatternsMatcher(sub) } func isIncludeFormatValid(line string) bool { return strings.HasPrefix(line, "/") }