Permalink
Browse files

Roll in concerto-netconfig script

  • Loading branch information...
1 parent 3f4bfb0 commit 4f8d4524d92e0ff5aca29fc658ba9a4175674b7e @asquared committed Jun 4, 2012
View
@@ -0,0 +1,57 @@
+Network configuration and the concerto-netconfig script
+
+Since Concerto clients must support a wide variety of networking environments,
+and network settings should be configurable through a graphical user interface,
+a piece of software is needed to translate a "user-friendly" network
+configuration (wired/wireless, static/dynamic IP) into the detailed
+configuration files required by the operating system. This job is carried out
+by concerto-netconfig, a simple Ruby script to parse a set of network
+details in JSON format and generate the necessary configuration.
+
+The netconfig script considers a network connection as two pieces.
+First is the physical connection: what port do I use? what wireless network
+do I authenticate to and how? This is the connection method. Second is the
+type of addressing to use (static or dynamic, maybe someday stateless-v6
+only :). This is called the addressing method. The various connection
+and addressing methods we need to support are implemented as Ruby classes.
+These classes are instantiated using parameters supplied in the JSON input.
+
+The connection method object returns the name of a physical interface to use,
+and potentially some extra lines for /etc/network/interfaces needed to make
+the connection (as in the case of wireless networks). Likewise, the addressing
+method returns the name of the (OS-level) addressing method to use (static,
+dhcp, or whatever else) and may also add some lines to the interface files
+if necessary (e.g. static address data). Each of these objects also has the
+opportunity to write additional configuration files if needed. It's unlikely,
+but conflict may arise between the connection and addressing methods with
+respect to a configuration file other than /etc/network/interfaces. There
+is currently no mechanism in place to avoid or resolve such conflict.
+
+Examples:
+An unsecured wireless connection with DHCP:
+{
+ "connection_method":"WirelessConnection",
+ "connection_method_args":
+ {
+ "ssid":"SomeNetwork"
+ },
+ "addressing_method":"DHCPAddressing",
+ "addressing_method_args":{}
+}
+
+A wired connection with a static address:
+{
+ "connection_method":"WiredConnection",
+ "connection_method_args":{},
+ "addressing_method":"StaticAddressing",
+ "addressing_method_args":
+ {
+ "address":"192.168.0.99",
+ "gateway":"192.168.0.1",
+ "netmask":"255.255.255.0",
+ "nameservers":["192.168.0.2","192.168.0.3"]
+ }
+}
+
+I think this will map nicely onto a web-based UI, basically we're just
+serializing a couple of forms into JSON.
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+gem install json
@@ -0,0 +1,41 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: concerto-netconfig
+# Required-Start: udev
+# Required-Stop:
+# Should-Start:
+# Should-Stop:
+# X-Start-Before: networking
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Create Debian network config files
+# Description: Parse user configuration data and generate Debian
+# network config files as needed
+### END INIT INFO
+
+NAME=concerto-netconfig
+NETCONFIG=/usr/local/bin/concerto-netconfig
+
+test -x $NETCONFIG || exit 0
+
+. /lib/lsb/init-functions
+
+run_netconfig()
+{
+ log_daemon_msg "Creating network configuration files" "$NAME"
+ $NETCONFIG < /etc/netconfig.json > /etc/network/interfaces
+ log_end_msg $?
+}
+
+case "$1" in
+ start)
+ run_netconfig
+ ;;
+ stop)
+ # do nothing
+ ;;
+ *)
+ echo "Usage: /etc/init.d/$NAME [start|stop]"
+ exit 2
+ ;;
+esac
@@ -0,0 +1,6 @@
+{
+ "connection_method":"WiredConnection",
+ "connection_method_args":{},
+ "addressing_method":"DHCPAddressing",
+ "addressing_method_args":{}
+}
@@ -0,0 +1,190 @@
+require 'rubygems'
+require 'json'
+
+def read_sysfs(iface, var)
+ # Read a sysfs variable (/sys/class/net/iface/var).
+ path = "/sys/class/net/#{iface}/#{var}"
+ IO.read(path).chomp
+end
+
+class WiredConnection
+ def initialize(args)
+ if args['interface_name']
+ @ifname = args['interface_name']
+ end
+ end
+
+ # Write any necessary auxiliary configuration files
+ def write_configs
+ # We don't need any.
+ end
+
+ # Return the name of the interface to be configured.
+ def interface_name
+ if @ifname
+ # the user has specified an interface to use
+ @ifname
+ else
+ # scan for the first wired interface that has media
+ scan_interfaces
+ end
+ end
+
+ # Return any additional lines needed in the interfaces file
+ # e.g. referencing WPA config files...
+ def interfaces_lines
+ # Nothing special needed for wired connections.
+ []
+ end
+
+private
+ def wired_interfaces
+ # This is somewhat Linux specific, and may not be all encompassing.
+ devices = Dir.glob('/sys/class/net/eth*')
+ devices.map { |d| File.basename(d) }
+ end
+
+ def scan_interfaces
+ wired_interfaces.each do |iface|
+ # bring up interface if it was down
+ if (read_sysfs(iface, 'operstate') != 'up')
+ system "ifconfig #{iface} up"
+ sleep 0.1
+ end
+ # check if cable is connected
+ carrier = read_sysfs(iface, 'carrier')
+ # bring interface back down
+ system "ifconfig #{iface} down"
+
+ if carrier == '1'
+ return iface
+ end
+ end
+
+ # if we get here no interface was found with a cable attached
+ # default to eth0 and hope for the best
+ STDERR.puts "warning: no suitable interface found, defaulting to eth0"
+ 'eth0'
+ end
+end
+
+class WirelessConnection
+ def initialize(args)
+ @ssid = args['ssid']
+ @force_iface = args['iface'] if args['iface']
+ @wpa_config_file = '/tmp/wpa_supplicant.concerto.conf'
+ end
+
+ def interface_name
+ # If the user has requested a specific interface, use it.
+ # Otherwise, just pick the first wlan interface, assuming
+ # it works and all wlan interfaces have approximately equal
+ # reception. When this assumption is wrong the user must force.
+ @force_iface || wireless_interfaces[0]
+ end
+
+ def write_configs
+ # Write a wpa_supplicant.conf file for an unsecured network.
+ File.open(@wpa_config_file, 'w') do |wpaconf|
+ # long lines, sorry!
+ wpaconf.puts "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel"
+ wpaconf.puts "network={"
+ wpaconf.puts "ssid=\"#{@ssid}\""
+ wpaconf.puts "scan_ssid=1"
+ wpaconf.puts "key_mgmt=NONE"
+ wpaconf.puts "}"
+ end
+ end
+
+ def interfaces_lines
+ ["wpa-conf #{@wpa_config_file}"]
+ end
+
+private
+
+ def wireless_interfaces
+ # This is somewhat Linux specific, and may not be all encompassing.
+ devices = Dir.glob('/sys/class/net/{ath,wlan}*')
+ devices.map { |d| File.basename(d) }
+ end
+end
+
+class StaticAddressing
+ def initialize(args)
+ @nameservers = args['nameservers']
+ @addr = args['address']
+ @mask = args['netmask']
+ @gateway = args['gateway']
+ end
+
+ def addressing_type
+ 'static'
+ end
+
+ def interfaces_lines
+ [
+ "address #{@addr}",
+ "netmask #{@mask}",
+ "gateway #{@gateway}"
+ ]
+ end
+
+ def write_configs
+ File.open('/etc/resolv.conf','w') do |resolvconf|
+ @nameservers.each do |nameserver|
+ resolvconf.puts("nameserver #{nameserver}");
+ end
+ end
+ end
+end
+
+class DHCPAddressing
+ def initialize(args)
+ # we accept no args
+ end
+
+ def addressing_type
+ 'dhcp'
+ end
+
+ def interfaces_lines
+ # DHCP needs no additional interfaces args from the addressing side
+ []
+ end
+
+ def write_configs
+ # dhclient will write our resolv.conf so we do not need to do anything
+ end
+end
+
+input = STDIN.read
+args = JSON.parse(input)
+
+connection_method_class = Object.const_get(args['connection_method'])
+addressing_method_class = Object.const_get(args['addressing_method'])
+
+connection_method = connection_method_class.new(
+ args['connection_method_args']
+)
+
+addressing_method = addressing_method_class.new(
+ args['addressing_method_args']
+)
+
+ifname = connection_method.interface_name
+
+puts "# Concerto Live network configuration"
+puts "# Generated by netconfig.rb"
+puts "# Changes will be lost on reboot"
+puts "auto #{ifname}"
+puts "iface #{ifname} inet #{addressing_method.addressing_type}"
+
+addressing_method.interfaces_lines.each do |line|
+ puts "\t#{line}"
+end
+
+connection_method.interfaces_lines.each do |line|
+ puts "\t#{line}"
+end
+
+connection_method.write_configs
@@ -14,3 +14,6 @@ firmware-libertas
firmware-ralink
firmware-realtek
+ruby
+rubygems
+vim

0 comments on commit 4f8d452

Please sign in to comment.