Skip to content

Commit

Permalink
Roll in concerto-netconfig script
Browse files Browse the repository at this point in the history
  • Loading branch information
asquared committed Jun 4, 2012
1 parent 3f4bfb0 commit 4f8d452
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 0 deletions.
57 changes: 57 additions & 0 deletions README.netconfig
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions config/hooks/100-rubygems.chroot
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

gem install json
41 changes: 41 additions & 0 deletions config/includes.chroot/etc/init.d/concerto-netconfig
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions config/includes.chroot/etc/netconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"connection_method":"WiredConnection",
"connection_method_args":{},
"addressing_method":"DHCPAddressing",
"addressing_method_args":{}
}
190 changes: 190 additions & 0 deletions config/includes.chroot/usr/local/bin/concerto-netconfig
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions config/package-lists/concerto.list.chroot
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ firmware-libertas
firmware-ralink
firmware-realtek

ruby
rubygems
vim

0 comments on commit 4f8d452

Please sign in to comment.