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 }