1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
package device
import (
"errors"
"fmt"
"slices"
"strings"
"github.com/lxc/incus/v6/internal/server/instance"
"github.com/lxc/incus/v6/internal/server/instance/instancetype"
"github.com/lxc/incus/v6/internal/server/network/acl"
"github.com/lxc/incus/v6/shared/validate"
)
// nicValidationRules returns config validation rules for nic devices.
func nicValidationRules(requiredFields []string, optionalFields []string, instConf instance.ConfigReader) map[string]func(value string) error {
// Define a set of default validators for each field name.
defaultValidators := map[string]func(value string) error{
"acceleration": validate.Optional(validate.IsOneOf("none", "sriov", "vdpa")),
"name": validate.Optional(validate.IsInterfaceName, func(_ string) error { return nicCheckNamesUnique(instConf) }),
"parent": validate.IsAny,
"network": validate.IsAny,
"mtu": validate.Optional(validate.IsNetworkMTU),
"vlan": validate.IsNetworkVLAN,
"gvrp": validate.Optional(validate.IsBool),
"hwaddr": validate.IsNetworkMAC,
"host_name": validate.IsAny,
"limits.ingress": validate.IsAny,
"limits.egress": validate.IsAny,
"limits.max": validate.IsAny,
"limits.priority": validate.Optional(validate.IsUint32),
"security.mac_filtering": validate.IsAny,
"security.ipv4_filtering": validate.IsAny,
"security.ipv6_filtering": validate.IsAny,
"security.port_isolation": validate.Optional(validate.IsBool),
"ipv4.address": validate.Optional(validate.IsNetworkAddressV4),
"ipv6.address": validate.Optional(validate.IsNetworkAddressV6),
"ipv4.routes": validate.Optional(validate.IsListOf(validate.IsNetworkV4)),
"ipv6.routes": validate.Optional(validate.IsListOf(validate.IsNetworkV6)),
"boot.priority": validate.Optional(validate.IsUint32),
"ipv4.gateway": networkValidGateway,
"ipv6.gateway": networkValidGateway,
"ipv4.host_address": validate.Optional(validate.IsNetworkAddressV4),
"ipv6.host_address": validate.Optional(validate.IsNetworkAddressV6),
"ipv4.host_table": validate.Optional(validate.IsUint32),
"ipv6.host_table": validate.Optional(validate.IsUint32),
"queue.tx.length": validate.Optional(validate.IsUint32),
"ipv4.routes.external": validate.Optional(validate.IsListOf(validate.IsNetworkV4)),
"ipv6.routes.external": validate.Optional(validate.IsListOf(validate.IsNetworkV6)),
"nested": validate.IsAny,
"security.acls": validate.IsAny,
"security.acls.default.ingress.action": validate.Optional(validate.IsOneOf(acl.ValidActions...)),
"security.acls.default.egress.action": validate.Optional(validate.IsOneOf(acl.ValidActions...)),
"security.acls.default.ingress.logged": validate.Optional(validate.IsBool),
"security.acls.default.egress.logged": validate.Optional(validate.IsBool),
"security.promiscuous": validate.Optional(validate.IsBool),
"mode": validate.Optional(validate.IsOneOf("bridge", "vepa", "passthru", "private")),
"io.bus": validate.Optional(func(_ string) error { return nicCheckIsVM(instConf) }, validate.IsOneOf("virtio", "usb")),
}
validators := map[string]func(value string) error{}
for _, k := range optionalFields {
defaultValidator := defaultValidators[k]
// If field doesn't have a known validator, it is an unknown field, skip.
if defaultValidator == nil {
continue
}
// Wrap the default validator in an empty check as field is optional.
validators[k] = func(value string) error {
if value == "" {
return nil
}
return defaultValidator(value)
}
}
// Add required fields last, that way if they are specified in both required and optional
// field sets, the required one will overwrite the optional validators.
for _, k := range requiredFields {
defaultValidator := defaultValidators[k]
// If field doesn't have a known validator, it is an unknown field, skip.
if defaultValidator == nil {
continue
}
// Wrap the default validator in a not empty check as field is required.
validators[k] = func(value string) error {
err := validate.IsNotEmpty(value)
if err != nil {
return err
}
return defaultValidator(value)
}
}
return validators
}
// nicHasAutoGateway takes the value of the "ipv4.gateway" or "ipv6.gateway" config keys and returns whether they
// specify whether the gateway mode is automatic or not.
func nicHasAutoGateway(value string) bool {
if value == "" || value == "auto" {
return true
}
return false
}
// nicCheckNamesUnique checks that all the NICs in the instConf's expanded devices have a unique (or unset) name.
func nicCheckNamesUnique(instConf instance.ConfigReader) error {
seenNICNames := []string{}
for _, devConfig := range instConf.ExpandedDevices() {
if devConfig["type"] != "nic" || devConfig["name"] == "" {
continue
}
if slices.Contains(seenNICNames, devConfig["name"]) {
return fmt.Errorf("Duplicate NIC name detected %q", devConfig["name"])
}
seenNICNames = append(seenNICNames, devConfig["name"])
}
return nil
}
// nicCheckDNSNameConflict returns if instNameA matches instNameB (case insensitive).
func nicCheckDNSNameConflict(instNameA string, instNameB string) bool {
return strings.EqualFold(instNameA, instNameB)
}
// nicCheckIsVM returns if the given instance is a VM.
func nicCheckIsVM(instConf instance.ConfigReader) error {
if instConf.Type() != instancetype.VM {
return errors.New("This option is only supported on virtual machines")
}
return nil
}
|