Permalink
Browse files

Move raw file I/O out into a ConfigStore module

  • Loading branch information...
1 parent 88eed6c commit 493bc45dc87e75471525804837d5f4e294611d06 @asquared committed Jun 12, 2012
@@ -1,5 +1,5 @@
require 'rubygems'
-require 'sinatra'
+require 'sinatra/base'
require 'haml'
require 'json'
require 'net/http'
@@ -201,7 +201,7 @@ def validate_url(url)
# is not implemented. This is also how we get away with just having
# one instance each of the config classes that are currently selected.
begin
- cm, am = ConcertoConfig.read_config
+ cm, am = ConcertoConfig.read_network_config
rescue Errno::ENOENT
cm = nil
am = nil
@@ -276,31 +276,16 @@ def do_assign(params, instance)
do_assign(cmargs, cm)
do_assign(amargs, am)
- # Check that everything is consistent. If not, we currently throw
- # an exception, which probably is not the best long term solution.
- cm.validate
- am.validate
-
- # Serialize our instances as JSON data to be written to the config file.
- json_data = {
- 'connection_method' => cmclass.basename,
- 'connection_method_args' => cm.args,
- 'addressing_method' => amclass.basename,
- 'addressing_method_args' => am.args
- }
-
- # Write the config file to disk.
- File.open(ConcertoConfig::CONFIG_FILE, 'w') do |f|
- f.write json_data.to_json
- end
+ # Save the configuration file.
+ ConcertoConfig.write_network_config(cm, am)
# Reload network configuration.
STDERR.puts "Trying to bring down the interface"
if ConcertoConfig.configured_interface
ConcertoConfig.configured_interface.ifdown
end
STDERR.puts "Rewriting configuration files"
- ConcertoConfig::configure_system
+ ConcertoConfig::configure_system_network
STDERR.puts "Bringing interface back up"
ConcertoConfig.configured_interface.ifup
@@ -0,0 +1,48 @@
+require 'concerto_client/live_image'
+require 'fileutils'
+
+# A key/value store for strings.
+# Right now implemented as disk files.
+module ConcertoConfig
+ module ConfigStore
+ @@path = nil
+
+ def self.read_config(name, default='')
+ initialize_path if not @@path
+ file = File.join(@@path, name)
+ rofile = File.join(@@ropath, name)
+
+ # Check the read/write config location first. If nothing there,
+ # check the read-only location. If nothing is there, return default.
+ # This way writes can be made at runtime on read-only media while
+ # still allowing some settings to be "baked into" the media.
+ if File.exist?(file)
+ IO.read(file)
+ elsif File.exist?(rofile)
+ IO.read(rofile)
+ else
+ default
+ end
+ end
+
+ # Write a config to the read/write configuration location.
+ def self.write_config(name, value)
+ initialize_path if not @@path
+ file = File.join(@@path, name)
+
+ File.open(file, 'w') do |f|
+ f.write value
+ end
+ end
+
+ def self.initialize_path
+ @@ropath = File.join(LiveImage.mountpoint, 'concerto', 'config')
+ if LiveImage.readonly?
+ @@path = '/tmp/concerto/config'
+ else
+ @@path = @@ropath
+ end
+ FileUtils.mkdir_p @@path
+ end
+ end
+end
@@ -0,0 +1,24 @@
+# Functions for dealing with the live image
+# (where it's mounted, if it's read-only, etc)
+module ConcertoConfig
+ module LiveImage
+ def self.mountpoint
+ '/live/image'
+ end
+
+ def self.readonly?
+ # on a readonly file system this will fail
+ if not File.exist? self.mountpoint
+ true
+ else
+ begin
+ f = Tempfile.new('test', self.mountpoint)
+ f.close!
+ false
+ rescue Errno::EROFS
+ true
+ end
+ end
+ end
+ end
+end
@@ -3,6 +3,7 @@
require 'rubygems'
require 'json'
require 'ipaddress'
+require 'concerto_client/config_store'
# The big idea here is that we have connection methods (layer 2)
# and addressing methods (layer 3) and by combining that configuration
@@ -19,21 +20,17 @@
# files such as wpa_supplicant.conf, resolv.conf etc.
class Module
+ # Get the name of a class/module and strip off any leading modules.
+ # This is useful in determining arguments for Module#const_get.
def basename
name.gsub(/^.*::/, '')
end
end
module ConcertoConfig
- # Where we store the name of the interface we are going to configure.
- INTERFACE_FILE='/tmp/concerto_configured_interface'
-
# The Debian interfaces configuration file we are going to write out.
INTERFACES_FILE='/etc/network/interfaces'
- # The configuration file we will read from.
- CONFIG_FILE='/live/image/netconfig.json'
-
# Some useful interface operations.
class Interface
# Wrap an interface name (eth0, wlan0 etc) with some useful operations.
@@ -440,17 +437,20 @@ def self.description
end
end
- # Read a JSON formatted network configuration from an input stream.
+ # Read a JSON formatted network configuration from the config store.
# This instantiates the connection and addressing method classes
- # and returns the instances
- # i.e.
- # cm, am = read_config(STDIN)
- def self.read_config
+ # and returns the instances i.e. cm, am = read_network_config
+ #
+ # If no configuration is saved or it is corrupt this returns
+ # a default configuration that is somewhat likely to work.
+ def self.read_network_config
+ input = ConfigStore.read_config('network_config', '')
+
begin
- input = IO.read(CONFIG_FILE)
args = JSON.parse(input)
- rescue Errno::ENOENT
- # set up some sane defaults if the config file doesn't exist
+ rescue
+ # set up some sane defaults if we have no configuration
+ # or it can't be parsed
args = {
'connection_method' => 'WiredConnection',
'addressing_method' => 'DHCPAddressing',
@@ -473,19 +473,38 @@ def self.read_config
return [connection_method, addressing_method]
end
+ # Save the network configuration to the configuration store.
+ # Arguments are instances of connection method and addressing
+ # method classes. Throws exception if either one is not valid.
+ def self.write_network_config(cm, am)
+ # Check that everything is consistent. If not, we currently throw
+ # an exception, which probably is not the best long term solution.
+ cm.validate
+ am.validate
+
+ # Serialize our instances as JSON data to be written to the config file.
+ json_data = {
+ 'connection_method' => cm.class.basename,
+ 'connection_method_args' => cm.args,
+ 'addressing_method' => am.class.basename,
+ 'addressing_method_args' => am.args
+ }.to_json
+
+ # Save the serialized configuration.
+ ConfigStore.write_config('network_config', json_data)
+ end
+
# This reads a JSON configuration file on STDIN and writes the interfaces
# file. Also the classes instantiated will have a chance to write
# out any auxiliary files needed.
- def self.configure_system
- connection_method, addressing_method = read_config
+ def self.configure_system_network
+ connection_method, addressing_method = read_network_config
ifname = connection_method.config_interface_name
# squirrel away the name of the interface we are configuring
# This will be useful later for getting network status information.
- File.open(INTERFACE_FILE, 'w') do |f|
- f.write ifname
- end
+ ConfigStore.write_config('network_interface', ifname)
# Write the /etc/network/interfaces file.
File.open(INTERFACES_FILE, 'w') do |f|
@@ -510,13 +529,11 @@ def self.configure_system
# Get the name of the interface we configured
def self.configured_interface
- begin
- ifname = File.open(INTERFACE_FILE) do |f|
- f.readline.chomp
- end
- Interface.new(ifname)
- rescue
- nil
- end
+ ifname = ConfigStore.read_config('network_interface', '')
+ if ifname != ''
+ Interface.new(ifname)
+ else
+ nil
+ end
end
end

0 comments on commit 493bc45

Please sign in to comment.