github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/networker/configfiles.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package networker
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  
    12  	"github.com/juju/utils"
    13  
    14  	"github.com/juju/juju/network"
    15  )
    16  
    17  // ConfigFile defines operations on a network config file for a single
    18  // network interface.
    19  type ConfigFile interface {
    20  	// InterfaceName returns the inteface name for this config file.
    21  	InterfaceName() string
    22  
    23  	// FileName returns the full path for storing this config file on
    24  	// disk.
    25  	FileName() string
    26  
    27  	// InterfaceInfo returns the network.InterfaceInfo associated with
    28  	// this config file.
    29  	InterfaceInfo() network.InterfaceInfo
    30  
    31  	// ReadData opens the underlying config file and populates the
    32  	// data.
    33  	ReadData() error
    34  
    35  	// Data returns the original raw contents of this config file.
    36  	Data() []byte
    37  
    38  	// RenderManaged generates network config based on the known
    39  	// network.InterfaceInfo and returns it.
    40  	RenderManaged() []byte
    41  
    42  	// NeedsUpdating returns true if this config file needs to be
    43  	// written to disk.
    44  	NeedsUpdating() bool
    45  
    46  	// IsPendingRemoval returns true if this config file needs to be
    47  	// removed.
    48  	IsPendingRemoval() bool
    49  
    50  	// IsManaged returns true if this config file is managed by Juju.
    51  	IsManaged() bool
    52  
    53  	// UpdateData updates the internally stored raw contents of this
    54  	// config file, and sets the "needs updating" internal flag,
    55  	// returning true, if newData is different. If newData is the same
    56  	// as the old or the interface is not managed, returns false and
    57  	// does not change anything.
    58  	UpdateData(newData []byte) bool
    59  
    60  	// MarkForRemoval marks this config file as pending for removal,
    61  	// if the interface is managed.
    62  	MarkForRemoval()
    63  
    64  	// Apply updates the config file data (if it needs updating),
    65  	// removes the file (if it's marked removal), or does nothing.
    66  	Apply() error
    67  }
    68  
    69  // ManagedHeader is the header of a network config file managed by Juju.
    70  const ManagedHeader = "# Managed by Juju, please don't change.\n\n"
    71  
    72  // RenderMainConfig generates a managed main config file, which
    73  // includes *.cfg individual config files inside configSubDir (i.e.
    74  // /etc/network/interfaces).
    75  func RenderMainConfig(configSubDir string) []byte {
    76  	var data bytes.Buffer
    77  	globSpec := fmt.Sprintf("%s/*.cfg", configSubDir)
    78  	logger.Debugf("rendering main network config to include %q", globSpec)
    79  	fmt.Fprintf(&data, ManagedHeader)
    80  	fmt.Fprintf(&data, "source %s\n\n", globSpec)
    81  	return data.Bytes()
    82  }
    83  
    84  // configFile implement ConfigFile.
    85  type configFile struct {
    86  	// interfaceName holds the name of the network interface.
    87  	interfaceName string
    88  
    89  	// fileName holds the full path to the config file on disk.
    90  	fileName string
    91  
    92  	// interfaceInfo holds the network information about this
    93  	// interface, known by the API server.
    94  	interfaceInfo network.InterfaceInfo
    95  
    96  	// data holds the raw file contents of the underlying file.
    97  	data []byte
    98  
    99  	// needsUpdating is true when the interface config has changed and
   100  	// needs to be written back to disk.
   101  	needsUpdating bool
   102  
   103  	// pendingRemoval is true when the interface config file is about
   104  	// to be removed.
   105  	pendingRemoval bool
   106  }
   107  
   108  var _ ConfigFile = (*configFile)(nil)
   109  
   110  // InterfaceName implements ConfigFile.InterfaceName().
   111  func (f *configFile) InterfaceName() string {
   112  	return f.interfaceName
   113  }
   114  
   115  // FileName implements ConfigFile.FileName().
   116  func (f *configFile) FileName() string {
   117  	return f.fileName
   118  }
   119  
   120  // ReadData implements ConfigFile.ReadData().
   121  func (f *configFile) ReadData() error {
   122  	data, err := ioutil.ReadFile(f.fileName)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	f.UpdateData(data)
   127  	return nil
   128  }
   129  
   130  // InterfaceInfo implements ConfigFile.InterfaceInfo().
   131  func (f *configFile) InterfaceInfo() network.InterfaceInfo {
   132  	return f.interfaceInfo
   133  }
   134  
   135  // Data implements ConfigFile.Data().
   136  func (f *configFile) Data() []byte {
   137  	return f.data
   138  }
   139  
   140  // RenderManaged implements ConfigFile.RenderManaged().
   141  //
   142  // TODO(dimitern) Once container addressability work has progressed
   143  // enough, modify this to render the config taking all fields of
   144  // network.InterfaceInfo into account.
   145  func (f *configFile) RenderManaged() []byte {
   146  	var data bytes.Buffer
   147  	actualName := f.interfaceInfo.ActualInterfaceName()
   148  	logger.Debugf("rendering managed config for %q", actualName)
   149  	fmt.Fprintf(&data, ManagedHeader)
   150  	fmt.Fprintf(&data, "auto %s\n", actualName)
   151  	fmt.Fprintf(&data, "iface %s inet dhcp\n", actualName)
   152  
   153  	// Add vlan-raw-device line for VLAN interfaces.
   154  	if f.interfaceInfo.IsVLAN() {
   155  		// network.InterfaceInfo.InterfaceName is always the physical
   156  		// device name, i.e. "eth1" for VLAN interface "eth1.42".
   157  		fmt.Fprintf(&data, "\tvlan-raw-device %s\n", f.interfaceInfo.InterfaceName)
   158  	}
   159  	fmt.Fprintf(&data, "\n")
   160  	return data.Bytes()
   161  }
   162  
   163  // NeedsUpdating implements ConfigFile.NeedsUpdating().
   164  func (f *configFile) NeedsUpdating() bool {
   165  	return f.needsUpdating
   166  }
   167  
   168  // IsPendingRemoval implements ConfigFile.IsPendingRemoval().
   169  func (f *configFile) IsPendingRemoval() bool {
   170  	return f.pendingRemoval
   171  }
   172  
   173  // IsManaged implements ConfigFile.IsManaged()
   174  func (f *configFile) IsManaged() bool {
   175  	return len(f.data) > 0 && bytes.HasPrefix(f.data, []byte(ManagedHeader))
   176  }
   177  
   178  // UpdateData implements ConfigFile.UpdateData().
   179  func (f *configFile) UpdateData(newData []byte) bool {
   180  	if bytes.Equal(f.data, newData) {
   181  		// Not changed.
   182  		if f.interfaceName == "" {
   183  			// This is the main config.
   184  			logger.Debugf("main network config not changed")
   185  		} else {
   186  			logger.Debugf("network config for %q not changed", f.interfaceName)
   187  		}
   188  		return false
   189  	}
   190  	f.data = make([]byte, len(newData))
   191  	copy(f.data, newData)
   192  	f.needsUpdating = true
   193  	return true
   194  }
   195  
   196  // MarkForRemoval implements ConfigFile.MarkForRemoval().
   197  func (f *configFile) MarkForRemoval() {
   198  	f.pendingRemoval = true
   199  }
   200  
   201  // Apply implements ConfigFile.Apply().
   202  func (f *configFile) Apply() error {
   203  	if f.needsUpdating {
   204  		err := utils.AtomicWriteFile(f.fileName, f.data, 0644)
   205  		if err != nil {
   206  			logger.Errorf("failed to write file %q: %v", f.fileName, err)
   207  			return err
   208  		}
   209  		if f.interfaceName == "" {
   210  			logger.Debugf("updated main network config %q", f.fileName)
   211  		} else {
   212  			logger.Debugf("updated network config %q for %q", f.fileName, f.interfaceName)
   213  		}
   214  		f.needsUpdating = false
   215  	}
   216  	if f.pendingRemoval {
   217  		err := os.Remove(f.fileName)
   218  		if err != nil {
   219  			logger.Errorf("failed to remove file %q: %v", f.fileName, err)
   220  			return err
   221  		}
   222  		logger.Debugf("removed config %q for %q", f.fileName, f.interfaceName)
   223  		f.pendingRemoval = false
   224  	}
   225  	return nil
   226  }