//nolint:golint package dcxAutohost import ( "encoding/json" "fmt" "net/url" "regexp" "strconv" "strings" "github.rackspace.com/gorax/libs/switchlib/swModels" "github.rackspace.com/gorax/tools/util" ) var ( switchPortRegex = regexp.MustCompile(`(?i)(^\S+)\[(\d+)\]\s+\((.*?)\)`) switchPortMigrationRegex = regexp.MustCompile(`(?i)(migration\s+port)`) cabSwitchRegex = regexp.MustCompile(`(?i)([0-9]+)-([0-9]+)($|[a-z]$)`) ) type ( // DevicesMigrations is the parent type to return detailed and parsed information regarding the // current and migration port data. DevicesMigrations struct { Device int `json:"device_number" bson:"device_number"` Current *Locations `json:"current" bson:"current"` Migration *Locations `json:"migration" bson:"migration"` ErrorList []string `json:"error_list" bson:"error_list"` PortMatch []*PortMatch `json:"migration_port_match" bson:"migration_port_match"` PortString []string `json:"migration_port_list" bson:"migration_port_list"` } // DeviceMigrationPorts is the parent type of the current and migration port data as it is returned from // the DCX API endpoint. DeviceMigrationPorts struct { Device string `json:"device_number" bson:"device_number"` Current *MigrationData `json:"current" bson:"current"` Migration *MigrationData `json:"migration" bson:"migration"` } // DeviceMigrate type represents the response data from the MigrationLocation // DCX method call DeviceMigrate struct { Container string `json:"container" bson:"container"` DataCenter string `json:"data_center" bson:"data_center"` } // DevicePortMap is the complex type to represent the port mappings for // a given device. DevicePortMap struct { Device string `json:"device_number" bson:"device_number"` Error string `json:"error" bson:"error"` PortMaps []*PortMapPair `json:"port_mapping" bson:"port_mapping"` DirectMaps []DirectMapPair `json:"direct_mapping" bson:"direct_mapping"` UnmappedMigrationPorts string `json:"unmapped_migration_ports" bson:"unmapped_migration_ports"` UnmappedSourcePorts string `json:"unmapped_source_ports" bson:"unmapped_source_ports"` SourceDC string `json:"source_dc" bson:"source_dc"` MigrationDC string `json:"migration_dc" bson:"migration_dc"` } // PortMapPair is the type to represent a single source -> destination mapping from the // map_migration_ports API call PortMapPair struct { Source PortMapDetail `json:"source" bson:"source"` Migration PortMapDetail `json:"migration" bson:"migration"` } // DirectMapPair is the type to represent a single device-to-device mapping, NOT in the DCX response. // Scholars maintain that this will enhance the port-map data. DirectMapPair struct { Source DirectMapDetail `json:"source" bson:"source"` Migration DirectMapDetail `json:"migration" bson:"migration"` } // DirectMapDetail is the details about a source or migration port-map for device-to-device mappings. DirectMapDetail struct { DevicePort string `json:"device_port" bson:"device_port"` OtherDevice OtherDevice `json:"other_device" bson:"other_device"` } // OtherDevice is the other device, DUH OtherDevice struct { DevicePort string `json:"device_port" bson:"device_port"` ID int `json:"id" bson:"id"` Type string `json:"type" bson:"type"` LinkedID string `json:"linked_id" bson:"linked_id"` } // PortMapDetail is the details about a source or migration port-map from the // map_migration_ports API call PortMapDetail struct { DeviceConfigNumber string `json:"device_configuration_number" bson:"device_configuration_number"` DeviceID int `json:"device_id" bson:"device_id"` ID int `json:"id" bson:"id"` Name string `json:"name" bson:"name"` PortNumber int `json:"port_number" bson:"port_number"` ReservationID int `json:"reservation_id" bson:"reservation_id"` // DevicePort is not a DCX response data point, but rather a field that allows us to // enrich the PortMapDetail struct. DevicePort string `json:"device_port" bson:"device_port"` Switch struct { ConfigurationNumber string `json:"configuration_number" bson:"configuration_number"` ID int `json:"id" bson:"id"` Number string `json:"number" bson:"number"` } `json:"switch" bson:"switch"` SwitchID int `json:"switch_id" bson:"switch_id"` SwitchPortType string `json:"switch_port_interface_type_name" bson:"switch_port_interface_type_name"` } MigrationData struct { DataCenter string `json:"data_center" bson:"data_center"` Container string `json:"container" bson:"container"` StartingSpace float32 `json:"starting_space" bson:"starting_space"` SwitchPorts []string `json:"switch_ports" bson:"switch_ports"` } Locations struct { DataCenter string `json:"data_center" bson:"data_center"` Container string `json:"container" bson:"container"` StartingSpace float32 `json:"starting_space" bson:"starting_space"` ParsedSwitchPorts []ParsedSwitchPort `json:"switch_ports_parsed" bson:"switch_ports_parsed"` SwitchPorts []string `json:"switch_ports" bson:"switch_ports"` } PortMatch struct { Current *MigrationPorts `json:"current_port" bson:"current_port"` Migration *MigrationPorts `json:"migration_port" bson:"migration_port"` Changes []string `json:"configuration_changes" bson:"configuration_changes"` } MigrationPorts struct { PortNumber int `json:"port_number" bson:"port_number"` PortType string `json:"port_type" bson:"port_type"` SwitchName string `json:"switch_name" bson:"switch_name"` Migration bool `json:"migration_port" bson:"migration_port"` DataCenter string `json:"data_center,omitempty" bson:"data_center,omitempty"` Cab int `json:"cab" bson:"cab"` SwitchNum int `json:"switch_num" bson:"switch_num"` Vlans []int `json:"-" bson:"-"` Config *swModels.IntConfig `json:"config,omitempty" bson:"config,omitempty"` } // ParsedSwitchPort is the deconstructed port data for a // Environment device. ParsedSwitchPort struct { PortNumber int `json:"port_number" bson:"port_number"` PortName string `json:"port_name" bson:"port_name"` PortType string `json:"port_type" bson:"port_type"` SwitchName string `json:"switch_name" bson:"switch_name"` Migration bool `json:"migration_port" bson:"migration_port"` DataCenter string `json:"data_center,omitempty" bson:"data_center,omitempty"` Cab int `json:"cab" bson:"cab"` SwitchNum int `json:"switch_num" bson:"switch_num"` VLANs []int `json:"vlans" bson:"vlans"` Config *swModels.IntConfig `json:"config,omitempty" bson:"config,omitempty"` } ) // MapMigrationPorts method will return a list of DevicePortMap types that contain the migration // port mappings for a given set of devices. // // See: https://pages.github.rackspace.com/dcx/gateway/#/devices/mapMigrationPorts func (a *Client) MapMigrationPorts(devices []int) (data []*DevicePortMap, err error) { var deviceNumbers []string for _, d := range devices { deviceNumbers = append(deviceNumbers, strconv.Itoa(d)) } body, err := a.get("/devices/map_migration_ports", url.Values{"device_numbers": []string{strings.Join(deviceNumbers, ",")}}) if err != nil { return } if err = json.Unmarshal(body, &data); err != nil { return } return } // MigrateLocation method will migrate a device in Location Manager // based on reservation information that is defined. // // See: https://pages.github.rackspace.com/dcx/gateway/#/devices/migrate func (a *Client) MigrateLocation(device int) (data DeviceMigrate, err error) { body, err := a.post(fmt.Sprintf("/devices/%d/migrate", device), nil) if err != nil { return } if err = json.Unmarshal(body, &data); err != nil { return } return } // MigrationPorts method will call the DCX API for location and migration information for // specified devices. // // See: https://pages.github.rackspace.com/dcx/gateway/#/devices/migrations func (a *Client) MigrationPorts(devices []int) ([]*DeviceMigrationPorts, error) { var deviceList []string for _, d := range devices { deviceList = append(deviceList, strconv.Itoa(d)) } body, err := a.get("/devices/migrations.json", url.Values{"device_numbers": []string{strings.Join(deviceList, ",")}}) if err != nil { return nil, err } var dm []*DeviceMigrationPorts if err = json.Unmarshal(body, &dm); err != nil { return nil, err } return dm, nil } // MigrationsByDevice method will call the DCX API for location and migration information for // specified devices, and run the switchports through parsers to setup a detailed migration type. // // See: https://pages.github.rackspace.com/dcx/gateway/#/devices/migrations func (a *Client) MigrationsByDevice(device int) (data DevicesMigrations, err error) { body, err := a.get("/devices/migrations.json", url.Values{"device_numbers": []string{strconv.Itoa(device)}}) if err != nil { return } var dm []*DeviceMigrationPorts if err = json.Unmarshal(body, &dm); err != nil { return } for _, d := range dm { n, _ := strconv.Atoi(d.Device) data = DevicesMigrations{ Device: n, Current: func() *Locations { if d.Current == nil { return &Locations{} } return &Locations{ DataCenter: d.Current.DataCenter, Container: d.Current.Container, StartingSpace: d.Current.StartingSpace, ParsedSwitchPorts: parseSwitchPorts(d.Current.SwitchPorts, d.Current.DataCenter), SwitchPorts: d.Current.SwitchPorts, } }(), Migration: func() *Locations { if d.Migration == nil { return &Locations{} } return &Locations{ DataCenter: d.Migration.DataCenter, Container: d.Migration.Container, StartingSpace: d.Migration.StartingSpace, ParsedSwitchPorts: parseSwitchPorts(d.Migration.SwitchPorts, d.Migration.DataCenter), SwitchPorts: d.Migration.SwitchPorts, } }(), } return } return } func parseSwitchPorts(switchPorts []string, dc string) (ports []ParsedSwitchPort) { for _, p := range switchPorts { if p == "" { continue } portConfig := switchPortRegex.FindStringSubmatch(p) migration := switchPortMigrationRegex.MatchString(p) if portConfig != nil { cabSwitch := cabSwitchRegex.FindStringSubmatch(portConfig[1]) ports = append(ports, ParsedSwitchPort{ SwitchName: portConfig[1], // fixSwitchName(&(*portConfig)[1]), PortNumber: func() int { if p := util.AtoiPointer(portConfig[2]); p != nil { return *p } return 0 }(), Cab: func() int { if cabSwitch == nil { return 0 } port := util.AtoiPointer(cabSwitch[1]) if port != nil { return *port } return 0 }(), SwitchNum: func() int { if cabSwitch == nil { return 0 } port := util.AtoiPointer(cabSwitch[2]) if port != nil { return *port } return 0 }(), PortType: portConfig[3], Migration: migration, DataCenter: dc, }) continue } } return }