blob: 3b864d7efa34ed68a783df4adc6030d48932e922 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package util
import (
"fmt"
"strings"
crimsoncommon "go.chromium.org/luci/machine-db/api/common/v1"
crimsonconfig "go.chromium.org/luci/machine-db/api/config/v1"
"go.chromium.org/luci/machine-db/api/crimson/v1"
ufspb "infra/unifiedfleet/api/v1/models"
)
// ToChromeMachines converts crimson machines to UFS format.
func ToChromeMachines(old []*crimson.Machine, machineToNics map[string][]string, machineToDracs map[string]string) []*ufspb.Machine {
newObjects := make([]*ufspb.Machine, len(old))
for i, o := range old {
newObjects[i] = &ufspb.Machine{
// Temporarily use existing display name as browser machine's name instead of serial number/assettag
Name: o.Name,
Location: toLocation(o.Rack, o.Datacenter),
SerialNumber: o.GetServiceTag(),
Device: &ufspb.Machine_ChromeBrowserMachine{
ChromeBrowserMachine: &ufspb.ChromeBrowserMachine{
// RpmInterface is not available for browser machine.
// KvmInterface is currently attached to rack.
// NetworkDeviceInterface is attached to the nics.
DisplayName: o.Name,
ChromePlatform: FormatResourceName(o.Platform),
Description: o.GetDescription(),
DeploymentTicket: o.DeploymentTicket,
},
},
Realm: BrowserLabAdminRealm,
ResourceState: ToState(o.GetState()),
}
}
return newObjects
}
func toLocation(rack, datacenter string) *ufspb.Location {
return &ufspb.Location{
Rack: rack,
Zone: ToZone(strings.ToLower(datacenter)),
}
}
// ToChromePlatforms converts platforms in static file to UFS format.
func ToChromePlatforms(oldP *crimsonconfig.Platforms) []*ufspb.ChromePlatform {
ps := oldP.GetPlatform()
newP := make([]*ufspb.ChromePlatform, len(ps))
for i, p := range ps {
newP[i] = &ufspb.ChromePlatform{
Name: FormatResourceName(p.GetName()),
Manufacturer: p.GetManufacturer(),
Description: p.GetDescription(),
}
}
return newP
}
// ToOses converts the os versions to UFS format.
func ToOses(old []*crimson.OS) []*ufspb.OSVersion {
newOSes := make([]*ufspb.OSVersion, len(old))
for i, p := range old {
newOSes[i] = &ufspb.OSVersion{
Value: p.GetName(),
Description: p.GetDescription(),
}
}
return newOSes
}
// ProcessDatacenters converts datacenters to several UFS objects
func ProcessDatacenters(dc *crimsonconfig.Datacenter) ([]*ufspb.Rack, []*ufspb.KVM, []*ufspb.Switch, []*ufspb.DHCPConfig) {
dcName := dc.GetName()
switches := make([]*ufspb.Switch, 0)
racks := make([]*ufspb.Rack, 0)
rackToKvms := make(map[string][]string, 0)
kvms := make([]*ufspb.KVM, 0)
dhcps := make([]*ufspb.DHCPConfig, 0)
for _, oldKVM := range dc.GetKvm() {
name := FormatDHCPHostname(oldKVM.GetName())
k := &ufspb.KVM{
Name: name,
MacAddress: oldKVM.GetMacAddress(),
ChromePlatform: FormatResourceName(oldKVM.GetPlatform()),
Rack: oldKVM.GetRack(),
Zone: ToZone(strings.ToLower(dcName)).String(),
ResourceState: ToState(oldKVM.GetState()),
Description: oldKVM.GetDescription(),
}
kvms = append(kvms, k)
rackName := oldKVM.GetRack()
rackToKvms[rackName] = append(rackToKvms[rackName], name)
dhcps = append(dhcps, &ufspb.DHCPConfig{
MacAddress: oldKVM.GetMacAddress(),
Hostname: name,
Ip: oldKVM.GetIpv4(),
})
}
for _, old := range dc.GetRack() {
rackName := old.GetName()
switchNames := make([]string, 0)
for _, crimsonSwitch := range old.GetSwitch() {
s := &ufspb.Switch{
Name: crimsonSwitch.GetName(),
CapacityPort: crimsonSwitch.GetPorts(),
Description: crimsonSwitch.GetDescription(),
Rack: rackName,
Zone: ToZone(strings.ToLower(dcName)).String(),
ResourceState: ToState(crimsonSwitch.GetState()),
}
switches = append(switches, s)
switchNames = append(switchNames, s.GetName())
}
// Also add the kvms which is attached to the rack in rack definitation
found := false
for _, rack := range rackToKvms[rackName] {
if rack == old.GetKvm() {
found = true
break
}
}
if !found {
rackToKvms[rackName] = append(rackToKvms[rackName], old.GetKvm())
}
r := &ufspb.Rack{
Name: rackName,
Location: toLocation(rackName, dcName),
Rack: &ufspb.Rack_ChromeBrowserRack{
ChromeBrowserRack: &ufspb.ChromeBrowserRack{},
},
ResourceState: ToState(old.GetState()),
Description: old.GetDescription(),
}
racks = append(racks, r)
}
return racks, kvms, switches, dhcps
}
// ProcessNetworkInterfaces converts nics and dracs to several UFS formats for further importing
func ProcessNetworkInterfaces(nics []*crimson.NIC, dracs []*crimson.DRAC, machines []*crimson.Machine) ([]*ufspb.Nic, []*ufspb.Drac, []*ufspb.DHCPConfig, map[string][]string, map[string]string) {
machineToNics := make(map[string][]string, 0)
machineToDracs := make(map[string]string, 0)
machineMap := make(map[string]*crimson.Machine, len(machines))
newNics := make([]*ufspb.Nic, 0)
newDracs := make([]*ufspb.Drac, 0)
dhcps := make([]*ufspb.DHCPConfig, 0)
for _, machine := range machines {
machineMap[machine.GetName()] = machine
}
for _, nic := range nics {
name := GetNicName(nic.GetName(), nic.GetMachine())
switch nic.GetName() {
case "drac":
// Use ListDrac() as the source of truth for drac
continue
default:
// zone and rack are for indexing nic table
var rack string
var zone string
machine, ok := machineMap[nic.GetMachine()]
if ok {
rack = machine.GetRack()
zone = ToZone(strings.ToLower(machine.GetDatacenter())).String()
}
// Multiple nic names, e.g. eth0, eth1, bmc
newNic := &ufspb.Nic{
Name: name,
MacAddress: nic.GetMacAddress(),
SwitchInterface: &ufspb.SwitchInterface{
Switch: nic.GetSwitch(),
PortName: Int32ToStr(nic.GetSwitchport()),
},
Rack: rack,
Zone: zone,
Machine: nic.GetMachine(),
}
newNics = append(newNics, newNic)
machineToNics[nic.GetMachine()] = append(machineToNics[nic.GetMachine()], name)
}
}
for _, drac := range dracs {
// zone and rack are for indexing drac table
var rack string
var zone string
machine, ok := machineMap[drac.GetMachine()]
if ok {
rack = machine.GetRack()
zone = ToZone(strings.ToLower(machine.GetDatacenter())).String()
}
hostname := FormatDHCPHostname(FormatResourceName(drac.GetName()))
d := &ufspb.Drac{
Name: hostname,
// Inject machine name to display name
DisplayName: GetNicName("drac", drac.GetMachine()),
MacAddress: drac.GetMacAddress(),
SwitchInterface: &ufspb.SwitchInterface{
Switch: drac.GetSwitch(),
PortName: Int32ToStr(drac.GetSwitchport()),
},
Rack: rack,
Zone: zone,
Machine: drac.GetMachine(),
}
newDracs = append(newDracs, d)
machineToDracs[drac.GetMachine()] = hostname
if ip := drac.GetIpv4(); ip != "" {
dhcps = append(dhcps, &ufspb.DHCPConfig{
MacAddress: drac.GetMacAddress(),
Hostname: hostname,
Ip: drac.GetIpv4(),
Vlan: GetBrowserLabName(Int64ToStr(drac.GetVlan())),
})
}
}
return newNics, newDracs, dhcps, machineToNics, machineToDracs
}
// ToMachineLSEs converts crimson data to UFS LSEs.
func ToMachineLSEs(hosts []*crimson.PhysicalHost, vms []*crimson.VM, machines []*crimson.Machine, platforms []*crimson.Platform) ([]*ufspb.MachineLSE, []*ufspb.VM, []*ufspb.IP, []*ufspb.DHCPConfig) {
hostToVMs := make(map[string][]*ufspb.VM, 0)
ufsVMs := make([]*ufspb.VM, 0)
ips := make([]*ufspb.IP, 0)
dhcps := make([]*ufspb.DHCPConfig, 0)
machineMap := make(map[string]*crimson.Machine, len(machines))
platformMap := make(map[string]*crimson.Platform, len(platforms))
hostToMachine := make(map[string]*crimson.Machine, len(hosts))
for _, machine := range machines {
machineMap[machine.GetName()] = machine
}
for _, p := range platforms {
platformMap[p.GetName()] = p
}
for _, h := range hosts {
hostToMachine[h.GetName()] = machineMap[h.GetMachine()]
}
for _, vm := range vms {
name := FormatDHCPHostname(vm.GetName())
var zone string
if machine, ok := hostToMachine[vm.GetHost()]; ok {
zone = ToZone(strings.ToLower(machine.GetDatacenter())).String()
}
v := &ufspb.VM{
Name: name,
OsVersion: &ufspb.OSVersion{
Value: vm.GetOs(),
},
Hostname: name,
MacAddress: parseMacFromDesp(vm.GetDescription()),
Vlan: GetBrowserLabName(Int64ToStr(vm.GetVlan())),
Ip: vm.GetIpv4(),
Zone: zone,
MachineLseId: FormatDHCPHostname(vm.GetHost()),
ResourceState: ToState(vm.GetState()),
Description: vm.GetDescription(),
DeploymentTicket: vm.GetDeploymentTicket(),
}
hostToVMs[vm.GetHost()] = append(hostToVMs[vm.GetHost()], v)
ufsVMs = append(ufsVMs, v)
ip := FormatIP(GetBrowserLabName(Int64ToStr(vm.GetVlan())), vm.GetIpv4(), false, true)
if ip != nil {
ips = append(ips, ip)
}
dhcps = append(dhcps, &ufspb.DHCPConfig{
Hostname: v.GetHostname(),
Ip: vm.GetIpv4(),
Vlan: GetBrowserLabName(Int64ToStr(vm.GetVlan())),
// No mac address found
})
}
lses := make([]*ufspb.MachineLSE, 0)
var lsePrototype string
for _, h := range hosts {
var rack string
var zone string
machine, ok := machineMap[h.GetMachine()]
if ok {
rack = machine.GetRack()
zone = ToZone(strings.ToLower(machine.GetDatacenter())).String()
}
name := FormatDHCPHostname(h.GetName())
vms := hostToVMs[name]
if len(vms) > 0 {
lsePrototype = "browser:vm"
} else {
lsePrototype = "browser:no-vm"
}
var manufacturer string
if machine.GetPlatform() != "" {
manufacturer = platformMap[machine.GetPlatform()].GetManufacturer()
}
lse := &ufspb.MachineLSE{
Name: name,
MachineLsePrototype: lsePrototype,
Hostname: name,
Machines: []string{h.GetMachine()},
Lse: &ufspb.MachineLSE_ChromeBrowserMachineLse{
ChromeBrowserMachineLse: &ufspb.ChromeBrowserMachineLSE{
VmCapacity: h.GetVmSlots(),
OsVersion: &ufspb.OSVersion{
Value: h.GetOs(),
},
VirtualDatacenter: h.GetVirtualDatacenter(),
},
},
Rack: rack,
Zone: zone,
Nic: GetNicName(h.GetNic(), h.GetMachine()),
Vlan: GetBrowserLabName(Int64ToStr(h.GetVlan())),
Ip: h.GetIpv4(),
ResourceState: ToState(h.GetState()),
Manufacturer: manufacturer,
Description: h.GetDescription(),
DeploymentTicket: h.DeploymentTicket,
}
lses = append(lses, lse)
ip := FormatIP(GetBrowserLabName(Int64ToStr(h.GetVlan())), h.GetIpv4(), false, true)
if ip != nil {
ips = append(ips, ip)
}
dhcps = append(dhcps, &ufspb.DHCPConfig{
Hostname: h.GetName(),
Ip: h.GetIpv4(),
MacAddress: h.GetMacAddress(),
Vlan: GetBrowserLabName(Int64ToStr(h.GetVlan())),
})
}
return lses, ufsVMs, ips, dhcps
}
// ToState converts crimson state to UFS state.
func ToState(state crimsoncommon.State) ufspb.State {
switch state {
case crimsoncommon.State_SERVING:
return ufspb.State_STATE_SERVING
case crimsoncommon.State_DECOMMISSIONED:
return ufspb.State_STATE_DECOMMISSIONED
case crimsoncommon.State_REPAIR:
return ufspb.State_STATE_NEEDS_REPAIR
case crimsoncommon.State_TEST:
return ufspb.State_STATE_DEPLOYED_TESTING
case crimsoncommon.State_PRERELEASE:
return ufspb.State_STATE_DEPLOYED_PRE_SERVING
case crimsoncommon.State_FREE:
return ufspb.State_STATE_READY
}
return ufspb.State_STATE_UNSPECIFIED
}
// ToLab converts the crimson lab string to UFS lab.
func ToLab(datacenter string) ufspb.Lab {
switch strings.ToLower(datacenter) {
case "atl97":
return ufspb.Lab_LAB_DATACENTER_ATL97
case "iad97":
return ufspb.Lab_LAB_DATACENTER_IAD97
case "mtv96":
return ufspb.Lab_LAB_DATACENTER_MTV96
case "mtv97":
return ufspb.Lab_LAB_DATACENTER_MTV97
case "lab01":
return ufspb.Lab_LAB_DATACENTER_FUCHSIA
default:
return ufspb.Lab_LAB_UNSPECIFIED
}
}
// ToZone converts the crimson lab string to UFS zone.
func ToZone(datacenter string) ufspb.Zone {
switch strings.ToLower(datacenter) {
case "atl97":
return ufspb.Zone_ZONE_ATL97
case "iad97":
return ufspb.Zone_ZONE_IAD97
case "mtv96":
return ufspb.Zone_ZONE_MTV96
case "mtv97":
return ufspb.Zone_ZONE_MTV97
case "lab01":
return ufspb.Zone_ZONE_FUCHSIA
case "atl":
return ufspb.Zone_ZONE_ATLANTA
default:
return ufspb.Zone_ZONE_UNSPECIFIED
}
}
// GetNicName formats a nic name with its attached machine
func GetNicName(nicName, machineName string) string {
return fmt.Sprintf("%s:%s", machineName, nicName)
}
func parseMacFromDesp(desp string) string {
if !strings.HasPrefix(desp, "MAC") {
return ""
}
mac := desp[4:]
if parsedMac, err := ParseMac(mac); err == nil {
return parsedMac
}
return ""
}