bandshell 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|