github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/net/configurator/compute.go (about)

     1  package configurator
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/Cloud-Foundations/Dominator/lib/log"
    10  	fm_proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager"
    11  	hyper_proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    12  )
    13  
    14  func findMatchingSubnet(subnets []*hyper_proto.Subnet,
    15  	ipAddr net.IP) *hyper_proto.Subnet {
    16  	for _, subnet := range subnets {
    17  		subnetMask := net.IPMask(subnet.IpMask)
    18  		subnetAddr := subnet.IpGateway.Mask(subnetMask)
    19  		if ipAddr.Mask(subnetMask).Equal(subnetAddr) {
    20  			return subnet
    21  		}
    22  	}
    23  	return nil
    24  }
    25  
    26  func findSubnet(subnets []*hyper_proto.Subnet,
    27  	subnetId string) *hyper_proto.Subnet {
    28  	for _, subnet := range subnets {
    29  		if subnet.Id == subnetId {
    30  			return subnet
    31  		}
    32  	}
    33  	return nil
    34  }
    35  
    36  func getNetworkEntries(
    37  	info fm_proto.GetMachineInfoResponse) []fm_proto.NetworkEntry {
    38  	networkEntries := make([]fm_proto.NetworkEntry, 1,
    39  		len(info.Machine.SecondaryNetworkEntries)+1)
    40  	networkEntries[0] = info.Machine.NetworkEntry
    41  	for _, networkEntry := range info.Machine.SecondaryNetworkEntries {
    42  		networkEntries = append(networkEntries, networkEntry)
    43  	}
    44  	return networkEntries
    45  }
    46  
    47  func (netconf *NetworkConfig) addBondedInterface(name string, ipAddr net.IP,
    48  	subnet *hyper_proto.Subnet) {
    49  	netconf.bondedInterfaces = append(netconf.bondedInterfaces,
    50  		bondedInterfaceType{
    51  			name:   name,
    52  			ipAddr: ipAddr,
    53  			subnet: subnet,
    54  		})
    55  }
    56  
    57  func (netconf *NetworkConfig) addBridgeOnlyInterface(iface net.Interface,
    58  	subnetId string) {
    59  	netconf.bridgeOnlyInterfaces = append(netconf.bridgeOnlyInterfaces,
    60  		bridgeOnlyInterfaceType{
    61  			netInterface: iface,
    62  			subnetId:     subnetId,
    63  		})
    64  }
    65  
    66  func (netconf *NetworkConfig) addNormalInterface(iface net.Interface,
    67  	ipAddr net.IP, subnet *hyper_proto.Subnet) {
    68  	netconf.normalInterfaces = append(netconf.normalInterfaces,
    69  		normalInterfaceType{
    70  			netInterface: iface,
    71  			ipAddr:       ipAddr,
    72  			subnet:       subnet,
    73  		})
    74  }
    75  
    76  func compute(machineInfo fm_proto.GetMachineInfoResponse,
    77  	_interfaces map[string]net.Interface,
    78  	logger log.DebugLogger) (*NetworkConfig, error) {
    79  	netconf := &NetworkConfig{}
    80  	networkEntries := getNetworkEntries(machineInfo)
    81  	interfaces := make(map[string]net.Interface, len(_interfaces))
    82  	var vlanInterfaceNames []string
    83  	for name, iface := range _interfaces {
    84  		interfaces[name] = iface
    85  	}
    86  	hwAddrToInterface := make(map[string]net.Interface, len(interfaces))
    87  	for _, iface := range interfaces {
    88  		hwAddrToInterface[iface.HardwareAddr.String()] = iface
    89  	}
    90  	preferredSubnet := findMatchingSubnet(machineInfo.Subnets,
    91  		machineInfo.Machine.HostIpAddress)
    92  	if machineInfo.Machine.GatewaySubnetId != "" {
    93  		preferredSubnet = findSubnet(machineInfo.Subnets,
    94  			machineInfo.Machine.GatewaySubnetId)
    95  	}
    96  	// First process network entries with normal interfaces.
    97  	var bondedNetworkEntries []fm_proto.NetworkEntry
    98  	normalInterfaceIndex := 0
    99  	usedSubnets := make(map[*hyper_proto.Subnet]struct{})
   100  	for _, networkEntry := range networkEntries {
   101  		if len(networkEntry.HostIpAddress) < 1 {
   102  			if len(networkEntry.HostMacAddress) < 1 ||
   103  				networkEntry.SubnetId == "" {
   104  				continue
   105  			}
   106  			iface, ok := hwAddrToInterface[networkEntry.HostMacAddress.String()]
   107  			if !ok {
   108  				return nil, fmt.Errorf("MAC address: %s not found",
   109  					networkEntry.HostMacAddress)
   110  			}
   111  			subnet := findSubnet(machineInfo.Subnets, networkEntry.SubnetId)
   112  			if subnet == nil {
   113  				return nil,
   114  					fmt.Errorf("subnetId: %s not found", networkEntry.SubnetId)
   115  			}
   116  			if !subnet.Manage {
   117  				return nil,
   118  					fmt.Errorf("subnetId: %s is not managed", subnet.Id)
   119  			}
   120  			if len(subnet.Id) >= 10 {
   121  				return nil,
   122  					fmt.Errorf("subnetId: %s is over 9 characters", subnet.Id)
   123  			}
   124  			if strings.ContainsAny(subnet.Id, "/.") {
   125  				return nil,
   126  					fmt.Errorf("subnetId: %s contains '/' or '.'", subnet.Id)
   127  			}
   128  			usedSubnets[subnet] = struct{}{}
   129  			netconf.addBridgeOnlyInterface(iface, networkEntry.SubnetId)
   130  			delete(interfaces, iface.Name)
   131  			continue
   132  		}
   133  		if len(networkEntry.HostMacAddress) < 1 {
   134  			bondedNetworkEntries = append(bondedNetworkEntries, networkEntry)
   135  			continue
   136  		}
   137  		iface, ok := hwAddrToInterface[networkEntry.HostMacAddress.String()]
   138  		if !ok {
   139  			logger.Printf("MAC address: %s not found\n",
   140  				networkEntry.HostMacAddress)
   141  			continue
   142  		}
   143  		subnet := findMatchingSubnet(machineInfo.Subnets,
   144  			networkEntry.HostIpAddress)
   145  		if subnet == nil {
   146  			logger.Printf("no matching subnet for ip=%s\n",
   147  				networkEntry.HostIpAddress)
   148  			continue
   149  		}
   150  		usedSubnets[subnet] = struct{}{}
   151  		normalInterfaceIndex++
   152  		netconf.addNormalInterface(iface, networkEntry.HostIpAddress, subnet)
   153  		delete(interfaces, iface.Name)
   154  		if subnet == preferredSubnet {
   155  			netconf.DefaultSubnet = subnet
   156  		} else if netconf.DefaultSubnet == nil {
   157  			netconf.DefaultSubnet = subnet
   158  		}
   159  		if networkEntry.VlanTrunk {
   160  			vlanInterfaceNames = append(vlanInterfaceNames, iface.Name)
   161  			netconf.vlanRawDevice = iface.Name
   162  		}
   163  	}
   164  	// All remaining interfaces which are marked as up will be used for VLAN
   165  	// trunk. If there multiple interfaces, they will be bonded.
   166  	for name, iface := range interfaces {
   167  		if iface.Flags&net.FlagUp == 0 {
   168  			delete(interfaces, name)
   169  		} else {
   170  			netconf.bondSlaves = append(netconf.bondSlaves, name)
   171  			netconf.vlanRawDevice = name
   172  			vlanInterfaceNames = append(vlanInterfaceNames, name)
   173  		}
   174  	}
   175  	if len(vlanInterfaceNames) > 1 {
   176  		netconf.vlanRawDevice = "bond0"
   177  	}
   178  	sort.Strings(netconf.bondSlaves)
   179  	if len(vlanInterfaceNames) > 0 {
   180  		for _, networkEntry := range bondedNetworkEntries {
   181  			subnet := findMatchingSubnet(machineInfo.Subnets,
   182  				networkEntry.HostIpAddress)
   183  			if subnet == nil {
   184  				logger.Printf("no matching subnet for ip=%s\n",
   185  					networkEntry.HostIpAddress)
   186  				continue
   187  			}
   188  			usedSubnets[subnet] = struct{}{}
   189  			entryName := fmt.Sprintf("%s.%d",
   190  				netconf.vlanRawDevice, subnet.VlanId)
   191  			netconf.addBondedInterface(entryName, networkEntry.HostIpAddress,
   192  				subnet)
   193  			if subnet == preferredSubnet {
   194  				netconf.DefaultSubnet = subnet
   195  			} else if netconf.DefaultSubnet == nil {
   196  				netconf.DefaultSubnet = subnet
   197  			}
   198  		}
   199  		for _, subnet := range machineInfo.Subnets {
   200  			if _, ok := usedSubnets[subnet]; ok {
   201  				continue
   202  			}
   203  			if subnet.VlanId > 0 && subnet.Manage {
   204  				netconf.bridges = append(netconf.bridges, subnet.VlanId)
   205  			}
   206  		}
   207  	}
   208  	return netconf, nil
   209  }