// 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 failover will assign CORE failover devices based on myadmin package failover import ( "fmt" "net/http" "strconv" "strings" "github.com/sirupsen/logrus" "github.rackspace.com/DCM/migration/pkg/collection/mongo" "github.rackspace.com/gorax/clients/goCoreUI" "github.rackspace.com/gorax/clients/goctk" "github.rackspace.com/gorax/clients/mydp" raxidentity "github.rackspace.com/gorax/clients/raxIdentity" ) var netDevicePlatforms = []string{ "Load-Balancer", "Cisco ASA", "Firewall - Cisco", "Cisco Firepower", "Palo Alto", "Citrix NetScaler", "Brocade ADX", } type ( // Client contains all the data types needed to // initialize and use this package. Client struct { ctk *goctk.Client dp *mydp.Client mgo *mongo.Client token string } // Result is the struct that will get return back to the HTTP handler to // marshal as a json response. Result struct { ID int `json:"device_number"` FailoverID int `json:"failover_device_number"` Message string `json:"message"` } ) // NewContext is the initialization method for this package that will populate an // instance of the Client struct for subsequent calls to its methods. func NewContext(r *http.Request) *Client { return &Client{ ctk: goctk.FromContext(r.Context()), dp: mydp.FromContext(r.Context()), token: raxidentity.FromContext(r.Context()).APIKey(), mgo: mongo.NewContext(r), } } // Run function is the main entry point for the failover package. func (c *Client) Run(device int) (Result, error) { r := Result{ID: device} ctkDevice, err := c.CTKDevice(device) if err != nil { r.Message = err.Error() return r, fmt.Errorf("CTKDevice:%s", err) } if ctkDevice.SKUs.SubAttributes.Device.Value == "" { r.Message = "MyDP device association does not exist." return r, fmt.Errorf("CTKDevice:%s", r.Message) } dpDeviceID, err := strconv.Atoi(ctkDevice.SKUs.SubAttributes.Device.Value) if err != nil { r.Message = "[admin.mydatapipe.com] device id is not a valid integer" return r, fmt.Errorf("%s:%s", r.Message, err) } dpDevice, err := c.dp.DeviceByDeviceID(dpDeviceID) if err != nil { r.Message = err.Error() return r, fmt.Errorf("MyDPDevice:%s", r.Message) } foDevice, err := c.getSpecificDevice(*dpDevice) if err != nil { r.Message = err.Error() return r, fmt.Errorf("MyDPDevice:%s", r.Message) } // No error here because the device is not part of an HA pair. if foDevice == "" { r.Message = "Device does not have a failover assigned in admin.mydatapipe.com" return r, nil } foDeviceID, err := strconv.Atoi(foDevice) if err != nil { r.Message = "[admin.mydatapipe.com] failover device id is not a valid integer" return r, fmt.Errorf("%s:%s", r.Message, err) } logrus.Infof("[%d => %d] has failover ID of %d", device, dpDeviceID, foDeviceID) dpFailoverDevice, err := c.dp.DeviceByDeviceID(foDeviceID) if err != nil { r.Message = err.Error() return r, fmt.Errorf("MyDPDevice:%s", r.Message) } if dpFailoverDevice.RSCoreDeviceID == "" { r.Message = "MyDP failover device does not have a Rackspace CORE ID defined" return r, fmt.Errorf("MyDPFailoverDevice:%s", r.Message) } coreFoDeviceID, err := strconv.Atoi(dpFailoverDevice.RSCoreDeviceID) if err != nil { r.Message = "[admin.mydatapipe.com] failover device id is not a valid integer" return r, fmt.Errorf("%s:%s", r.Message, err) } r.FailoverID = coreFoDeviceID resMessage, err := c.UpdateCore(ctkDevice, coreFoDeviceID) if err != nil { r.Message = err.Error() return r, fmt.Errorf("UpdateCORE:%s", r.Message) } r.Message = resMessage return r, nil } // CTKDevice will fetch the device info from CORE func (c *Client) CTKDevice(device int) (goctk.ComputerQueryInfo, error) { res, err := c.ctk.ComputerQuery(device) if err != nil { return goctk.ComputerQueryInfo{}, fmt.Errorf("CTKDevice:%s", err) } logrus.Infof("[%d] Assigned platform: %s", device, res.PlatformModel) for _, ndPlatform := range netDevicePlatforms { if strings.Contains(strings.ToLower(res.PlatformModel), strings.ToLower(ndPlatform)) { return *res, nil } } return goctk.ComputerQueryInfo{}, fmt.Errorf("[%d] %s Unknown Platform", device, res.PlatformModel) } // UpdateCore will assign the sku and failover device for the given CORE device. func (c *Client) UpdateCore(device goctk.ComputerQueryInfo, foDevice int) (string, error) { if !c.skuExist(device, 103900) { ss, err := c.ctk.SetSkuforComputer(device.Number, 103900, 435) if err != nil { return "", err } logrus.Debugf("Sku needed update in core for device %d. Success %t", device.Number, ss.Success) } if device.FailoverDevice != foDevice { if _, err := goCoreUI.SetFailoverforComputer(device.Number, foDevice, c.token); err != nil { return "", err } logrus.Debugf("Updated %d failover device to %d.", device.Number, foDevice) return fmt.Sprintf("CORE failover [%d] assigned successfully", foDevice), nil } return fmt.Sprintf("CORE failover [%d] already assigned. No Action", foDevice), nil } func (c *Client) skuExist(dev goctk.ComputerQueryInfo, sku int) bool { for i := range dev.Skus { if sku == dev.Skus[i] { return true } } return false } func mustString(s *string) string { if s == nil { return "" } return *s }