github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/network/netplan/activate.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package netplan
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/juju/clock"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  
    15  	"github.com/juju/juju/utils/scriptrunner"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.network.netplan")
    19  
    20  // ActivationParams contains options to use when bridging interfaces
    21  type ActivationParams struct {
    22  	Clock     clock.Clock
    23  	Devices   []DeviceToBridge
    24  	RunPrefix string
    25  	Directory string
    26  	Timeout   time.Duration
    27  }
    28  
    29  // ActivationResult captures the result of actively bridging the
    30  // interfaces using ifup/ifdown.
    31  type ActivationResult struct {
    32  	Stdout string
    33  	Stderr string
    34  	Code   int
    35  }
    36  
    37  // BridgeAndActivate will parse a set of netplan yaml files in a directory,
    38  // create a new netplan config with the provided interfaces bridged
    39  // bridged, then reconfigure the network using the ifupdown package
    40  // for the new bridges.
    41  func BridgeAndActivate(params ActivationParams) (*ActivationResult, error) {
    42  	if len(params.Devices) == 0 {
    43  		return nil, errors.Errorf("no devices specified")
    44  	}
    45  
    46  	netplan, err := ReadDirectory(params.Directory)
    47  
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	for _, device := range params.Devices {
    53  		deviceID, deviceType, err := netplan.FindDeviceByNameOrMAC(device.DeviceName, device.MACAddress)
    54  		if err != nil {
    55  			return nil, errors.Trace(err)
    56  		}
    57  		switch deviceType {
    58  		case TypeEthernet:
    59  			err = netplan.BridgeEthernetById(deviceID, device.BridgeName)
    60  			if err != nil {
    61  				return nil, err
    62  			}
    63  		case TypeBond:
    64  			err = netplan.BridgeBondById(deviceID, device.BridgeName)
    65  			if err != nil {
    66  				return nil, err
    67  			}
    68  		case TypeVLAN:
    69  			err = netplan.BridgeVLANById(deviceID, device.BridgeName)
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  		default:
    74  			return nil, errors.Errorf("unable to create bridge for %q, unknown device type %q", deviceID, deviceType)
    75  		}
    76  	}
    77  	_, err = netplan.Write("")
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	err = netplan.MoveYamlsToBak()
    83  	if err != nil {
    84  		_ = netplan.Rollback()
    85  		return nil, err
    86  	}
    87  
    88  	environ := os.Environ()
    89  	// TODO(wpk) 2017-06-21 Is there a way to verify that apply is finished?
    90  	// https://bugs.launchpad.net/netplan/+bug/1701436
    91  	command := fmt.Sprintf("%snetplan generate && netplan apply && sleep 10", params.RunPrefix)
    92  
    93  	result, err := scriptrunner.RunCommand(command, environ, params.Clock, params.Timeout)
    94  
    95  	activationResult := ActivationResult{
    96  		Stderr: string(result.Stderr),
    97  		Stdout: string(result.Stdout),
    98  		Code:   result.Code,
    99  	}
   100  
   101  	logger.Debugf("Netplan activation result %q %q %d", result.Stderr, result.Stdout, result.Code)
   102  
   103  	if err != nil {
   104  		_ = netplan.Rollback()
   105  		return &activationResult, errors.Errorf("bridge activation error: %s", err)
   106  	}
   107  	if result.Code != 0 {
   108  		_ = netplan.Rollback()
   109  		return &activationResult, errors.Errorf("bridge activation error code %d", result.Code)
   110  	}
   111  	return nil, nil
   112  }