// 2018 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 shutdown import ( "errors" "fmt" "regexp" "strconv" "strings" "github.rackspace.com/gorax/asaapi/pkg/asaModels" ) type asa struct { Interfaces []*asaModels.InterfaceConfig Model string Opts Options RawConfig string StartupConfig string UpdatedConfig string } func (d *asa) modify() string { d.UpdatedConfig = d.RawConfig d.modifyPublicInfo() d.modifyVlans() return d.UpdatedConfig } func (d *asa) modifyPublicInfo() { if d.Opts.PublicInfo == nil { return } re := regexp.MustCompile( fmt.Sprintf( `ip address %s %s(\b)`, d.Opts.PublicInfo.IP.Old, d.Opts.PublicInfo.Netmask.Old, )) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`ip address %s %s${1}`, d.Opts.PublicInfo.IP.New, d.Opts.PublicInfo.Netmask.New), ) re = regexp.MustCompile(fmt.Sprintf(`route (.*?) %s(\b)`, d.Opts.PublicInfo.Gateway.Old)) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`route ${1} %s${2}`, d.Opts.PublicInfo.Gateway.New), ) if d.Opts.PublicInfo.StandbyIP.New != "" { re = regexp.MustCompile(fmt.Sprintf(`ip address (.*?) standby %s(\b)`, d.Opts.PublicInfo.StandbyIP.Old)) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`ip address ${1} standby %s${2}`, d.Opts.PublicInfo.StandbyIP.New), ) } } func (d *asa) modifyVlans() { if d.Opts.VlanMap == nil { return } if strings.Contains(d.Model, "5505") { d.modifyVlans5505() return } for _, i := range d.Interfaces { if i == nil || i.SubInt == nil { continue } for _, subI := range i.SubInt { if subI == nil || subI.Nameif == nil || d.rackconnectInt(*subI.Nameif) || subI.Vlan == nil { continue } oldVlan := strconv.Itoa(*subI.Vlan) newVlan := d.Opts.VlanMap[oldVlan] if newVlan == "" { continue } re := regexp.MustCompile(fmt.Sprintf(`(\n\s*)interface %s\.%s(\b)`, i.Name, oldVlan)) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`${1}interface %s.%s${2}`, i.Name, newVlan), ) re = regexp.MustCompile(fmt.Sprintf(`(?s)(\n\s*interface %s.*?) vlan %s(\b)`, i.Name, oldVlan)) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`${1} vlan %s${2}`, newVlan), ) } } } func (d *asa) modifyVlans5505() { for _, i := range d.Interfaces { if i == nil || i.Nameif == nil || d.rackconnectInt(*i.Nameif) || d.rackconnectInt(*i.Description) { continue } re := regexp.MustCompile(`^Vlan(\d+)$`) match := re.FindStringSubmatch(i.Name) if len(match) == 2 { oldVlan := match[1] newVlan := d.Opts.VlanMap[oldVlan] if newVlan == "" { continue } re = regexp.MustCompile(fmt.Sprintf(`interface Vlan%s(\b)`, oldVlan)) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`interface Vlan%s${1}`, newVlan), ) } if len(i.TrunkAllowed) > 0 { vlans := make([]string, 0) for _, v := range i.TrunkAllowed { oldVlan := strconv.Itoa(v) newVlan := d.Opts.VlanMap[oldVlan] if newVlan == "" { vlans = append(vlans, oldVlan) continue } vlans = append(vlans, newVlan) } re = regexp.MustCompile( fmt.Sprintf(`(?s)(interface %s.*?switchport trunk allowed vlan) \S+(\b)`, i.Name), ) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`${1} %s${2}`, strings.Join(vlans, ",")), ) } else if i.AccessVlan != nil { oldVlan := strconv.Itoa(*i.AccessVlan) newVlan := d.Opts.VlanMap[oldVlan] if newVlan == "" { continue } re = regexp.MustCompile( fmt.Sprintf(`(?s)(interface %s.*?switchport access vlan) %s(\b)`, i.Name, oldVlan), ) d.UpdatedConfig = re.ReplaceAllString( d.UpdatedConfig, fmt.Sprintf(`${1} %s${2}`, newVlan), ) } } } func (d *asa) rackconnectInt(name string) bool { return strings.Contains(strings.ToLower(name), "rackconnect") || strings.Contains(strings.ToLower(name), "hybridcontrol") } func (d *asa) verify() error { if err := d.verifyPublicInfo(); err != nil { return err } if err := d.verifyVlans(); err != nil { return err } return nil } func (d *asa) verifyPublicInfo() error { if d.Opts.PublicInfo == nil { return nil } re := regexp.MustCompile( fmt.Sprintf( `ip address %s %s(\b)`, d.Opts.PublicInfo.IP.New, d.Opts.PublicInfo.Netmask.New, ), ) if !re.MatchString(d.StartupConfig) { return errors.New("verify(): failure matching on updated public IP") } return nil } func (d *asa) verifyVlans() error { if d.Opts.VlanMap == nil { return nil } if strings.Contains(d.Model, "5505") { return d.verifyVlans5505() } vlans := make([]string, 0) for o := range d.Opts.VlanMap { re := regexp.MustCompile(fmt.Sprintf(`(?s)\n\s*interface \S+\s+vlan %s\b`, o)) if re.MatchString(d.RawConfig) { vlans = append(vlans, o) } } for _, oldVlan := range vlans { re := regexp.MustCompile(fmt.Sprintf(`(?s)\n\s*interface \S+\s+vlan %s\b`, d.Opts.VlanMap[oldVlan])) if !re.MatchString(d.StartupConfig) { return fmt.Errorf("verify(): failure matching on vlan %s", d.Opts.VlanMap[oldVlan]) } } return nil } func (d *asa) verifyVlans5505() error { vlans := make([]string, 0) for o := range d.Opts.VlanMap { re := regexp.MustCompile(fmt.Sprintf(`Vlan%s(\b)`, o)) if re.MatchString(d.RawConfig) { vlans = append(vlans, o) } } for _, oldVlan := range vlans { re := regexp.MustCompile(fmt.Sprintf(`Vlan%s(\b)`, d.Opts.VlanMap[oldVlan])) if !re.MatchString(d.StartupConfig) { return fmt.Errorf("verify(): failure matching on vlan %s", d.Opts.VlanMap[oldVlan]) } } return nil }