blob: 8157909a7be6487918bfdb89412509d506b41865 [file] [log] [blame]
// Copyright 2017 The LUCI Authors.
//
// 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 config
import (
"context"
"net"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/config"
"go.chromium.org/luci/config/cfgclient"
"go.chromium.org/luci/config/validation"
configPB "go.chromium.org/luci/machine-db/api/config/v1"
"go.chromium.org/luci/machine-db/appengine/model"
)
// vlansFilename is the name of the config file enumerating VLANs.
const vlansFilename = "vlans.cfg"
// vlanMaxID is the highest ID a VLAN may have.
const vlanMaxID = 65535
// vlanMinCIDRBlockSuffix is the minimum suffix of a CIDR block.
// This limits the length of CIDR blocks. Each block contains 2^(32 - suffix) IP addresses.
const vlanMinCIDRBlockSuffix = 18
// importVLANs fetches, validates, and applies VLAN configs.
func importVLANs(c context.Context, configSet config.Set) error {
vlan := &configPB.VLANs{}
metadata := &config.Meta{}
if err := cfgclient.Get(c, configSet, vlansFilename, cfgclient.ProtoText(vlan), metadata); err != nil {
return errors.Annotate(err, "failed to load %s", vlansFilename).Err()
}
logging.Infof(c, "Found %s revision %q", vlansFilename, metadata.Revision)
ctx := &validation.Context{Context: c}
ctx.SetFile(vlansFilename)
validateVLANs(ctx, vlan)
if err := ctx.Finalize(); err != nil {
return errors.Annotate(err, "invalid config").Err()
}
if err := model.EnsureVLANs(c, vlan.Vlan); err != nil {
return errors.Annotate(err, "failed to ensure VLANs").Err()
}
if err := model.EnsureIPs(c, vlan.Vlan); err != nil {
return errors.Annotate(err, "failed to ensure IP addresses").Err()
}
return nil
}
// validateVLANs validates vlans.cfg.
func validateVLANs(c *validation.Context, cfg *configPB.VLANs) {
// VLAN IDs must be unique.
// Keep records of ones we've already seen.
vlans := make(map[int64]struct{}, len(cfg.Vlan))
for _, vlan := range cfg.Vlan {
switch _, ok := vlans[vlan.Id]; {
case vlan.Id < 1:
c.Errorf("VLAN ID %d must be positive", vlan.Id)
case vlan.Id > vlanMaxID:
c.Errorf("VLAN ID %d must not exceed %d", vlan.Id, vlanMaxID)
case ok:
c.Errorf("duplicate VLAN %d", vlan.Id)
}
vlans[vlan.Id] = struct{}{}
c.Enter("VLAN %d", vlan.Id)
_, subnet, err := net.ParseCIDR(vlan.CidrBlock)
if err != nil {
c.Errorf("invalid CIDR block")
} else {
ones, _ := subnet.Mask.Size()
if ones < vlanMinCIDRBlockSuffix {
c.Errorf("CIDR block suffix must be at least %d", vlanMinCIDRBlockSuffix)
}
}
c.Exit()
// TODO(smut): Check that CIDR blocks are disjoint.
}
}