// 2019 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 networkarchitect import ( "fmt" "log" "os" "time" "github.rackspace.com/gorax/clients/migrationAPI" "github.com/jokelyo/argparse" "github.com/360EntSecGroup-Skylar/excelize" "github.com/jedib0t/go-pretty/v6/table" "github.com/sirupsen/logrus" "github.rackspace.com/DCM/gomigrate/core" "github.rackspace.com/DCM/gomigrate/shared" "github.rackspace.com/gorax/gokeys" ) const httpTimeout = 180 * time.Second var ( moveGroup *[]string csvFlag *bool ) // SubPlugin function will return a argparse.Command type back to the parent parser // nolint:typecheck func SubPlugin(p *argparse.Command) core.Plugin { // Create a command and argument for the network architect audit cmd := p.NewCommand("network_architect", "Audits Network Architect for consistency") moveGroup = shared.ArgMoveGroups(cmd) csvFlag = shared.ArgFlag(cmd, "csv", "Create a CSV results file") return core.Plugin{CMD: cmd, Func: pluginFunc} } // pluginFunc function will prompt for a list of move-groups, send the MG(s) to the NA audit // for a listing of devices to run the network architect audit call on. // It should then determine where there are discrepancies between switch config and // NA information func pluginFunc(cfg gokeys.Settings) { // Pull an identity token from the users gokeys keychain identKey, err := cfg.Identity(false) if err != nil { log.Fatalf("gokeys.Identity:%s", err) } if moveGroup == nil { log.Fatal("must specify a move-group or device") } c := migrationAPI.New(identKey.Password, false) naResp := make([]migrationAPI.AuditResponse, 0) for _, moveGroup := range *moveGroup { resp, err := c.AuditNetworkArchitect( shared.GetCollection(moveGroup), migrationAPI.NewOptions().SetHTTPTimeout(httpTimeout).SetMoveGroup(moveGroup), ) if err != nil { logrus.Fatal(err) } naResp = append(naResp, *resp) pPrint(resp) } if *csvFlag { if err := csv(naResp, "Network_Architect_Audit", "Sheet1"); err != nil { logrus.Error(err) } } } // pPrint func will take a structured type of response data and render a table // output to stdout. func pPrint(data *migrationAPI.AuditResponse) { t := table.NewWriter() t.SetOutputMirror(os.Stdout) t.AppendHeader(table.Row{"ID", "Account", "Current Location", "Migration Location", "Reason", "Team Name"}) for _, d := range data.Data { t.AppendRow(table.Row{d.ID, d.AccountID, d.Current, d.Destination, d.Reason, d.TeamName}) } t.SortBy([]table.SortBy{{Name: "Account", Mode: table.AscNumeric}}) t.Render() } // Function to create a CSV of data on demand func csv(results []migrationAPI.AuditResponse, fileName, sheetName string) error { file := excelize.NewFile() index := file.NewSheet(sheetName) file.SetCellValue(sheetName, "A1", "Device ID") file.SetCellValue(sheetName, "B1", "Account Number") file.SetCellValue(sheetName, "C1", "Current Location") file.SetCellValue(sheetName, "D1", "Migration Location") file.SetCellValue(sheetName, "E1", "Reason") row := 2 for _, r := range results { for _, v := range r.Data { file.SetCellValue(sheetName, fmt.Sprintf("A%d", row), v.ID) file.SetCellValue(sheetName, fmt.Sprintf("B%d", row), v.AccountID) file.SetCellValue(sheetName, fmt.Sprintf("C%d", row), v.Current) file.SetCellValue(sheetName, fmt.Sprintf("D%d", row), v.Destination) file.SetCellValue(sheetName, fmt.Sprintf("E%d", row), v.Reason) row = row + 1 } } file.SetActiveSheet(index) if err := file.SaveAs(fmt.Sprintf("%s.xlsx", fileName)); err != nil { return err } logrus.Infof("csv file written to %s", fmt.Sprintf("%s.xlsx", fileName)) return nil }