bandshell 0.0.20 → 0.0.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/bandshell-timer.rb +34 -0
- data/bin/bandshelld +14 -2
- data/bin/concerto_netsetup +0 -1
- data/lib/bandshell/application/app.rb +440 -312
- data/lib/bandshell/application/config.ru +4 -1
- data/lib/bandshell/application/public/authenticate.js +134 -0
- data/lib/bandshell/application/public/network.js +15 -15
- data/lib/bandshell/application/public/problem.js +2 -2
- data/lib/bandshell/application/views/authenticate.haml +15 -0
- data/lib/bandshell/application/views/main.haml +1 -1
- data/lib/bandshell/application/views/player_status.haml +10 -7
- data/lib/bandshell/application/views/setup.haml +7 -2
- data/lib/bandshell/config_store.rb +36 -36
- data/lib/bandshell/hardware_api.rb +180 -0
- data/lib/bandshell/live_image.rb +26 -26
- data/lib/bandshell/netconfig.rb +459 -458
- metadata +35 -48
- data/bin/concerto_configserver +0 -10
data/lib/bandshell/live_image.rb
CHANGED
@@ -3,31 +3,31 @@ require 'tempfile'
|
|
3
3
|
# Functions for dealing with the live image
|
4
4
|
# (where it's mounted, if it's read-only, etc)
|
5
5
|
module Bandshell
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
module LiveImage
|
7
|
+
def self.mountpoint
|
8
|
+
if File.exist? '/etc/concerto/medium_path'
|
9
|
+
IO.read('/etc/concerto/medium_path').chomp
|
10
|
+
else
|
11
|
+
# sane default for Debian Live-based systems
|
12
|
+
# (as of 2013-04-24)
|
13
|
+
'/lib/live/mount/medium'
|
14
|
+
end
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
17
|
+
def self.readonly?
|
18
|
+
# on a readonly file system this will fail
|
19
|
+
if not File.exist? self.mountpoint
|
20
|
+
true
|
21
|
+
else
|
22
|
+
begin
|
23
|
+
f = Tempfile.new('test', self.mountpoint)
|
24
|
+
f.close!
|
25
|
+
false
|
26
|
+
rescue
|
27
|
+
# if the tempfile creation bombs we assume readonly
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
33
|
end
|
data/lib/bandshell/netconfig.rb
CHANGED
@@ -20,523 +20,524 @@ require 'bandshell/config_store'
|
|
20
20
|
# files such as wpa_supplicant.conf, resolv.conf etc.
|
21
21
|
|
22
22
|
class Module
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
# Get the name of a class/module and strip off any leading modules.
|
24
|
+
# This is useful in determining arguments for Module#const_get.
|
25
|
+
def basename
|
26
|
+
name.gsub(/^.*::/, '')
|
27
|
+
end
|
28
28
|
end
|
29
29
|
|
30
30
|
module Bandshell
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# Get the name of the interface as a string.
|
42
|
-
attr_reader :name
|
43
|
-
|
44
|
-
# Get the (first) IPv4 address assigned to the interface.
|
45
|
-
# Return "0.0.0.0" if we don't have any v4 addresses.
|
46
|
-
def ip
|
47
|
-
if ifconfig =~ /inet addr:([0-9.]+)/
|
48
|
-
$1
|
49
|
-
else
|
50
|
-
"0.0.0.0"
|
51
|
-
end
|
52
|
-
end
|
31
|
+
# The Debian interfaces configuration file we are going to write out.
|
32
|
+
INTERFACES_FILE='/etc/network/interfaces'
|
33
|
+
|
34
|
+
# Some useful interface operations.
|
35
|
+
class Interface
|
36
|
+
# Wrap an interface name (eth0, wlan0 etc) with some useful operations.
|
37
|
+
def initialize(name)
|
38
|
+
@name = name
|
39
|
+
end
|
53
40
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
41
|
+
# Get the name of the interface as a string.
|
42
|
+
attr_reader :name
|
43
|
+
|
44
|
+
# Get the (first) IPv4 address assigned to the interface.
|
45
|
+
# Return "0.0.0.0" if we don't have any v4 addresses.
|
46
|
+
def ip
|
47
|
+
if ifconfig =~ /inet addr:([0-9.]+)/
|
48
|
+
$1
|
49
|
+
else
|
50
|
+
"0.0.0.0"
|
51
|
+
end
|
52
|
+
end
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
54
|
+
# Get the physical (mac, ethernet) address of the interface.
|
55
|
+
def mac
|
56
|
+
File.open("/sys/class/net/#{@name}/address") do |f|
|
57
|
+
f.read.chomp
|
58
|
+
end
|
59
|
+
end
|
64
60
|
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
def up
|
62
|
+
system("/sbin/ifconfig #{@name} up")
|
63
|
+
end
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
def down
|
66
|
+
system("/sbin/ifconfig #{@name} down")
|
67
|
+
end
|
72
68
|
|
73
|
-
|
74
|
-
|
75
|
-
|
69
|
+
def ifup
|
70
|
+
system("/sbin/ifup #{@name}")
|
71
|
+
end
|
76
72
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
false
|
82
|
-
end
|
83
|
-
end
|
73
|
+
def ifdown
|
74
|
+
system("/sbin/ifdown #{@name}")
|
75
|
+
end
|
84
76
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
up
|
92
|
-
sleep 10
|
93
|
-
end
|
94
|
-
|
95
|
-
if ifconfig =~ /RUNNING/
|
96
|
-
result = true
|
97
|
-
end
|
98
|
-
|
99
|
-
if brought_up
|
100
|
-
down
|
101
|
-
end
|
102
|
-
|
103
|
-
result
|
104
|
-
end
|
105
|
-
private
|
106
|
-
def ifconfig
|
107
|
-
`/sbin/ifconfig #{@name}`
|
108
|
-
end
|
77
|
+
def up?
|
78
|
+
if ifconfig =~ /UP/
|
79
|
+
true
|
80
|
+
else
|
81
|
+
false
|
82
|
+
end
|
109
83
|
end
|
110
84
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
# creation and serialization:
|
115
|
-
#
|
116
|
-
# initialize(args={}): Create a new instance. When unserializing the args
|
117
|
-
# hash created during serialization is passed in.
|
118
|
-
#
|
119
|
-
# args: return a hash of data needed to reconstruct the instance. This
|
120
|
-
# hash will be passed to initialize() when unserializing.
|
121
|
-
#
|
122
|
-
# OS-level configuration:
|
123
|
-
#
|
124
|
-
# write_configs: Write out any additional configuration files needed
|
125
|
-
# (e.g. resolv.conf, wpa_supplicant.conf, ...)
|
126
|
-
#
|
127
|
-
# config_interface_name (only for connection methods): return the name of
|
128
|
-
# the physical interface to be used for the connection
|
129
|
-
#
|
130
|
-
# addressing_type (only for addressing methods): return the name of the
|
131
|
-
# addressing method (dhcp, static, manual...) to be used in the Debian
|
132
|
-
# network configuration file /etc/network/interfaces.
|
133
|
-
#
|
134
|
-
# interfaces_lines: an array of strings representing lines to be added
|
135
|
-
# to the /etc/network/interfaces file after the line naming the interface.
|
136
|
-
#
|
137
|
-
# Stuff for the Web interface:
|
138
|
-
#
|
139
|
-
# safe_assign: return an array of symbols representing fields the user
|
140
|
-
# should be allowed to modify.
|
141
|
-
#
|
142
|
-
# validate: check that the internal configuration is at least somewhat
|
143
|
-
# consistent and stands a chance of working; throw exception if not
|
144
|
-
# (FIXME! need a better error handling mechanism)
|
145
|
-
#
|
146
|
-
# and attr_accessors for everything the web interface
|
147
|
-
# should be able to assign to.
|
148
|
-
#
|
149
|
-
# Everyone must define a class method self.description as well.
|
150
|
-
# This returns a string that is displayed in the web interface dropdowns
|
151
|
-
# because using plain class identifiers there doesn't look good.
|
152
|
-
# This should be something short like "Wired Connection".
|
153
|
-
|
154
|
-
# Layer 2 connection via wired media.
|
155
|
-
# We will look for wired interfaces that have media connected,
|
156
|
-
# or use an interface chosen by the user via the args. There's
|
157
|
-
# nothing extra to be contributed to the interfaces file besides
|
158
|
-
# the name of the interface to be used.
|
159
|
-
class WiredConnection
|
160
|
-
def initialize(args={})
|
161
|
-
if args['interface_name']
|
162
|
-
@interface_name = args['interface_name']
|
163
|
-
end
|
164
|
-
end
|
85
|
+
def medium_present?
|
86
|
+
brought_up = false
|
87
|
+
result = false
|
165
88
|
|
166
|
-
|
167
|
-
|
168
|
-
|
89
|
+
if not up?
|
90
|
+
brought_up = true
|
91
|
+
up
|
92
|
+
sleep 10
|
93
|
+
end
|
169
94
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
@interface_name
|
174
|
-
else
|
175
|
-
# scan for the first wired interface that has media
|
176
|
-
scan_interfaces
|
177
|
-
end
|
178
|
-
end
|
95
|
+
if ifconfig =~ /RUNNING/
|
96
|
+
result = true
|
97
|
+
end
|
179
98
|
|
180
|
-
|
181
|
-
|
182
|
-
|
99
|
+
if brought_up
|
100
|
+
down
|
101
|
+
end
|
183
102
|
|
184
|
-
|
185
|
-
|
186
|
-
end
|
103
|
+
result
|
104
|
+
end
|
187
105
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
106
|
+
private
|
107
|
+
def ifconfig
|
108
|
+
`/sbin/ifconfig #{@name}`
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# (Instance) methods that must be defined by all connection and
|
113
|
+
# addressing method classes
|
114
|
+
#
|
115
|
+
# creation and serialization:
|
116
|
+
#
|
117
|
+
# initialize(args={}): Create a new instance. When unserializing the args
|
118
|
+
# hash created during serialization is passed in.
|
119
|
+
#
|
120
|
+
# args: return a hash of data needed to reconstruct the instance. This
|
121
|
+
# hash will be passed to initialize() when unserializing.
|
122
|
+
#
|
123
|
+
# OS-level configuration:
|
124
|
+
#
|
125
|
+
# write_configs: Write out any additional configuration files needed
|
126
|
+
# (e.g. resolv.conf, wpa_supplicant.conf, ...)
|
127
|
+
#
|
128
|
+
# config_interface_name (only for connection methods): return the name of
|
129
|
+
# the physical interface to be used for the connection
|
130
|
+
#
|
131
|
+
# addressing_type (only for addressing methods): return the name of the
|
132
|
+
# addressing method (dhcp, static, manual...) to be used in the Debian
|
133
|
+
# network configuration file /etc/network/interfaces.
|
134
|
+
#
|
135
|
+
# interfaces_lines: an array of strings representing lines to be added
|
136
|
+
# to the /etc/network/interfaces file after the line naming the interface.
|
137
|
+
#
|
138
|
+
# Stuff for the Web interface:
|
139
|
+
#
|
140
|
+
# safe_assign: return an array of symbols representing fields the user
|
141
|
+
# should be allowed to modify.
|
142
|
+
#
|
143
|
+
# validate: check that the internal configuration is at least somewhat
|
144
|
+
# consistent and stands a chance of working; throw exception if not
|
145
|
+
# (FIXME! need a better error handling mechanism)
|
146
|
+
#
|
147
|
+
# and attr_accessors for everything the web interface
|
148
|
+
# should be able to assign to.
|
149
|
+
#
|
150
|
+
# Everyone must define a class method self.description as well.
|
151
|
+
# This returns a string that is displayed in the web interface dropdowns
|
152
|
+
# because using plain class identifiers there doesn't look good.
|
153
|
+
# This should be something short like "Wired Connection".
|
154
|
+
|
155
|
+
# Layer 2 connection via wired media.
|
156
|
+
# We will look for wired interfaces that have media connected,
|
157
|
+
# or use an interface chosen by the user via the args. There's
|
158
|
+
# nothing extra to be contributed to the interfaces file besides
|
159
|
+
# the name of the interface to be used.
|
160
|
+
class WiredConnection
|
161
|
+
def initialize(args={})
|
162
|
+
if args['interface_name']
|
163
|
+
@interface_name = args['interface_name']
|
164
|
+
end
|
165
|
+
end
|
197
166
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|iface| iface.name == @interface_name
|
202
|
-
}.nil?
|
203
|
-
fail "The interface doesn't exist on the system"
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
167
|
+
def write_configs
|
168
|
+
# We don't need any.
|
169
|
+
end
|
207
170
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
171
|
+
def config_interface_name
|
172
|
+
if @interface_name && @interface_name.length > 0
|
173
|
+
# the user has specified an interface to use
|
174
|
+
@interface_name
|
175
|
+
else
|
176
|
+
# scan for the first wired interface that has media
|
177
|
+
scan_interfaces
|
178
|
+
end
|
179
|
+
end
|
214
180
|
|
215
|
-
|
216
|
-
|
217
|
-
|
181
|
+
# If interface_name is something other than nil or the empty string,
|
182
|
+
# we will override the automatic detection and use that interface.
|
183
|
+
attr_accessor :interface_name
|
218
184
|
|
219
|
-
|
220
|
-
|
221
|
-
# is found default to eth0.
|
222
|
-
def scan_interfaces
|
223
|
-
first_with_medium = self.class.interfaces.find {
|
224
|
-
|iface| iface.medium_present?
|
225
|
-
}
|
226
|
-
|
227
|
-
if first_with_medium
|
228
|
-
first_with_medium.name
|
229
|
-
else
|
230
|
-
# if we get here no interface was found with a cable attached
|
231
|
-
# default to eth0 and hope for the best
|
232
|
-
STDERR.puts "warning: no suitable interface found, using eth0"
|
233
|
-
'eth0'
|
234
|
-
end
|
235
|
-
end
|
185
|
+
def safe_assign
|
186
|
+
[ :interface_name ]
|
236
187
|
end
|
237
188
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
@ssid = args['ssid'] || ''
|
244
|
-
@interface_name = args['interface_name'] if args['interface_name']
|
245
|
-
@wpa_config_file = '/tmp/wpa_supplicant.concerto.conf'
|
246
|
-
end
|
189
|
+
def args
|
190
|
+
{
|
191
|
+
'interface_name' => @interface_name
|
192
|
+
}
|
193
|
+
end
|
247
194
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
# If the user has requested a specific interface, use it.
|
252
|
-
# Otherwise, just pick the first wlan interface, assuming
|
253
|
-
# it works and all wlan interfaces have approximately equal
|
254
|
-
# reception. When this assumption is wrong the user must force.
|
255
|
-
if @interface_name && @interface_name != ''
|
256
|
-
@interface_name
|
257
|
-
else
|
258
|
-
self.class.interfaces[0].name
|
259
|
-
end
|
260
|
-
end
|
195
|
+
def interfaces_lines
|
196
|
+
[]
|
197
|
+
end
|
261
198
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
199
|
+
def validate
|
200
|
+
if @interface_name != ''
|
201
|
+
if self.class.interfaces.find {
|
202
|
+
|iface| iface.name == @interface_name
|
203
|
+
}.nil?
|
204
|
+
fail "The interface doesn't exist on the system"
|
266
205
|
end
|
206
|
+
end
|
207
|
+
end
|
267
208
|
|
268
|
-
|
269
|
-
|
270
|
-
|
209
|
+
# Try to find all wired interfaces on the system.
|
210
|
+
def self.interfaces
|
211
|
+
# This is somewhat Linux specific and may miss some oddballs.
|
212
|
+
devices = Dir.glob('/sys/class/net/eth*')
|
213
|
+
devices.map { |d| Interface.new(File.basename(d)) }
|
214
|
+
end
|
271
215
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
# long lines, sorry!
|
276
|
-
wpaconf.puts "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev"
|
277
|
-
wpaconf.puts "network={"
|
278
|
-
wpaconf.puts "ssid=\"#{@ssid}\""
|
279
|
-
wpaconf.puts "scan_ssid=1"
|
280
|
-
wpaconf.puts "key_mgmt=NONE"
|
281
|
-
wpaconf.puts "}"
|
282
|
-
end
|
283
|
-
end
|
216
|
+
def self.description
|
217
|
+
"Wired connection"
|
218
|
+
end
|
284
219
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
220
|
+
private
|
221
|
+
# Find the first wired interface with medium present. If none
|
222
|
+
# is found default to eth0.
|
223
|
+
def scan_interfaces
|
224
|
+
first_with_medium = self.class.interfaces.find {
|
225
|
+
|iface| iface.medium_present?
|
226
|
+
}
|
227
|
+
|
228
|
+
if first_with_medium
|
229
|
+
first_with_medium.name
|
230
|
+
else
|
231
|
+
# if we get here no interface was found with a cable attached
|
232
|
+
# default to eth0 and hope for the best
|
233
|
+
STDERR.puts "warning: no suitable interface found, using eth0"
|
234
|
+
'eth0'
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# 802.11* unencrypted wireless connections.
|
240
|
+
# These are managed by wpa_supplicant on Debian so we need to create its
|
241
|
+
# configuration file and link it to the interfaces file.
|
242
|
+
class WirelessConnection
|
243
|
+
def initialize(args={})
|
244
|
+
@ssid = args['ssid'] || ''
|
245
|
+
@interface_name = args['interface_name'] if args['interface_name']
|
246
|
+
@wpa_config_file = '/tmp/wpa_supplicant.concerto.conf'
|
247
|
+
end
|
289
248
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
249
|
+
attr_accessor :ssid, :interface_name
|
250
|
+
|
251
|
+
def config_interface_name
|
252
|
+
# If the user has requested a specific interface, use it.
|
253
|
+
# Otherwise, just pick the first wlan interface, assuming
|
254
|
+
# it works and all wlan interfaces have approximately equal
|
255
|
+
# reception. When this assumption is wrong the user must force.
|
256
|
+
if @interface_name && @interface_name != ''
|
257
|
+
@interface_name
|
258
|
+
else
|
259
|
+
self.class.interfaces[0].name
|
260
|
+
end
|
261
|
+
end
|
296
262
|
|
297
|
-
|
298
|
-
|
299
|
-
|
263
|
+
def validate
|
264
|
+
if @ssid == ''
|
265
|
+
fail "Need SSID for wireless connection"
|
266
|
+
end
|
267
|
+
end
|
300
268
|
|
301
|
-
|
302
|
-
|
303
|
-
devices = Dir.glob('/sys/class/net/{ath,wlan}*')
|
304
|
-
devices.map { |d| Interface.new(File.basename(d)) }
|
305
|
-
end
|
269
|
+
def safe_assign
|
270
|
+
[ :ssid, :interface_name ]
|
306
271
|
end
|
307
272
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
273
|
+
def write_configs
|
274
|
+
# Write a wpa_supplicant.conf file for an unsecured network.
|
275
|
+
File.open(@wpa_config_file, 'w') do |wpaconf|
|
276
|
+
# long lines, sorry!
|
277
|
+
wpaconf.puts "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev"
|
278
|
+
wpaconf.puts "network={"
|
279
|
+
wpaconf.puts "ssid=\"#{@ssid}\""
|
280
|
+
wpaconf.puts "scan_ssid=1"
|
281
|
+
wpaconf.puts "key_mgmt=NONE"
|
282
|
+
wpaconf.puts "}"
|
283
|
+
end
|
284
|
+
end
|
319
285
|
|
320
|
-
|
321
|
-
|
322
|
-
|
286
|
+
def interfaces_lines
|
287
|
+
# This links the wpa config to the interfaces file.
|
288
|
+
["wpa-conf #{@wpa_config_file}"]
|
289
|
+
end
|
323
290
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
}
|
331
|
-
end
|
291
|
+
def args
|
292
|
+
{
|
293
|
+
'interface_name' => @interface_name,
|
294
|
+
'ssid' => @ssid
|
295
|
+
}
|
296
|
+
end
|
332
297
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
"netmask #{@netmask}",
|
337
|
-
"gateway #{@gateway}"
|
338
|
-
]
|
339
|
-
end
|
298
|
+
def self.description
|
299
|
+
"Wireless connection (no encryption)"
|
300
|
+
end
|
340
301
|
|
302
|
+
def self.interfaces
|
303
|
+
# Again this is not guaranteed to be a catch all.
|
304
|
+
devices = Dir.glob('/sys/class/net/{ath,wlan}*')
|
305
|
+
devices.map { |d| Interface.new(File.basename(d)) }
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Static IPv4 addressing.
|
310
|
+
# We use the IPAddress gem to validate that the address information
|
311
|
+
# is vaguely correct (weeding out errors like the gateway
|
312
|
+
# being on another subnet)
|
313
|
+
class StaticAddressing
|
314
|
+
def initialize(args={})
|
315
|
+
@nameservers = args['nameservers']
|
316
|
+
@address = args['address']
|
317
|
+
@netmask = args['netmask']
|
318
|
+
@gateway = args['gateway']
|
319
|
+
end
|
341
320
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
resolvconf.puts("nameserver #{nameserver}");
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
321
|
+
def addressing_type
|
322
|
+
'static'
|
323
|
+
end
|
349
324
|
|
350
|
-
|
351
|
-
|
352
|
-
|
325
|
+
def args
|
326
|
+
{
|
327
|
+
'address' => @address,
|
328
|
+
'netmask' => @netmask,
|
329
|
+
'gateway' => @gateway,
|
330
|
+
'nameservers' => @nameservers
|
331
|
+
}
|
332
|
+
end
|
353
333
|
|
354
|
-
|
334
|
+
def interfaces_lines
|
335
|
+
[
|
336
|
+
"address #{@address}",
|
337
|
+
"netmask #{@netmask}",
|
338
|
+
"gateway #{@gateway}"
|
339
|
+
]
|
340
|
+
end
|
355
341
|
|
356
|
-
def safe_assign
|
357
|
-
[ :address, :netmask, :gateway, :nameservers_flat ]
|
358
|
-
end
|
359
342
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
if not IPAddress.valid_ipv4?(@address)
|
366
|
-
fail "Static address is invalid"
|
367
|
-
end
|
368
|
-
|
369
|
-
p @netmask
|
370
|
-
if not IPAddress.valid_ipv4_netmask?(@netmask)
|
371
|
-
fail "Static netmask is invalid"
|
372
|
-
end
|
373
|
-
|
374
|
-
p @netmask
|
375
|
-
subnet = IPAddress::IPv4.new(@address)
|
376
|
-
subnet.netmask = @netmask
|
377
|
-
if not subnet.include? IPAddress::IPv4.new(gateway)
|
378
|
-
fail "Gateway provided is unreachable"
|
379
|
-
end
|
343
|
+
def write_configs
|
344
|
+
File.open('/etc/resolv.conf','w') do |resolvconf|
|
345
|
+
@nameservers.each do |nameserver|
|
346
|
+
resolvconf.puts("nameserver #{nameserver}");
|
380
347
|
end
|
348
|
+
end
|
349
|
+
end
|
381
350
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
def nameservers_flat=(separated_list)
|
386
|
-
servers = separated_list.strip.split(/\s*[,|:;\s]\s*/)
|
387
|
-
servers.each do |server|
|
388
|
-
server.strip!
|
389
|
-
p server
|
390
|
-
if not IPAddress.valid? server
|
391
|
-
fail "One or more invalid IP addresses in nameserver list"
|
392
|
-
end
|
393
|
-
end
|
394
|
-
@nameservers = servers
|
395
|
-
end
|
351
|
+
def self.description
|
352
|
+
"Static Addressing"
|
353
|
+
end
|
396
354
|
|
397
|
-
|
398
|
-
|
399
|
-
|
355
|
+
attr_accessor :address, :netmask, :gateway, :nameservers
|
356
|
+
|
357
|
+
def safe_assign
|
358
|
+
[ :address, :netmask, :gateway, :nameservers_flat ]
|
400
359
|
end
|
401
360
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
361
|
+
def validate
|
362
|
+
@address.strip!
|
363
|
+
@netmask.strip!
|
364
|
+
@gateway.strip!
|
365
|
+
|
366
|
+
if not IPAddress.valid_ipv4?(@address)
|
367
|
+
fail "Static address is invalid"
|
368
|
+
end
|
369
|
+
|
370
|
+
p @netmask
|
371
|
+
if not IPAddress.valid_ipv4_netmask?(@netmask)
|
372
|
+
fail "Static netmask is invalid"
|
373
|
+
end
|
374
|
+
|
375
|
+
p @netmask
|
376
|
+
subnet = IPAddress::IPv4.new(@address)
|
377
|
+
subnet.netmask = @netmask
|
378
|
+
if not subnet.include? IPAddress::IPv4.new(gateway)
|
379
|
+
fail "Gateway provided is unreachable"
|
380
|
+
end
|
381
|
+
end
|
407
382
|
|
408
|
-
|
409
|
-
|
410
|
-
|
383
|
+
# These next two methods are for the web interface, where it's
|
384
|
+
# more convenient to enter a bunch of nameservers on one line
|
385
|
+
# than to have to deal with an array of fields.
|
386
|
+
def nameservers_flat=(separated_list)
|
387
|
+
servers = separated_list.strip.split(/\s*[,|:;\s]\s*/)
|
388
|
+
servers.each do |server|
|
389
|
+
server.strip!
|
390
|
+
p server
|
391
|
+
if not IPAddress.valid? server
|
392
|
+
fail "One or more invalid IP addresses in nameserver list"
|
393
|
+
end
|
394
|
+
end
|
395
|
+
@nameservers = servers
|
396
|
+
end
|
411
397
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
end
|
398
|
+
def nameservers_flat
|
399
|
+
@nameservers.join(',')
|
400
|
+
end
|
401
|
+
end
|
417
402
|
|
418
|
-
|
419
|
-
|
420
|
-
|
403
|
+
# Dynamic IPv4 addressing via DHCP
|
404
|
+
class DHCPAddressing
|
405
|
+
def initialize(args={})
|
406
|
+
# we accept no args
|
407
|
+
end
|
421
408
|
|
422
|
-
|
423
|
-
|
424
|
-
|
409
|
+
def addressing_type
|
410
|
+
'dhcp'
|
411
|
+
end
|
425
412
|
|
426
|
-
|
427
|
-
|
428
|
-
|
413
|
+
def interfaces_lines
|
414
|
+
# DHCP needs no additional interfaces args
|
415
|
+
# from the addressing side
|
416
|
+
[]
|
417
|
+
end
|
429
418
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
end
|
419
|
+
def validate
|
420
|
+
# nothing to validate
|
421
|
+
end
|
434
422
|
|
435
|
-
|
436
|
-
|
437
|
-
end
|
423
|
+
def safe_assign
|
424
|
+
[] # no args
|
438
425
|
end
|
439
426
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
end
|
475
|
-
|
476
|
-
# Save the network configuration to the configuration store.
|
477
|
-
# Arguments are instances of connection method and addressing
|
478
|
-
# method classes. Throws exception if either one is not valid.
|
479
|
-
def self.write_network_config(cm, am)
|
480
|
-
# Check that everything is consistent. If not, we currently throw
|
481
|
-
# an exception, which probably is not the best long term solution.
|
482
|
-
cm.validate
|
483
|
-
am.validate
|
484
|
-
|
485
|
-
# Serialize our instances as JSON data to be written to the config file.
|
486
|
-
json_data = {
|
487
|
-
'connection_method' => cm.class.basename,
|
488
|
-
'connection_method_args' => cm.args,
|
489
|
-
'addressing_method' => am.class.basename,
|
490
|
-
'addressing_method_args' => am.args
|
491
|
-
}.to_json
|
492
|
-
|
493
|
-
# Save the serialized configuration.
|
494
|
-
ConfigStore.write_config('network_config', json_data)
|
495
|
-
end
|
496
|
-
|
497
|
-
# This reads a JSON configuration file on STDIN and writes the interfaces
|
498
|
-
# file. Also the classes instantiated will have a chance to write
|
499
|
-
# out any auxiliary files needed.
|
500
|
-
def self.configure_system_network
|
501
|
-
connection_method, addressing_method = read_network_config
|
502
|
-
|
503
|
-
ifname = connection_method.config_interface_name
|
504
|
-
|
505
|
-
# squirrel away the name of the interface we are configuring
|
506
|
-
# This will be useful later for getting network status information.
|
507
|
-
ConfigStore.write_config('network_interface', ifname)
|
508
|
-
|
509
|
-
# Write the /etc/network/interfaces file.
|
510
|
-
File.open(INTERFACES_FILE, 'w') do |f|
|
511
|
-
f.puts "# Concerto Live network configuration"
|
512
|
-
f.puts "# Generated by netconfig.rb"
|
513
|
-
f.puts "# Changes will be lost on reboot"
|
514
|
-
f.puts "auto lo"
|
515
|
-
f.puts "iface lo inet loopback"
|
516
|
-
f.puts ""
|
517
|
-
f.puts "auto #{ifname}"
|
518
|
-
f.puts "iface #{ifname} inet #{addressing_method.addressing_type}"
|
519
|
-
|
520
|
-
addressing_method.interfaces_lines.each do |line|
|
521
|
-
f.puts "\t#{line}"
|
522
|
-
end
|
523
|
-
|
524
|
-
connection_method.interfaces_lines.each do |line|
|
525
|
-
f.puts "\t#{line}"
|
526
|
-
end
|
527
|
-
end
|
427
|
+
def args
|
428
|
+
{ }
|
429
|
+
end
|
430
|
+
|
431
|
+
def write_configs
|
432
|
+
# dhclient will write our resolv.conf so we do not need
|
433
|
+
# to do anything
|
434
|
+
end
|
435
|
+
|
436
|
+
def self.description
|
437
|
+
"Dynamic Addressing - DHCP"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# Read a JSON formatted network configuration from the config store.
|
442
|
+
# This instantiates the connection and addressing method classes
|
443
|
+
# and returns the instances i.e. cm, am = read_network_config
|
444
|
+
#
|
445
|
+
# If no configuration is saved or it is corrupt this returns
|
446
|
+
# a default configuration that is somewhat likely to work.
|
447
|
+
def self.read_network_config
|
448
|
+
input = ConfigStore.read_config('network_config', '')
|
449
|
+
|
450
|
+
begin
|
451
|
+
args = JSON.parse(input)
|
452
|
+
rescue
|
453
|
+
# set up some sane defaults if we have no configuration
|
454
|
+
# or it can't be parsed
|
455
|
+
args = {
|
456
|
+
'connection_method' => 'WiredConnection',
|
457
|
+
'addressing_method' => 'DHCPAddressing',
|
458
|
+
'connection_method_args' => { },
|
459
|
+
'addressing_method_args' => { }
|
460
|
+
}
|
461
|
+
end
|
528
462
|
|
529
|
-
|
530
|
-
|
463
|
+
connection_method_class = Bandshell.const_get(args['connection_method'])
|
464
|
+
addressing_method_class = Bandshell.const_get(args['addressing_method'])
|
465
|
+
|
466
|
+
connection_method = connection_method_class.new(
|
467
|
+
args['connection_method_args']
|
468
|
+
)
|
469
|
+
|
470
|
+
addressing_method = addressing_method_class.new(
|
471
|
+
args['addressing_method_args']
|
472
|
+
)
|
473
|
+
|
474
|
+
return [connection_method, addressing_method]
|
475
|
+
end
|
476
|
+
|
477
|
+
# Save the network configuration to the configuration store.
|
478
|
+
# Arguments are instances of connection method and addressing
|
479
|
+
# method classes. Throws exception if either one is not valid.
|
480
|
+
def self.write_network_config(cm, am)
|
481
|
+
# Check that everything is consistent. If not, we currently throw
|
482
|
+
# an exception, which probably is not the best long term solution.
|
483
|
+
cm.validate
|
484
|
+
am.validate
|
485
|
+
|
486
|
+
# Serialize our instances as JSON data to be written to the config file.
|
487
|
+
json_data = {
|
488
|
+
'connection_method' => cm.class.basename,
|
489
|
+
'connection_method_args' => cm.args,
|
490
|
+
'addressing_method' => am.class.basename,
|
491
|
+
'addressing_method_args' => am.args
|
492
|
+
}.to_json
|
493
|
+
|
494
|
+
# Save the serialized configuration.
|
495
|
+
ConfigStore.write_config('network_config', json_data)
|
496
|
+
end
|
497
|
+
|
498
|
+
# This reads a JSON configuration file on STDIN and writes the interfaces
|
499
|
+
# file. Also the classes instantiated will have a chance to write
|
500
|
+
# out any auxiliary files needed.
|
501
|
+
def self.configure_system_network
|
502
|
+
connection_method, addressing_method = read_network_config
|
503
|
+
|
504
|
+
ifname = connection_method.config_interface_name
|
505
|
+
|
506
|
+
# squirrel away the name of the interface we are configuring
|
507
|
+
# This will be useful later for getting network status information.
|
508
|
+
ConfigStore.write_config('network_interface', ifname)
|
509
|
+
|
510
|
+
# Write the /etc/network/interfaces file.
|
511
|
+
File.open(INTERFACES_FILE, 'w') do |f|
|
512
|
+
f.puts "# Concerto Live network configuration"
|
513
|
+
f.puts "# Generated by netconfig.rb"
|
514
|
+
f.puts "# Changes will be lost on reboot"
|
515
|
+
f.puts "auto lo"
|
516
|
+
f.puts "iface lo inet loopback"
|
517
|
+
f.puts ""
|
518
|
+
f.puts "auto #{ifname}"
|
519
|
+
f.puts "iface #{ifname} inet #{addressing_method.addressing_type}"
|
520
|
+
|
521
|
+
addressing_method.interfaces_lines.each do |line|
|
522
|
+
f.puts "\t#{line}"
|
523
|
+
end
|
524
|
+
|
525
|
+
connection_method.interfaces_lines.each do |line|
|
526
|
+
f.puts "\t#{line}"
|
527
|
+
end
|
531
528
|
end
|
532
529
|
|
533
|
-
#
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
530
|
+
# Write auxiliary configuration files.
|
531
|
+
connection_method.write_configs
|
532
|
+
end
|
533
|
+
|
534
|
+
# Get the name of the interface we configured
|
535
|
+
def self.configured_interface
|
536
|
+
ifname = ConfigStore.read_config('network_interface', '')
|
537
|
+
if ifname != ''
|
538
|
+
Interface.new(ifname)
|
539
|
+
else
|
540
|
+
nil
|
541
541
|
end
|
542
|
+
end
|
542
543
|
end
|