blob: c8fe6df0646cb2f7bd857e01ac9d27bd7c4b831e [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 controller
import (
"context"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
crimsonconfig "go.chromium.org/luci/machine-db/api/config/v1"
ufspb "infra/unifiedfleet/api/v1/models"
api "infra/unifiedfleet/api/v1/rpc"
"infra/unifiedfleet/app/model/configuration"
"infra/unifiedfleet/app/model/datastore"
"infra/unifiedfleet/app/model/registration"
"infra/unifiedfleet/app/util"
)
// ImportDatacenter imports the datacenters and the related resources.
//
// The function will return:
// * all of the results of the operations that already run
// * the first error that it meets
//
// The function will stop at the very first error.
func ImportDatacenter(ctx context.Context, dcs []*crimsonconfig.Datacenter, pageSize int) (*datastore.OpResults, error) {
allRes := make(datastore.OpResults, 0)
logging.Infof(ctx, "Importing the basic lse prototypes for browser lab")
lps := []*ufspb.RackLSEPrototype{
{
Name: "browser:normal",
PeripheralRequirements: []*ufspb.PeripheralRequirement{
{
PeripheralType: ufspb.PeripheralType_PERIPHERAL_TYPE_KVM,
Min: 0,
Max: 2,
},
{
PeripheralType: ufspb.PeripheralType_PERIPHERAL_TYPE_SWITCH,
Min: 0,
// A manual setup with no consideration, the current maximum
// switches per rack is 3 in crimson.
Max: 5,
},
},
Tags: []string{"browser", "normal"},
},
}
res, err := configuration.ImportRackLSEPrototypes(ctx, lps)
if err != nil {
return res, err
}
allRes = append(allRes, *res...)
racks := make([]*ufspb.Rack, 0)
kvms := make([]*ufspb.KVM, 0)
switches := make([]*ufspb.Switch, 0)
dhcps := make([]*ufspb.DHCPConfig, 0)
for _, dc := range dcs {
rackRes, kvmRes, switchRes, dhcpRes := util.ProcessDatacenters(dc)
racks = append(racks, rackRes...)
kvms = append(kvms, kvmRes...)
switches = append(switches, switchRes...)
dhcps = append(dhcps, dhcpRes...)
}
logging.Infof(ctx, "Got %d racks, %d kvms, %d switches, %d dhcp configs", len(racks), len(kvms), len(switches), len(dhcps))
if err := api.ValidateResourceKey(racks, "Name"); err != nil {
return nil, errors.Annotate(err, "racks has invalid chars").Err()
}
if err := api.ValidateResourceKey(kvms, "Name"); err != nil {
return nil, errors.Annotate(err, "kvms has invalid chars").Err()
}
if err := api.ValidateResourceKey(switches, "Name"); err != nil {
return nil, errors.Annotate(err, "switches has invalid chars").Err()
}
if err := api.ValidateResourceKey(dhcps, "Hostname"); err != nil {
return nil, errors.Annotate(err, "dhcps has invalid chars").Err()
}
// Please note that the importing here is not in one transaction, which
// actually may cause data incompleteness. But as the importing job
// will be triggered periodically, such incompleteness that's caused by
// potential failure will be ignored.
deleteNonExistingRacks(ctx, racks, pageSize)
logging.Infof(ctx, "Importing %d racks", len(racks))
for i := 0; ; i += pageSize {
end := util.Min(i+pageSize, len(racks))
res, err := registration.ImportRacks(ctx, racks[i:end])
allRes = append(allRes, *res...)
if err != nil {
return &allRes, err
}
if i+pageSize >= len(racks) {
break
}
}
deleteNonExistingKVMs(ctx, kvms, pageSize)
logging.Infof(ctx, "Importing %d kvms", len(kvms))
for i := 0; ; i += pageSize {
end := util.Min(i+pageSize, len(kvms))
res, err := registration.ImportKVMs(ctx, kvms[i:end])
allRes = append(allRes, *res...)
if err != nil {
return &allRes, err
}
if i+pageSize >= len(kvms) {
break
}
}
deleteNonExistingSwitches(ctx, switches, pageSize)
logging.Infof(ctx, "Importing %d switches", len(switches))
for i := 0; ; i += pageSize {
end := util.Min(i+pageSize, len(switches))
res, err := registration.ImportSwitches(ctx, switches[i:end])
allRes = append(allRes, *res...)
if err != nil {
return &allRes, err
}
if i+pageSize >= len(switches) {
break
}
}
logging.Infof(ctx, "Importing %d DHCP configs", len(dhcps))
for i := 0; ; i += pageSize {
end := util.Min(i+pageSize, len(dhcps))
res, err := configuration.ImportDHCPConfigs(ctx, dhcps[i:end])
allRes = append(allRes, *res...)
if err != nil {
return &allRes, err
}
if i+pageSize >= len(dhcps) {
break
}
}
return &allRes, nil
}
func deleteNonExistingRacks(ctx context.Context, racks []*ufspb.Rack, pageSize int) (*datastore.OpResults, error) {
resMap := make(map[string]bool)
for _, r := range racks {
resMap[r.GetName()] = true
}
rackRes, err := registration.GetAllRacks(ctx)
if err != nil {
return nil, err
}
var toDelete []string
for _, sr := range rackRes.Passed() {
s := sr.Data.(*ufspb.Rack)
if _, ok := resMap[s.GetName()]; !ok {
toDelete = append(toDelete, s.GetName())
}
}
logging.Infof(ctx, "Deleting %d non-existing racks", len(toDelete))
return deleteByPage(ctx, toDelete, pageSize, registration.DeleteRacks), nil
}
func deleteNonExistingKVMs(ctx context.Context, kvms []*ufspb.KVM, pageSize int) (*datastore.OpResults, error) {
resMap := make(map[string]bool)
for _, r := range kvms {
resMap[r.GetName()] = true
}
resp, err := registration.GetAllKVMs(ctx)
if err != nil {
return nil, err
}
var toDelete []string
for _, sr := range resp.Passed() {
s := sr.Data.(*ufspb.KVM)
if _, ok := resMap[s.GetName()]; !ok {
toDelete = append(toDelete, s.GetName())
}
}
logging.Infof(ctx, "Deleting %d non-existing kvms", len(toDelete))
allRes := *deleteByPage(ctx, toDelete, pageSize, registration.DeleteKVMs)
logging.Infof(ctx, "Deleting %d non-existing kvm-related dhcps", len(toDelete))
allRes = append(allRes, *deleteByPage(ctx, toDelete, pageSize, configuration.DeleteDHCPs)...)
return &allRes, nil
}
func deleteNonExistingSwitches(ctx context.Context, switches []*ufspb.Switch, pageSize int) (*datastore.OpResults, error) {
resMap := make(map[string]bool)
for _, r := range switches {
resMap[r.GetName()] = true
}
resp, err := registration.GetAllSwitches(ctx)
if err != nil {
return nil, err
}
var toDelete []string
for _, sr := range resp.Passed() {
s := sr.Data.(*ufspb.Switch)
if _, ok := resMap[s.GetName()]; !ok {
toDelete = append(toDelete, s.GetName())
}
}
logging.Infof(ctx, "Deleting %d non-existing switches", len(toDelete))
return deleteByPage(ctx, toDelete, pageSize, registration.DeleteSwitches), nil
}