// 2017 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 dcx import ( "encoding/json" "fmt" "strconv" "strings" "time" "github.rackspace.com/gorax/clients/dcxAutohost" dcmTools "github.rackspace.com/DCM/tools" ) // collectDCXInfo will query a DCXPS endpoint to gather environment manager information // about a given device. func (c *Client) collectDCXInfo(device dcmTools.Device) (dcmTools.EnvironmentData, error) { var ( environ dcmTools.EnvironmentData ) devEm, deErr := c.dcx.DeviceEnvironment(device.ID) if deErr == nil { environ.Name = devEm.Name environ.Number = devEm.Environment environ.GatewayDevices = devEm.Gateways } devEmMig, mdErr := c.dcx.MigrationsByDevice(device.ID) if mdErr == nil { realPorts := func(l *dcxAutohost.Locations) dcmTools.Location { if l == nil { return dcmTools.Location{} } return dcmTools.Location{ Cabinet: l.Container, DataCenter: l.DataCenter, Container: l.Container, StartingSpace: l.StartingSpace, SwitchPorts: l.SwitchPorts, PortsParsed: func() []dcmTools.PortParsed { r := make([]dcmTools.PortParsed, 0) for _, item := range l.ParsedSwitchPorts { r = append(r, dcmTools.PortParsed{ Cab: item.Cab, DataCenter: item.DataCenter, MigrationPort: item.Migration, PortNumber: item.PortNumber, PortName: item.PortName, PortType: item.PortType, SwitchName: item.SwitchName, SwitchNum: item.SwitchNum, Vlans: item.VLANs, }) } return r }(), } } if c.MigrationType == dcmTools.MigTypeRackspace { environ.Current = realPorts(devEmMig.Current) environ.Migration = realPorts(devEmMig.Migration) } else { // don't overwrite any existing environment.current data // store the current locations in DCX/CORE as the "migration" locations in our DB // we will gather the actual current env info in the /collect/from_linked_cmdb function environ.Current = device.Environment.Current migDCXPorts := realPorts(devEmMig.Migration) environ.Migration = realPorts(devEmMig.Current) environ.Migration.SwitchPorts = append(environ.Migration.SwitchPorts, migDCXPorts.SwitchPorts...) environ.Migration.PortsParsed = append(environ.Migration.PortsParsed, migDCXPorts.PortsParsed...) // We have to do this because location manager only relies on the CORE DC assignment to determine the // switch names. environ.Migration.DataCenter = c.getMigrationDC(device.ID, &environ.Migration) for i := range environ.Migration.PortsParsed { environ.Migration.PortsParsed[i].DataCenter = environ.Migration.DataCenter } } } // remove any null slice values if environ.GatewayDevices == nil { environ.GatewayDevices = make([]int, 0) } if environ.Current.SwitchPorts == nil { environ.Current.SwitchPorts = make([]string, 0) } if environ.Current.PortsParsed == nil { environ.Current.PortsParsed = make([]dcmTools.PortParsed, 0) } if environ.Migration.SwitchPorts == nil { environ.Migration.SwitchPorts = make([]string, 0) } if environ.Migration.PortsParsed == nil { environ.Migration.PortsParsed = make([]dcmTools.PortParsed, 0) } var err error // DeviceEnvironment and MigrationsByDevice calls need to run together. Each call may fail independently // of the other, however, we only need to report an error if both fail. if deErr != nil && mdErr != nil { err = fmt.Errorf("%s:%s", deErr, mdErr) } return environ, err } func (c *Client) getMigrationDC(did int, locations *dcmTools.Location) string { c.RLock() _, ok := c.dcMap[locations.DataCenter] c.RUnlock() if !ok { if r, err := c.dcx.SwitchPorts(strconv.Itoa(did)); err == nil { if len(r.SwitchPorts) != 0 { for _, s := range r.SwitchPorts { split := strings.Split(s.SwitchPortData.SwitchName, ".") if len(split) == 2 { c.Lock() c.dcMap[locations.DataCenter] = strings.ToUpper(split[1]) c.Unlock() } } } } c.RLock() dc := c.dcMap[locations.DataCenter] c.RUnlock() if dc == "" { if r, err := c.dcx.MigrationPorts([]int{did}); err == nil && r != nil { for _, d := range r { if d == nil { continue } if d.Migration == nil { continue } if len(d.Migration.SwitchPorts) != 0 { c.Lock() c.dcMap[locations.DataCenter] = strings.ToUpper(d.Migration.DataCenter) c.Unlock() } } } } } c.RLock() defer c.RUnlock() return c.dcMap[locations.DataCenter] } // attempt to get data from redis cache; ignore redis errors func (c *Client) getZoneFromDevice(switchName string) (*dcxAutohost.InfraDevice, error) { redisKey := fmt.Sprintf(redisInfraDeviceKey, switchName) naDevice := new(dcxAutohost.InfraDevice) b, err := c.r.Get(redisKey).Bytes() if err == nil { if err = json.Unmarshal(b, naDevice); err == nil { return naDevice, nil } } naDevice, err = c.dcx.InfrastructureDevice(switchName) if err != nil { return nil, err } if b, err := json.Marshal(naDevice); err == nil { _ = c.r.Set(redisKey, b, redisInfraDeviceTTL*time.Second) } return naDevice, nil }