// 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 info import ( "fmt" "log" "net" "strings" "github.com/labstack/gommon/color" "github.com/sirupsen/logrus" "github.rackspace.com/DCM/gomigrate/shared" "github.rackspace.com/DCM/migui/pkg/notes" "github.rackspace.com/DCM/tools" "github.rackspace.com/gorax/clients/dcxAutohost" "github.rackspace.com/gorax/gokeys" ) type ( client struct { devices []int coreToken string identityToken string acctNoteMap map[int][]notes.Note } result struct { Dev tools.Device Notes []notes.Note Maps dcxAutohost.DevicePortMap MigType string } ) func pluginInit(cfg gokeys.Settings) *client { // Set all the key from the arg parser c := client{ devices: *argD, acctNoteMap: make(map[int][]notes.Note), } // Pull an identity token from the users gokeys keychain key, err := cfg.Identity(false) if err != nil { log.Fatal(err) } c.identityToken = key.Password // Pull a core token from the gokeys keychain coreKey, err := cfg.Core(false) if err != nil { log.Fatal(err) } c.coreToken = coreKey.Password return &c } // pluginFunc function is executed from the gomigrate caller func pluginFunc(cfg gokeys.Settings) { c := pluginInit(cfg) if len(c.devices) == 0 { logrus.Fatal("-d required: must provide at least one device") } collection := shared.GetDeviceCollection(c.devices[0], c.coreToken) if collection == "" { logrus.Fatalf("invalid collection name") } metadata, err := shared.GetMetadataFromCollection(collection, c.coreToken) if err != nil { logrus.Fatal(err) } // Get the device list from a list of move-groups or devices. devices, err := shared.MongoDevices([]string{}, nil, c.devices, collection, c.coreToken, make(map[string]interface{})) if err != nil { logrus.Fatal(err) } // Stop here if we did not find any devices. if len(devices) == 0 { logrus.Fatalf("no DB devices found in devices:(%v)", c.devices) } for i, d := range shared.SortDevices(devices) { r := result{Dev: d, Notes: c.customerNotes(d.Account.Number), MigType: metadata.Type} if r.Maps, err = shared.PortMapsForDevice(d.ID, c.identityToken); err != nil { logrus.Errorf("[%d] PortMaps:%s", d.ID, err) } pPrint(r) if i+1 < len(devices) { fmt.Println() } } } // pPrint func will take a structured type of response data and render a table // output to stdout. func pPrint(data result) { fmt.Println(fmt.Sprintf("Device \t\t %s:%s", color.Cyan(data.Dev.ID), color.Cyan(data.Dev.Device.Type))) if data.Dev.Linked.ID != 0 { fmt.Println(fmt.Sprintf("Linked ID \t %s", color.Cyan(data.Dev.Linked.ID))) } fmt.Println(fmt.Sprintf("Hostname \t %s", color.Cyan(data.Dev.Device.HostName))) fmt.Println(fmt.Sprintf("Primary IP \t %s", color.Cyan(data.Dev.Device.PrimaryIP))) fmt.Println(fmt.Sprintf("Status \t\t %s", color.Cyan(data.Dev.Device.Status))) fmt.Println(fmt.Sprintf("Account \t %s:%s", color.Cyan(data.Dev.Account.Number), color.Cyan(data.Dev.Account.Name))) fmt.Println(fmt.Sprintf("Move-Group \t %s", color.Cyan(data.Dev.MoveGroup))) fmt.Println(fmt.Sprintf("Zone \t\t %s", color.Cyan(data.Dev.Network.Public.Zone))) fmt.Println(fmt.Sprintf("Env \t\t %s", color.Cyan(data.Dev.Environment.Number))) printIPs(data.Dev.Network.Address.DracIPs, "Drac") printIPs(data.Dev.Network.Address.ServiceNetIPs, "ServiceNet") if len(data.Maps.PortMaps) > 0 { fmt.Println(color.Green("Port Assignments Mapped:")) fmt.Println(fmt.Sprintf("\t%-50s%s", "Source Ports", "Migration Ports")) for _, p := range shared.SortPortMaps(data.Maps.PortMaps) { fmt.Println(color.Italic(fmt.Sprintf("\t%-50s%s", data.portString(p.Source, data.Maps.SourceDC), data.portString(p.Migration, data.Maps.MigrationDC), ))) } for _, ums := range strings.Split(data.Maps.UnmappedSourcePorts, ",") { if strings.TrimSpace(ums) == "" { continue } fmt.Println(color.Italic(fmt.Sprintf("\t%-50sUnmapped Migration Port", ums))) } for _, umd := range strings.Split(data.Maps.UnmappedMigrationPorts, ",") { if strings.TrimSpace(umd) == "" { continue } fmt.Println(color.Italic(fmt.Sprintf("\t%-50s%s", "Unmapped Source Port", umd))) } } else if len(data.Dev.Environment.Current.SwitchPorts) > 0 || len(data.Dev.Environment.Migration.SwitchPorts) > 0 { fmt.Println(color.Red("Port Assignments Unmapped:")) fmt.Println(fmt.Sprintf("\t%-50s%s", "Source Ports", "Migration Ports")) for i, p := range data.Dev.Environment.Current.SwitchPorts { mp := "" if len(data.Dev.Environment.Migration.SwitchPorts) > i { mp = data.Dev.Environment.Migration.SwitchPorts[i] } fmt.Println(color.Italic(fmt.Sprintf("\t%-50s%s", p, mp))) } clen := len(data.Dev.Environment.Current.SwitchPorts) mlen := len(data.Dev.Environment.Migration.SwitchPorts) if clen < mlen && mlen != 0 { if clen == 0 { clen = 1 } for _, p := range data.Dev.Environment.Migration.SwitchPorts[clen-1:] { fmt.Println(color.Italic(fmt.Sprintf("\t%-50s%s", "", p))) } } } if len(data.Dev.Network.Vlans) > 0 { fmt.Println(fmt.Sprintf("Vlan Mappings:")) for _, v := range data.Dev.Network.Vlans { fmt.Println(fmt.Sprintf("\t%s:%s \t %s:%s", color.Italic(v.Source.ZoneName), color.Italic(v.Source.VlanNumber), color.Italic(v.Migration.ZoneName), color.Italic(v.Migration.VlanNumber), )) } } if len(data.Notes) > 0 { fmt.Println(color.Yellow("Customer Notes:")) for _, n := range data.Notes { if strings.EqualFold(n.Status, "complete") { continue } if strings.EqualFold(n.Status, "closed") { continue } fmt.Println(color.Yellow(fmt.Sprintf("\t%-12s%-10s%s", n.Status, n.Team, n.Summary))) } } } func printIPs(ip tools.IPMapping, t string) { if len(ip.Source) == 0 && len(ip.Migration) == 0 { return } fmt.Println(color.Magenta(fmt.Sprintf("%s IP Assignments:", t))) fmt.Println(fmt.Sprintf("\t%-25s%s", "Source", "Migration")) for i, a := range ip.Source { mig := "" if len(ip.Migration) > i { mig = ipToString(ip.Migration[i]) } fmt.Println(fmt.Sprintf("\t%-25s%s", ipToString(a), mig)) } if len(ip.Source) < len(ip.Migration) { clen := len(ip.Source) if clen == 0 { clen = 1 } for _, a := range ip.Migration[clen-1:] { fmt.Println(fmt.Sprintf("\t%-25s%s", "", ipToString(a))) } } } func ipToString(ip tools.IPAddressDetails) string { addr := net.ParseIP(ip.Netmask).To4() sz, _ := net.IPv4Mask(addr[0], addr[1], addr[2], addr[3]).Size() return fmt.Sprintf("%s/%d:%d", ip.IPAddress, sz, ip.Vlan) } func (c *client) customerNotes(account int) []notes.Note { if _, ok := c.acctNoteMap[account]; ok { return c.acctNoteMap[account] } var err error c.acctNoteMap[account], err = shared.MongoNotes([]int{account}, c.coreToken) if err != nil { logrus.Errorf("[%d] Unable to fetch notes", account) c.acctNoteMap[account] = []notes.Note{} } return c.acctNoteMap[account] } func (r *result) portString(p dcxAutohost.PortMapDetail, dc string) string { return fmt.Sprintf("%s.%s[%s] (%s)", p.Switch.Number, dc, func() string { if r.MigType != tools.MigTypeRackspace && p.Name != "" { return p.Name } return fmt.Sprintf("%d", p.PortNumber) }(), p.SwitchPortType, ) }