allow passing bgp community filters in query text
This commit is contained in:
parent
bd78a05f37
commit
f72c4a3518
@ -174,7 +174,7 @@ func filterValueAsString(value interface{}) string {
|
||||
case ExtCommunity:
|
||||
return v.String()
|
||||
}
|
||||
panic("unexpected filter value")
|
||||
panic("unexpected filter value: " + fmt.Sprintf("%v", value))
|
||||
}
|
||||
|
||||
// GetFilterByValue retrieves a filter by matching
|
||||
@ -563,7 +563,7 @@ func parseCommunityFilterText(text string) (string, *SearchFilter, error) {
|
||||
|
||||
filter, err := parseCommunityValue(text)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", nil, fmt.Errorf("BGP community incomplete")
|
||||
}
|
||||
|
||||
if len(tokens) == 2 {
|
||||
@ -573,19 +573,17 @@ func parseCommunityFilterText(text string) (string, *SearchFilter, error) {
|
||||
return SearchKeyLargeCommunities, filter, nil
|
||||
}
|
||||
|
||||
// FiltersFromQueryText parses the passed list of filters
|
||||
// FiltersFromTokens parses the passed list of filters
|
||||
// extracted from the query string and creates the filter.
|
||||
func FiltersFromQueryText(filters []string) (*SearchFilters, error) {
|
||||
func FiltersFromTokens(tokens []string) (*SearchFilters, error) {
|
||||
queryFilters := NewSearchFilters()
|
||||
for _, filter := range filters {
|
||||
if strings.HasPrefix(filter, "#") { // Community query
|
||||
key, value, err := parseCommunityFilterText(filter[1:])
|
||||
for _, value := range tokens {
|
||||
if strings.HasPrefix(value, "#") { // Community query
|
||||
key, filter, err := parseCommunityFilterText(value[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryFilters.GetGroupByKey(key).AddFilter(&SearchFilter{
|
||||
Value: value,
|
||||
})
|
||||
queryFilters.GetGroupByKey(key).AddFilter(filter)
|
||||
}
|
||||
|
||||
}
|
||||
@ -623,6 +621,31 @@ func (s *SearchFilters) MatchRoute(r Filterable) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Combine two search filters
|
||||
func (s *SearchFilters) Combine(other *SearchFilters) *SearchFilters {
|
||||
result := make(SearchFilters, len(*s))
|
||||
for id, group := range *s {
|
||||
otherGroup := (*other)[id]
|
||||
combined := &SearchFilterGroup{
|
||||
Key: group.Key,
|
||||
Filters: []*SearchFilter{},
|
||||
}
|
||||
for _, f := range group.Filters {
|
||||
combined.Filters = append(combined.Filters, f)
|
||||
}
|
||||
for _, f := range otherGroup.Filters {
|
||||
if combined.Contains(f) {
|
||||
continue
|
||||
}
|
||||
combined.Filters = append(combined.Filters, f)
|
||||
}
|
||||
combined.rebuildIndex()
|
||||
result[id] = combined
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
// Sub makes a diff of two search filters
|
||||
func (s *SearchFilters) Sub(other *SearchFilters) *SearchFilters {
|
||||
result := make(SearchFilters, len(*s))
|
||||
|
@ -738,3 +738,54 @@ func TestParseExtCommunityFilterText(t *testing.T) {
|
||||
t.Error("Expected community to be ro:12345:23 but got:", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFiltersFromTokens(t *testing.T) {
|
||||
tokens := []string{"#23:42", "#ro:23:42", "#1000:23:42"}
|
||||
|
||||
filters, err := FiltersFromTokens(tokens)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check communities
|
||||
communities := filters.GetGroupByKey(SearchKeyCommunities).Filters
|
||||
if len(communities) != 1 {
|
||||
t.Error("There should be 1 community filter")
|
||||
}
|
||||
|
||||
v0 := communities[0].Value.(Community)
|
||||
if v0[0] != 23 && v0[1] != 42 {
|
||||
t.Error("Expected community to be 23:42 but got:", v0)
|
||||
}
|
||||
|
||||
// Check ext. communities
|
||||
extCommunities := filters.GetGroupByKey(SearchKeyExtCommunities).Filters
|
||||
if len(extCommunities) != 1 {
|
||||
t.Error("There should be 1 ext. community filter")
|
||||
}
|
||||
|
||||
v1 := extCommunities[0].Value.(ExtCommunity)
|
||||
if v1[0] != "ro" && v1[1] != "23" && v1[2] != "42" {
|
||||
t.Error("Expected community to be ro:23:42 but got:", v1)
|
||||
}
|
||||
|
||||
// Check large communities
|
||||
largeCommunities := filters.GetGroupByKey(SearchKeyLargeCommunities).Filters
|
||||
if len(largeCommunities) != 1 {
|
||||
t.Error("There should be 1 large community filter")
|
||||
}
|
||||
|
||||
v2 := largeCommunities[0].Value.(Community)
|
||||
if v2[0] != 1000 && v2[1] != 23 && v2[2] != 42 {
|
||||
t.Error("Expected community to be 1000:23:42 but got:", v2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFiltersFromTokensInvalid(t *testing.T) {
|
||||
tokens := []string{"#"}
|
||||
_, err := FiltersFromTokens(tokens)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid filter")
|
||||
}
|
||||
t.Log(err)
|
||||
}
|
||||
|
@ -26,20 +26,20 @@ func (s *Server) apiLookupPrefixGlobal(
|
||||
|
||||
// Get prefix to query
|
||||
q, err := validateQueryString(req, "q")
|
||||
/*
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
|
||||
// Check what we want to query
|
||||
// Prefix -> fetch prefix
|
||||
// _ -> fetch neighbors and routes
|
||||
lookupPrefix := decoders.MaybePrefix(q)
|
||||
lookupPrefix = true
|
||||
q, filterTokens := QueryString(q).ExtractFilters()
|
||||
|
||||
// Measure response time
|
||||
t0 := time.Now()
|
||||
// Get filters from query string
|
||||
queryFilters, err := api.FiltersFromTokens(filterTokens)
|
||||
if err != nil {
|
||||
return nil, &ErrValidationFailed{
|
||||
Param: "q",
|
||||
Reason: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// Get additional filter criteria
|
||||
filtersApplied, err := api.FiltersFromQuery(req.URL.Query())
|
||||
@ -47,20 +47,24 @@ func (s *Server) apiLookupPrefixGlobal(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filtersApplied.GetGroupByKey(api.SearchKeyCommunities).AddFilter(&api.SearchFilter{
|
||||
Name: "65104:150",
|
||||
Value: api.Community{65104, 150},
|
||||
})
|
||||
// Merge query filters into applied filters
|
||||
filtersApplied = filtersApplied.Combine(queryFilters)
|
||||
|
||||
// Check what we want to query
|
||||
// Prefix -> fetch prefix
|
||||
// _ -> fetch neighbors and routes
|
||||
lookupPrefix := decoders.MaybePrefix(q)
|
||||
|
||||
// Measure response time
|
||||
t0 := time.Now()
|
||||
|
||||
// Perform query
|
||||
var routes api.LookupRoutes
|
||||
if lookupPrefix {
|
||||
/*
|
||||
q, err = validatePrefixQuery(q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
routes, err = s.routesStore.LookupPrefix(ctx, q, filtersApplied)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -57,3 +57,24 @@ func apiQueryFilterNextHopGateway(
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// QueryString wraps the q parameter from the query.
|
||||
// Extract the value and additional filters from the string
|
||||
type QueryString string
|
||||
|
||||
// ExtractFilters separates query and filters from string.
|
||||
func (q QueryString) ExtractFilters() (string, []string) {
|
||||
tokens := strings.Split(string(q), " ")
|
||||
query := []string{}
|
||||
filters := []string{}
|
||||
|
||||
for _, t := range tokens {
|
||||
if strings.HasPrefix(t, "#") {
|
||||
filters = append(filters, t)
|
||||
} else {
|
||||
query = append(query, t)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(query, " "), filters
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user