// 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 dpfailover will assign CORE failover devices based on myadmin package dpfailover import ( "context" "encoding/json" "fmt" "net/http" "strings" "time" "github.com/labstack/gommon/color" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.rackspace.com/DCM/gomigrate/shared" "github.rackspace.com/DCM/migration/pkg/audit/failover" "github.rackspace.com/DCM/tools" "github.rackspace.com/gorax/gokeys" ) type ( // Client contains all the data types needed to // initialize and use this package. Client struct { coreToken string identityToken string } ) // pluginFunc function is executed from the gomigrate caller func pluginFunc(cfg gokeys.Settings) { c := initCfg(cfg) devices, err := c.GatherFirewallsAndLBs() if err != nil { logrus.Fatal(err) } for _, d := range devices { res, err := c.CoreFailoverAPI(d) if err != nil { logrus.Errorf("[%s]: %s", color.Yellow(d.ID), color.Red(err)) continue } msg := res.Data.Message if strings.Contains(res.Data.Message, " assigned successfully") { msg = color.Green(res.Data.Message) } logrus.Infof("[%s]: %s", color.Yellow(d.ID), msg) } } // GatherFirewallsAndLBs will fetch devices from the migration DB and filter out only the // network devices we care about. func (c *Client) GatherFirewallsAndLBs() ([]tools.Device, error) { r := make([]tools.Device, 0) devices, err := shared.MongoDevices(*argM, nil, *argD, "", c.coreToken, make(map[string]interface{})) if err != nil { return r, err } for _, d := range devices { if strings.Contains(strings.ToLower(d.Device.MakeModel), "load-balancer") { r = append(r, d) continue } if strings.Contains(strings.ToLower(d.Device.MakeModel), "cisco asa") { r = append(r, d) continue } if strings.Contains(strings.ToLower(d.Device.MakeModel), "firewall - cisco") { r = append(r, d) continue } if strings.Contains(strings.ToLower(d.Device.MakeModel), "firewall - palo alto") { r = append(r, d) continue } } return r, nil } // CoreFailoverAPIResult is the response received back from the /core_failover api type CoreFailoverAPIResult struct { Data failover.Result `json:"data"` Error string `json:"error"` } // CoreFailoverAPI function will call the migration.nsi.rackspace.com/core_failover call to // assign or reassign the failover device for net-devices. func (c *Client) CoreFailoverAPI(d tools.Device) (CoreFailoverAPIResult, error) { var ( headers = map[string]string{ "Accept": "application/json", "Connection": "close", "X-Auth-Token": c.identityToken, } result = CoreFailoverAPIResult{} fullURL = fmt.Sprintf("%s/core_failover/datapipe/%d", shared.MigAPI, d.ID) ) ctx, cancel := context.WithTimeout(context.Background(), time.Second*120) defer cancel() // Build the request req, err := http.NewRequestWithContext(ctx, http.MethodPut, fullURL, nil) if err != nil { return result, fmt.Errorf("coreFailoverAPI:NewRequest:%d:%s", d.ID, err) } for k, v := range headers { req.Header.Set(k, v) } // Send the request via a client // Do sends an HTTP request and // returns an HTTP response r, err := http.DefaultClient.Do(req) if err != nil { return result, fmt.Errorf("coreFailoverAPI:Do:%d:%s", d.ID, err) } if r != nil { defer func() { if defErr := r.Body.Close(); defErr != nil { err = errors.Wrap(defErr, "defer error") } }() } // Use json.Decode for reading streams of JSON data if err := json.NewDecoder(r.Body).Decode(&result); err != nil { return result, fmt.Errorf("coreFailoverAPI:Decode:%d:%s", d.ID, err) } if result.Error != "" { return result, errors.New(result.Error) } return result, nil } // initCfg is the initialization method for this package that will populate an // instance of the Client struct for subsequent calls to its methods. func initCfg(cfg gokeys.Settings) *Client { // Pull an identity token from the users gokeys keychain iKey, err := cfg.Identity(false) if err != nil { logrus.Fatal(err) } cKey, err := cfg.Core(false) if err != nil { logrus.Fatal(err) } return &Client{identityToken: iKey.Password, coreToken: cKey.Password} }