// 2020 Rackspace, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package netparse import ( "fmt" "net" "regexp" "sort" "strconv" "strings" ) var ( vlanFullRE = regexp.MustCompile(`(?i)(v|)lan\s+([0-9]+)\n((.|\n)*?)(\n v|\nv|$)`) vlanNameRE = regexp.MustCompile(`(?i)name\s+(\S+)`) vlanRangeRE = regexp.MustCompile(`(?i)\n(|\s+)vlan\s+([0-9]+(,|-).*?)(\s+\n|\n)`) vlanOnlyRE = regexp.MustCompile(`(?i)vlan\s+([0-9]+)(\s+\n!|\n!|\s+\n\s+!)`) ) // Vlan type represents the items we will parse the vlan config type Vlan struct { Vlan int `json:"vlan_number"` Name string `json:"name"` Zone string `json:"zone"` Shut bool `json:"shutdown"` SVIConfig *IfcConfig `json:"svi_config"` SVIDevices []string `json:"svi_devices"` MigrationZone string `json:"-"` } // VlanConfigParse function serves as the parser for the vlan configuration portion // of a switch config. The 's string' input can be just the vlan configuration, or the // entire switch configuration. func VlanConfigParse(s string) []Vlan { retVal := make([]Vlan, 0) vlanMap := make(map[int]Vlan) for _, v := range vlanFullRE.FindAllStringSubmatch(s, -1) { vlan := Atoi(v[2]) if _, ok := vlanMap[vlan]; !ok { vlanMap[vlan] = Vlan{ Vlan: vlan, Name: regexpStrOrEmpty(vlanNameRE, v[3]), SVIDevices: make([]string, 0), } } } for _, v := range vlanFullRE.FindAllStringSubmatch(s, -1) { vlan := Atoi(v[2]) if _, ok := vlanMap[vlan]; !ok { vlanMap[vlan] = Vlan{ Vlan: vlan, Name: regexpStrOrEmpty(vlanNameRE, v[3]), SVIDevices: make([]string, 0), } } } for _, v := range vlanRangeRE.FindAllStringSubmatch(s, -1) { for _, nv := range strings.Split(v[2], ",") { if nv == "1-4094" { continue } if strings.Contains(nv, "-") { hiLow := strings.Split(nv, "-") low := Atoi(hiLow[0]) hi := Atoi(hiLow[1]) if low != -9999 && hi != -9999 { for x := low; x <= hi; x++ { if _, ok := vlanMap[x]; !ok { vlanMap[x] = Vlan{Vlan: x, Name: "UNAMED"} } } continue } } if tv := Atoi(nv); tv != -9999 { if _, ok := vlanMap[tv]; !ok { vlanMap[tv] = Vlan{ Vlan: tv, Name: "UNAMED", SVIDevices: make([]string, 0), } } } } } for _, v := range vlanOnlyRE.FindAllStringSubmatch(s, -1) { vlan := Atoi(v[1]) if _, ok := vlanMap[vlan]; !ok { vlanMap[vlan] = Vlan{ Vlan: vlan, Name: "UNAMED", SVIDevices: make([]string, 0), } } } if _, ok := vlanMap[1]; !ok { vlanMap[1] = Vlan{ Vlan: 1, Name: "default", SVIDevices: make([]string, 0), } } keys := make([]int, 0) for k := range vlanMap { keys = append(keys, k) } sort.Ints(keys) for _, k := range keys { retVal = append(retVal, vlanMap[k]) } return retVal } // SegmentName method will build out a common name that would be used to describe this // vlan based on the vlan info and SVI config, if exist. func (v *Vlan) SegmentName() string { if v.SVIConfig != nil { for _, svi := range v.SVIConfig.IPAddress { if !svi.Secondary { return fmt.Sprintf("Vlan%d_%s", v.Vlan, svi.CIDR()) } } } if v.Name == "" { return fmt.Sprintf("VL%d_INSIDE", v.Vlan) } nameLen := 12 - len(strconv.Itoa(v.Vlan)) if len(v.Name) > nameLen { v.Name = v.Name[:nameLen] } return fmt.Sprintf("VL%d_%s", v.Vlan, strings.ToUpper(v.Name)) } // CIDR will convert the network net.IP object, and the subnet string into // a string CIDR func (i *IP) CIDR() string { nm := i.Netmask.To4() ones, _ := net.IPv4Mask(nm[0], nm[1], nm[2], nm[3]).Size() return fmt.Sprintf("%s/%d", i.Address.String(), ones) }