...

Source file src/edge-infra.dev/pkg/edge/edgeadmin/edgecliutils/terminalInput.go

Documentation: edge-infra.dev/pkg/edge/edgeadmin/edgecliutils

     1  package edgecliutils
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"edge-infra.dev/pkg/edge/api/graph/model"
     8  	"edge-infra.dev/pkg/edge/api/utils"
     9  	"edge-infra.dev/pkg/lib/networkvalidator"
    10  )
    11  
    12  func makeTermInput(currentTerminal *model.Terminal, lane, role, class, mac, ipaddr, gateway4addr, gateway6addr, discoverDisks, terminalDiskID, devicePath *string, prefixLen *int, dhcp4, dhcp6, includeDisk, expectEmpty *bool, family model.InetType) (*model.TerminalIDInput, error) {
    13  	// Create term, iface and addr inputs separately, then update/merge
    14  	termInput := &model.TerminalIDInput{
    15  		TerminalID: currentTerminal.TerminalID,
    16  		TerminalValues: &model.TerminalUpdateInput{
    17  			Lane:       lane,
    18  			Disks:      []*model.TerminalDiskIDInput{},
    19  			Interfaces: []*model.TerminalInterfaceIDInput{},
    20  		},
    21  	}
    22  	if role != nil {
    23  		rl := model.TerminalRoleType(*role)
    24  		termInput.TerminalValues.Role = &rl
    25  	}
    26  	if class != nil {
    27  		cl := model.TerminalClassType(*class)
    28  		termInput.TerminalValues.Class = &cl
    29  	}
    30  	if discoverDisks != nil {
    31  		dd := model.TerminalDiscoverDisksType(*discoverDisks)
    32  		termInput.TerminalValues.DiscoverDisks = &dd
    33  	}
    34  
    35  	termInput = mergeIntoTermInput(termInput, currentTerminal)
    36  
    37  	if terminalDiskID != nil {
    38  		termInput.TerminalValues.Disks = []*model.TerminalDiskIDInput{
    39  			{
    40  				TerminalDiskID: *terminalDiskID,
    41  				TerminalDiskValues: &model.TerminalDiskUpdateInput{
    42  					DevicePath:  devicePath,
    43  					IncludeDisk: includeDisk,
    44  					ExpectEmpty: expectEmpty,
    45  				},
    46  			},
    47  		}
    48  	}
    49  
    50  	// Only attempt to update terminal interfaces if the user provides a MAC by which to identify
    51  	// the interface to be modified.
    52  	if mac != nil {
    53  		ifaceInput := &model.TerminalInterfaceIDInput{
    54  			TerminalInterfaceID: "", // leave empty for now
    55  			TerminalInterfaceValues: &model.TerminalInterfaceUpdateInput{
    56  				MacAddress: mac,
    57  				Dhcp4:      dhcp4,
    58  				Dhcp6:      dhcp6,
    59  				Addresses:  []*model.TerminalAddressIDInput{},
    60  				Gateway4:   gateway4addr,
    61  				Gateway6:   gateway6addr,
    62  			},
    63  		}
    64  
    65  		addrInput := &model.TerminalAddressIDInput{
    66  			TerminalAddressID: "", // leave empty for now
    67  			TerminalAddressValues: &model.TerminalAddressUpdateInput{
    68  				IP:        ipaddr,
    69  				PrefixLen: prefixLen,
    70  				Family:    &family,
    71  			},
    72  		}
    73  
    74  		err := updateIfaceOfTermInput(termInput.TerminalValues, ifaceInput, currentTerminal.Interfaces, addrInput)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  	}
    79  
    80  	return termInput, nil
    81  }
    82  
    83  // Our convention is to always mutate the _first_ argument
    84  
    85  func mergeIntoTermInput(newTerm *model.TerminalIDInput, currentTerminal *model.Terminal) *model.TerminalIDInput {
    86  	newTerm.TerminalValues.Lane = utils.AssignNotNil(currentTerminal.Lane, newTerm.TerminalValues.Lane)
    87  	newTerm.TerminalValues.Role = utils.AssignNotNil(&currentTerminal.Role, newTerm.TerminalValues.Role)
    88  	newTerm.TerminalValues.Class = utils.AssignNotNil(currentTerminal.Class, newTerm.TerminalValues.Class)
    89  
    90  	// Unlike the other helper functions, we don't invoke merging for ifaces/addresses
    91  	// here since we'd need access to lots of CLI args which we don't want to pass around AFAP
    92  
    93  	return newTerm
    94  }
    95  
    96  // Finds matching interface and updates the current values with new values
    97  func updateIfaceOfTermInput(newTerm *model.TerminalUpdateInput, newIface *model.TerminalInterfaceIDInput, currentTerminalInterfaces []*model.TerminalInterface, newAddr *model.TerminalAddressIDInput) error {
    98  	idx, matchingIface := FindFirst(func(iface *model.TerminalInterface) bool {
    99  		return iface.MacAddress == *newIface.TerminalInterfaceValues.MacAddress
   100  	}, currentTerminalInterfaces)
   101  
   102  	if matchingIface == nil {
   103  		return fmt.Errorf("no matching interface found with mac address %v, cannot modify non-existent interface", *newIface.TerminalInterfaceValues.MacAddress)
   104  	}
   105  	mergedIface, err := mergeIntoTermIfaceInput(newIface, matchingIface, newAddr)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	newTerm.Interfaces = SafeInsert(newTerm.Interfaces, idx, mergedIface)
   110  
   111  	return nil
   112  }
   113  
   114  // newIface is a sparsely-populated value, containing _just_ the fields passed via CLI.
   115  // currentIface is a fully-populated value (of the wrong type: a GQL output type, not input type).
   116  // Both relate to the same entry in the list of interfaces.
   117  func mergeIntoTermIfaceInput(newIface *model.TerminalInterfaceIDInput, currentIface *model.TerminalInterface, newAddr *model.TerminalAddressIDInput) (*model.TerminalInterfaceIDInput, error) {
   118  	// Add the ID omitted earlier
   119  	newIface.TerminalInterfaceID = currentIface.TerminalInterfaceID
   120  
   121  	newIface.TerminalInterfaceValues.Dhcp4 = utils.AssignNotNil(&currentIface.Dhcp4, newIface.TerminalInterfaceValues.Dhcp4)
   122  	newIface.TerminalInterfaceValues.Dhcp6 = utils.AssignNotNil(&currentIface.Dhcp6, newIface.TerminalInterfaceValues.Dhcp6)
   123  	newIface.TerminalInterfaceValues.Gateway4 = utils.AssignNotNil(currentIface.Gateway4, newIface.TerminalInterfaceValues.Gateway4)
   124  	newIface.TerminalInterfaceValues.Gateway6 = utils.AssignNotNil(currentIface.Gateway6, newIface.TerminalInterfaceValues.Gateway6)
   125  	newIface.TerminalInterfaceValues.MacAddress = utils.AssignNotNil(&currentIface.MacAddress, newIface.TerminalInterfaceValues.MacAddress)
   126  
   127  	if newAddr != nil {
   128  		err := updateAddressOfTermIfaceInput(newIface, newAddr, currentIface.Addresses)
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  	}
   133  
   134  	err := validateTermIfaceInput(newIface)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	return newIface, nil
   140  }
   141  
   142  // Set the terminal address values that aren't being updated to the current values in the db.
   143  
   144  // Finds matching address by checking IDs. In _our_ case, we don't know the ID straight away -
   145  // need to use FindFirst with the predicate to look for the first ipv4/6 address attaching
   146  // to the original matching interface.
   147  
   148  func updateAddressOfTermIfaceInput(newIface *model.TerminalInterfaceIDInput, newAddr *model.TerminalAddressIDInput, currentTerminalAddresses []*model.TerminalAddress) error {
   149  	idx, inetAddr := FindFirst(func(addr *model.TerminalAddress) bool {
   150  		return addr.Family == *newAddr.TerminalAddressValues.Family
   151  	}, currentTerminalAddresses)
   152  
   153  	if inetAddr == nil {
   154  		return fmt.Errorf("no %v address found for interface with ID %v, cannot modify non-existent address", *newAddr.TerminalAddressValues.Family, newIface.TerminalInterfaceID)
   155  	}
   156  
   157  	addr, err := mergeIntoTermAddrInput(newAddr, inetAddr)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	newIface.TerminalInterfaceValues.Addresses = SafeInsert(newIface.TerminalInterfaceValues.Addresses, idx, addr)
   162  
   163  	return nil
   164  }
   165  
   166  func mergeIntoTermAddrInput(newAddress *model.TerminalAddressIDInput, currentAddress *model.TerminalAddress) (*model.TerminalAddressIDInput, error) {
   167  	// Add the ID omitted earlier
   168  	newAddress.TerminalAddressID = currentAddress.TerminalAddressID
   169  
   170  	newAddress.TerminalAddressValues.IP = utils.AssignNotNil(currentAddress.IP, newAddress.TerminalAddressValues.IP)
   171  	newAddress.TerminalAddressValues.Family = utils.AssignNotNil(&currentAddress.Family, newAddress.TerminalAddressValues.Family)
   172  	newAddress.TerminalAddressValues.PrefixLen = utils.AssignNotNil(&currentAddress.PrefixLen, newAddress.TerminalAddressValues.PrefixLen)
   173  
   174  	err := validateTermAddrInput(newAddress)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	return newAddress, nil
   180  }
   181  
   182  func validateTermIfaceInput(iface *model.TerminalInterfaceIDInput) error {
   183  	if iface.TerminalInterfaceValues.Dhcp4 != nil && !*iface.TerminalInterfaceValues.Dhcp4 {
   184  		// DHCP4 false - IPv4 and gateway4 address must be provided
   185  		if iface.TerminalInterfaceValues.Gateway4 == nil {
   186  			return errors.New("DHCP4 is set to false - you must provide a value for gateway4")
   187  		} else if !networkvalidator.ValidateIP(*iface.TerminalInterfaceValues.Gateway4) {
   188  			return fmt.Errorf("invalid gateway4 address: %s", *iface.TerminalInterfaceValues.Gateway4)
   189  		}
   190  	}
   191  
   192  	if iface.TerminalInterfaceValues.MacAddress != nil {
   193  		_, err := networkvalidator.ValidateMacAddress(*iface.TerminalInterfaceValues.MacAddress)
   194  		if err != nil {
   195  			return err
   196  		}
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  func validateTermAddrInput(addr *model.TerminalAddressIDInput) error {
   203  	if addr.TerminalAddressID == "" {
   204  		return fmt.Errorf("invalid/missing terminal address ID")
   205  	}
   206  	if addr.TerminalAddressValues.IP != nil {
   207  		if !networkvalidator.ValidateIP(*addr.TerminalAddressValues.IP) {
   208  			return fmt.Errorf("invalid/missing terminal address IP")
   209  		}
   210  		if addr.TerminalAddressValues.PrefixLen != nil && !networkvalidator.ValidateCIDR(*addr.TerminalAddressValues.IP, *addr.TerminalAddressValues.PrefixLen) {
   211  			return fmt.Errorf("invalid/missing terminal address IP")
   212  		}
   213  	}
   214  	return nil
   215  }
   216  

View as plain text