blondy-dhcpd 0.0.1 → 0.0.2
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 +4 -4
- data/Gemfile +4 -1
- data/README.md +13 -1
- data/Rakefile +5 -0
- data/TODO.md +3 -0
- data/bin/blondy-dhcpd +67 -0
- data/blondy-dhcpd.gemspec +3 -0
- data/lib/blondy/dhcpd/cache.rb +33 -0
- data/lib/blondy/dhcpd/config.rb +56 -0
- data/lib/blondy/dhcpd/dispatcher.rb +83 -12
- data/lib/blondy/dhcpd/logger.rb +10 -0
- data/lib/blondy/dhcpd/pool.rb +78 -0
- data/lib/blondy/dhcpd/reply.rb +37 -0
- data/lib/blondy/dhcpd/server.rb +35 -2
- data/lib/blondy/dhcpd/version.rb +1 -1
- data/spec/cache_spec.rb +50 -0
- data/spec/dispatcher_spec.rb +194 -35
- data/spec/pool_spec.rb +122 -0
- data/spec/reply_spec.rb +9 -0
- data/spec/server_spec.rb +69 -8
- data/spec/spec_helper.rb +49 -5
- metadata +76 -22
- data/lib/blondy/dhcpd.rb +0 -10
data/spec/dispatcher_spec.rb
CHANGED
@@ -4,21 +4,69 @@ module Blondy
|
|
4
4
|
module DHCPD
|
5
5
|
describe 'Dispatcher' do
|
6
6
|
subject(:dispatcher) {Dispatcher}
|
7
|
+
let(:pool) {Pool}
|
8
|
+
let(:pool_query_result) do
|
9
|
+
pr = OpenStruct.new
|
10
|
+
pr.data = OpenStruct.new
|
11
|
+
pr.data.fname = String.new
|
12
|
+
pr.data.yiaddr = '0.0.0.0'
|
13
|
+
pr.data.options = Array.new
|
14
|
+
pr.code = 200
|
15
|
+
pr
|
16
|
+
end
|
7
17
|
let(:from_ip) {'0.0.0.0'}
|
8
18
|
let(:from_port) {67}
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
19
|
+
[ :discover, :request, :release, :inform ].each do |message|
|
20
|
+
let(message) do
|
21
|
+
msg_class = DHCP.const_get message.to_s.capitalize.to_sym
|
22
|
+
d = msg_class.new
|
23
|
+
d.xid = 123456789
|
24
|
+
d
|
25
|
+
end
|
26
|
+
end
|
27
|
+
let(:reply) {OpenStruct.new}
|
28
|
+
|
29
|
+
before(:each) do
|
30
|
+
allow(pool).to receive(:query).and_return(pool_query_result)
|
31
|
+
allow(pool).to receive(:query).with('ee:ee:ee:ee:ee:ee', :discover).and_return(pool_query_result)
|
32
|
+
end
|
33
|
+
|
34
|
+
shared_examples 'Dispatcher' do |message|
|
35
|
+
it 'data is correct' do
|
36
|
+
dispatcher.dispatch(eval(message.to_s).pack, from_ip, from_port).data.pack.should == reply.data.pack
|
37
|
+
end
|
38
|
+
it 'ip is correct' do
|
39
|
+
dispatcher.dispatch(eval(message.to_s).pack, from_ip, from_port).ip.should == reply.ip
|
40
|
+
end
|
41
|
+
it 'port is correct' do
|
42
|
+
dispatcher.dispatch(eval(message.to_s).pack, from_ip, from_port).port.should == reply.port
|
43
|
+
end
|
13
44
|
end
|
14
45
|
|
15
|
-
|
16
|
-
|
17
|
-
|
46
|
+
%w{discover request release inform}.each do |message|
|
47
|
+
context "receive dhcp #{message} message" do
|
48
|
+
it "dispatch it to specific #{message}_handler private method" do
|
49
|
+
dispatcher.should_receive("#{message}_handler".to_sym)
|
50
|
+
dispatcher.dispatch(eval(message).pack, from_ip, from_port)
|
51
|
+
end
|
52
|
+
if %w{discover request inform}.include? message
|
53
|
+
it 'reply xid is the same as received xid' do
|
54
|
+
dispatcher.dispatch(eval(message).pack, from_ip, from_port).data.xid.should == eval(message).xid
|
55
|
+
end
|
56
|
+
end
|
18
57
|
end
|
19
58
|
end
|
20
59
|
|
21
|
-
context '
|
60
|
+
context 'wrong message' do
|
61
|
+
it 'false when message is unknown' do
|
62
|
+
lambda { dispatcher.dispatch("abracadabra", from_ip, from_port) }.should raise_error(IncorrectMessage)
|
63
|
+
end
|
64
|
+
it 'false when action for message unspecified' do
|
65
|
+
lambda { dispatcher.dispatch("abracadabra", from_ip, from_port) }.should raise_error(IncorrectMessage)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'receive discovery message' do
|
22
70
|
# RFC 2131 (http://www.ietf.org/rfc/rfc2131.txt)
|
23
71
|
#
|
24
72
|
# http://stackoverflow.com/a/10757849
|
@@ -34,44 +82,155 @@ module Blondy
|
|
34
82
|
# then the server unicasts DHCPOFFER and DHCPACK messages to the client's hardware address
|
35
83
|
# and 'yiaddr' address. In all cases, when 'giaddr' is zero,
|
36
84
|
# the server broadcasts any DHCPNAK messages to 0xffffffff.
|
37
|
-
|
38
|
-
|
85
|
+
|
86
|
+
before(:each) do
|
87
|
+
discover.chaddr = [238, 238, 238, 238, 238, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
88
|
+
discover.hlen = 6
|
89
|
+
pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
|
90
|
+
pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
|
91
|
+
pool_query_result.data.options = [
|
92
|
+
DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_OFFER]}),
|
93
|
+
DHCP::ServerIdentifierOption.new({payload: [192, 168, 1, 1]}),
|
94
|
+
DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
|
95
|
+
DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
|
96
|
+
DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
|
97
|
+
DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
|
98
|
+
DHCP::RouterOption.new({payload: [192, 168, 1, 1]})
|
99
|
+
]
|
39
100
|
reply.data = DHCP::Offer.new
|
40
|
-
reply
|
101
|
+
reply.data.xid = discover.xid
|
102
|
+
reply.data.options = pool_query_result.data.options
|
103
|
+
reply.data.yiaddr = pool_query_result.data.yiaddr
|
104
|
+
reply.data.fname = pool_query_result.data.fname
|
105
|
+
reply.data.siaddr = IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'reply with offer' do
|
109
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).data.class == DHCP::Offer
|
41
110
|
end
|
42
111
|
|
43
112
|
context 'giaddr != 0' do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
113
|
+
#reply with offer message to bootp relay
|
114
|
+
before(:each) do
|
115
|
+
giaddr = '192.168.3.3'
|
116
|
+
discover.giaddr = IPAddr.new(giaddr).to_i
|
117
|
+
reply.ip = giaddr
|
118
|
+
reply.port = 67
|
49
119
|
end
|
120
|
+
it_should_behave_like 'Dispatcher', :discover
|
50
121
|
end
|
51
122
|
context 'giaddr = 0 and ciaddr != 0' do
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
123
|
+
#send offer message to client
|
124
|
+
before(:each) do
|
125
|
+
ciaddr = '192.168.3.4'
|
126
|
+
discover.ciaddr = IPAddr.new(ciaddr).to_i
|
127
|
+
reply.ip = ciaddr
|
128
|
+
reply.port = 68
|
57
129
|
end
|
130
|
+
it_should_behave_like 'Dispatcher', :discover
|
58
131
|
end
|
59
|
-
context 'giaddr = 0 and ciaddr = 0
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
132
|
+
context 'giaddr = 0 and ciaddr = 0' do
|
133
|
+
#send offer message to client by broadcast to 255.255.255.255
|
134
|
+
before(:each) do
|
135
|
+
discover.flags = 1
|
136
|
+
reply.ip = '255.255.255.255'
|
137
|
+
reply.port = 68
|
65
138
|
end
|
139
|
+
it_should_behave_like 'Dispatcher', :discover
|
66
140
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
141
|
+
|
142
|
+
context 'ask pool for configuration' do
|
143
|
+
context 'query already found in cache' do
|
144
|
+
it 'not reply for message' do
|
145
|
+
pool.should_receive(:query).with('ee:ee:ee:ee:ee:ee', :discover).and_return(false)
|
146
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).should be_false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
context 'query not found in cache' do
|
150
|
+
it 'set reply fields according to pool query result' do
|
151
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).data.fname.should == pool_query_result.data.fname
|
152
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).data.yiaddr.should == pool_query_result.data.yiaddr
|
153
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).data.options.should == pool_query_result.data.options
|
154
|
+
end
|
155
|
+
it 'set siaddr to server ip address' do
|
156
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).data.siaddr.should == IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe 'receive request message' do
|
163
|
+
before(:each) do
|
164
|
+
request.chaddr = [238, 238, 238, 238, 238, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
165
|
+
request.hlen = 6
|
166
|
+
pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
|
167
|
+
pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
|
168
|
+
pool_query_result.data.options = [
|
169
|
+
DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_ACK]}),
|
170
|
+
DHCP::ServerIdentifierOption.new({payload: [192, 168, 1, 1]}),
|
171
|
+
DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
|
172
|
+
DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
|
173
|
+
DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
|
174
|
+
DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
|
175
|
+
DHCP::RouterOption.new({payload: [192, 168, 1, 1]})
|
176
|
+
]
|
177
|
+
reply.data = DHCP::ACK.new
|
178
|
+
reply.data.xid = request.xid
|
179
|
+
reply.data.options = pool_query_result.data.options
|
180
|
+
reply.data.yiaddr = pool_query_result.data.yiaddr
|
181
|
+
reply.data.fname = pool_query_result.data.fname
|
182
|
+
reply.data.siaddr = IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
|
183
|
+
message = request
|
184
|
+
end
|
185
|
+
it 'reply with ack' do
|
186
|
+
dispatcher.dispatch(discover.pack, from_ip, from_port).data.class == DHCP::ACK
|
187
|
+
end
|
188
|
+
context 'giaddr != 0' do
|
189
|
+
#reply with ack message to bootp relay
|
190
|
+
before(:each) do
|
191
|
+
giaddr = '192.168.3.3'
|
192
|
+
request.giaddr = IPAddr.new(giaddr).to_i
|
193
|
+
reply.ip = giaddr
|
194
|
+
reply.port = 67
|
195
|
+
end
|
196
|
+
it_should_behave_like 'Dispatcher', :request
|
197
|
+
end
|
198
|
+
context 'giaddr = 0 and ciaddr != 0' do
|
199
|
+
#send ack message to client
|
200
|
+
before(:each) do
|
201
|
+
ciaddr = '192.168.3.4'
|
202
|
+
request.ciaddr = IPAddr.new(ciaddr).to_i
|
203
|
+
reply.ip = ciaddr
|
204
|
+
reply.port = 68
|
205
|
+
end
|
206
|
+
it_should_behave_like 'Dispatcher', :request
|
207
|
+
end
|
208
|
+
context 'giaddr = 0 and ciaddr = 0' do
|
209
|
+
#send ack message to client by broadcast to 255.255.255.255
|
210
|
+
before(:each) do
|
211
|
+
request.flags = 1
|
212
|
+
reply.ip = '255.255.255.255'
|
213
|
+
reply.port = 68
|
214
|
+
end
|
215
|
+
it_should_behave_like 'Dispatcher', :request
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'ask pool for configuration' do
|
219
|
+
context 'query already found in cache' do
|
220
|
+
it 'not reply for message' do
|
221
|
+
pool.should_receive(:query).with('ee:ee:ee:ee:ee:ee', :request).and_return(false)
|
222
|
+
dispatcher.dispatch(request.pack, from_ip, from_port).should be_false
|
223
|
+
end
|
224
|
+
end
|
225
|
+
context 'query not found in cache' do
|
226
|
+
it 'set reply fields according to pool query result' do
|
227
|
+
dispatcher.dispatch(request.pack, from_ip, from_port).data.fname.should == pool_query_result.data.fname
|
228
|
+
dispatcher.dispatch(request.pack, from_ip, from_port).data.yiaddr.should == pool_query_result.data.yiaddr
|
229
|
+
dispatcher.dispatch(request.pack, from_ip, from_port).data.options.should == pool_query_result.data.options
|
230
|
+
end
|
231
|
+
it 'set siaddr to server ip address' do
|
232
|
+
dispatcher.dispatch(request.pack, from_ip, from_port).data.siaddr.should == IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
|
233
|
+
end
|
75
234
|
end
|
76
235
|
end
|
77
236
|
end
|
data/spec/pool_spec.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Blondy
|
4
|
+
module DHCPD
|
5
|
+
|
6
|
+
describe 'Pool' do
|
7
|
+
subject(:pool) {Pool}
|
8
|
+
let(:cache) {Cache}
|
9
|
+
let(:logger) {Logger}
|
10
|
+
let(:remote_json) { {'fname' => 'test.txt',
|
11
|
+
'yiaddr' => '192.168.5.150',
|
12
|
+
'domain' => 'example.com',
|
13
|
+
'dns' => '8.8.8.8',
|
14
|
+
'gw' => '192.168.1.3',
|
15
|
+
'netmask' => '255.255.255.255' }.to_json }
|
16
|
+
let(:remote_invalid_json) { { 'netaddr' => 'invalid', 'yomask' => '' }.to_json }
|
17
|
+
let(:reply_data) do
|
18
|
+
pool_query_result = OpenStruct.new
|
19
|
+
pool_query_result.data = OpenStruct.new
|
20
|
+
pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
|
21
|
+
pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
|
22
|
+
pool_query_result.data.options = [
|
23
|
+
DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_OFFER]}),
|
24
|
+
DHCP::ServerIdentifierOption.new({payload: Blondy::DHCPD::CONFIG['server_ip'].split('.').map {|octet| octet.to_i}}),
|
25
|
+
DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
|
26
|
+
DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
|
27
|
+
DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
|
28
|
+
DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
|
29
|
+
DHCP::RouterOption.new({payload: [192, 168, 1, 3]})
|
30
|
+
]
|
31
|
+
pool_query_result
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'receive query' do
|
35
|
+
|
36
|
+
before(:each) do
|
37
|
+
cache.flush
|
38
|
+
stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
|
39
|
+
with(:headers => {'X-Blondy-Key'=>'abcd'}).
|
40
|
+
to_return(:status => 200, :body => remote_json, :headers => {})
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'check for reply in cache' do
|
44
|
+
cache.should_receive(:query).with('11:11:11:11:11:11', :discover)
|
45
|
+
pool.query('11:11:11:11:11:11', :discover)
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'not found in cache' do
|
49
|
+
before(:each) do
|
50
|
+
allow(cache).to receive(:query).with('11:11:11:11:11:11', :discover).and_return(false)
|
51
|
+
end
|
52
|
+
it 'initiate query to remote server' do
|
53
|
+
pool.query('11:11:11:11:11:11', :discover)
|
54
|
+
WebMock.should have_requested(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover")
|
55
|
+
end
|
56
|
+
it 'reply with false' do
|
57
|
+
pool.query('11:11:11:11:11:11', :discover).should == false
|
58
|
+
end
|
59
|
+
it 'set received data to cache', :no_em do
|
60
|
+
cache.stub(:query) { cache.unstub(:query); false }
|
61
|
+
EM.run_block { pool.query('11:11:11:11:11:11', :discover) }
|
62
|
+
cache.query('11:11:11:11:11:11', :discover)[:data].should == reply_data
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'found in cache' do
|
67
|
+
before(:each) do
|
68
|
+
allow(cache).to receive(:query).with('11:11:11:11:11:11', :discover).and_return({data:reply_data, time: Time.now})
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'return reply data' do
|
72
|
+
pool.query('11:11:11:11:11:11', :discover).should == reply_data
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'remote pool unavailable' do
|
77
|
+
before(:each) do
|
78
|
+
stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
|
79
|
+
with(:headers => {'X-Blondy-Key'=>'abcd'}).
|
80
|
+
to_timeout
|
81
|
+
end
|
82
|
+
it 'log that pool unavailable', :no_em do
|
83
|
+
logger.should_receive(:error).with('Remote pool server is unavailable.')
|
84
|
+
EM.run_block { pool.query('11:11:11:11:11:11', :discover) }
|
85
|
+
end
|
86
|
+
it 'not set values to cache' do
|
87
|
+
cache.should_not_receive(:add)
|
88
|
+
pool.query('11:11:11:11:11:11', :discover)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'remote pool send incorrect data' do
|
93
|
+
before(:each) do
|
94
|
+
stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
|
95
|
+
with(:headers => {'X-Blondy-Key'=>'abcd'}).
|
96
|
+
to_return(:status => 200, :body => remote_invalid_json, :headers => {})
|
97
|
+
end
|
98
|
+
it 'return false' do
|
99
|
+
pool.query('11:11:11:11:11:11', :discover).should be_false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'remote pool reply with error' do
|
104
|
+
before(:each) do
|
105
|
+
stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
|
106
|
+
with(:headers => {'X-Blondy-Key'=>'abcd'}).
|
107
|
+
to_return(:status => 500, :body => "", :headers => {})
|
108
|
+
end
|
109
|
+
it 'log error code', :no_em do
|
110
|
+
logger.should_receive(:error).with('Remote server reply with 500 error code.')
|
111
|
+
EM.run_block { pool.query('11:11:11:11:11:11', :discover) }
|
112
|
+
end
|
113
|
+
it 'not set values to cache' do
|
114
|
+
cache.should_not_receive(:add)
|
115
|
+
pool.query('11:11:11:11:11:11', :discover)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/spec/reply_spec.rb
ADDED
data/spec/server_spec.rb
CHANGED
@@ -17,26 +17,87 @@ module Blondy
|
|
17
17
|
d.xid = 123456789
|
18
18
|
d
|
19
19
|
end
|
20
|
+
let(:logger) {Logger}
|
20
21
|
|
21
22
|
before(:each) do
|
22
23
|
allow(EM).to receive(:open_udp_socket).and_return 0
|
23
24
|
allow(Socket).to receive(:unpack_sockaddr_in).and_return ['0.0.0.0', '68']
|
25
|
+
allow(Logger).to receive(:info)
|
26
|
+
allow(Logger).to receive(:warn)
|
27
|
+
allow(Logger).to receive(:error)
|
28
|
+
allow(Pool).to receive(:query)
|
24
29
|
end
|
25
30
|
|
26
|
-
|
31
|
+
it 'has defined buffer' do
|
32
|
+
server.instance_variable_defined?('@buffer').should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'receive message' do
|
27
36
|
before(:each) do
|
28
37
|
@ip, @port = Socket.unpack_sockaddr_in(server.get_peername)
|
29
38
|
end
|
30
|
-
|
31
|
-
|
32
|
-
server.receive_data(
|
39
|
+
|
40
|
+
it 'add received data in buffer' do
|
41
|
+
server.receive_data('test data')
|
42
|
+
server.instance_variable_get('@buffer').include?('test data').should be_true
|
33
43
|
end
|
34
|
-
|
35
|
-
|
36
|
-
|
44
|
+
context 'buffer include DHCP magic number' do
|
45
|
+
it 'run dispatcher' do
|
46
|
+
dispatcher.should_receive(:dispatch).with(discover.pack, @ip, @port)
|
47
|
+
server.receive_data(discover.pack)
|
48
|
+
end
|
49
|
+
it 'clear buffer' do
|
50
|
+
server.receive_data(discover.pack)
|
51
|
+
server.instance_variable_get('@buffer').size.should eq(0)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
context 'buffer not include DHCP magic' do
|
55
|
+
# Maximum dhcp message size is 576 bytes (but client can increase it)
|
56
|
+
# So we will try with 1000 bytes buffer
|
57
|
+
context 'data size in buffer is greater than 1000 bytes' do
|
58
|
+
it 'clear buffer' do
|
59
|
+
dummy_data = String.new
|
60
|
+
1000.times { dummy_data += rand(9).to_s }
|
61
|
+
server.instance_variable_set('@buffer', dummy_data)
|
62
|
+
server.receive_data(discover.pack.byteslice(0,15))
|
63
|
+
server.instance_variable_get('@buffer').size.should eq(15)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
it 'shoul not clear buffer' do
|
67
|
+
server.receive_data(discover.pack.byteslice(0,10))
|
68
|
+
server.instance_variable_get('@buffer').size.should eq(10)
|
69
|
+
end
|
37
70
|
end
|
38
|
-
end
|
39
71
|
|
72
|
+
context 'receive dhcp message' do
|
73
|
+
it 'send reply' do
|
74
|
+
reply = OpenStruct.new
|
75
|
+
reply.data = DHCP::Offer.new
|
76
|
+
allow(dispatcher).to receive(:dispatch).and_return(reply)
|
77
|
+
server.should_receive(:send_datagram)
|
78
|
+
server.receive_data(discover.pack)
|
79
|
+
end
|
80
|
+
context 'wrong data' do
|
81
|
+
it 'not send reply' do
|
82
|
+
server.should_not_receive(:send_datagram)
|
83
|
+
server.receive_data('abracadabra')
|
84
|
+
end
|
85
|
+
context 'can not convert received data to Message object' do
|
86
|
+
it 'should log error' do
|
87
|
+
logger.should_receive(:warn).with('Incorrect message received. Ignore.')
|
88
|
+
discover.options.shift
|
89
|
+
server.receive_data(discover.pack)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
context 'no handler for message' do
|
93
|
+
it 'should log error' do
|
94
|
+
logger.should_receive(:warn).with('No handler for message found. Ignore.')
|
95
|
+
server.receive_data(DHCP::Offer.new.pack)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
40
101
|
end
|
41
102
|
end
|
42
103
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,16 +4,31 @@
|
|
4
4
|
# loaded once.
|
5
5
|
#
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
require 'simplecov'
|
8
|
+
SimpleCov.start
|
7
9
|
|
8
10
|
require 'net-dhcp'
|
9
11
|
require 'eventmachine'
|
12
|
+
require 'em-http'
|
10
13
|
require 'ostruct'
|
11
14
|
require 'ipaddr'
|
12
15
|
require 'rspec'
|
16
|
+
require 'webmock/rspec'
|
17
|
+
require 'json'
|
18
|
+
require 'fiber'
|
19
|
+
require 'log4r'
|
20
|
+
|
13
21
|
|
14
22
|
spec_root = File.dirname(File.absolute_path(__FILE__))
|
15
|
-
|
23
|
+
exclude = %w{config.rb logger.rb}
|
24
|
+
Dir.glob(spec_root + '/../lib/blondy/dhcpd/*') {|file| require file unless exclude.include?(file.gsub(/^.*\//,''))}
|
16
25
|
|
26
|
+
# Set config for test
|
27
|
+
Blondy::DHCPD::CONFIG = Hash.new
|
28
|
+
Blondy::DHCPD::CONFIG['server_ip'] = '192.168.5.1'
|
29
|
+
Blondy::DHCPD::CONFIG['master'] = 'https://127.0.0.1/blondy/dhcpd'
|
30
|
+
Blondy::DHCPD::CONFIG['client_key'] = 'abcd'
|
31
|
+
Logger = Log4r::Logger.new 'ruby-dhcpd'
|
17
32
|
|
18
33
|
RSpec.configure do |config|
|
19
34
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
@@ -30,17 +45,46 @@ end
|
|
30
45
|
# Wrap for running examples inside eventmachine reactor
|
31
46
|
RSpec::Core::Example.class_eval do
|
32
47
|
alias ignorant_run run
|
48
|
+
class << self
|
49
|
+
attr_accessor :examples_fibers
|
50
|
+
end
|
51
|
+
self.examples_fibers = Array.new
|
33
52
|
|
34
53
|
def run(example_group_instance, reporter)
|
35
|
-
|
54
|
+
if @options[:no_em]
|
55
|
+
fibers_alive = [true]
|
56
|
+
while fibers_alive.include?(true)
|
57
|
+
fibers_alive = []
|
58
|
+
self.class.examples_fibers.each do |f|
|
59
|
+
f.resume if f.alive?
|
60
|
+
fibers_alive << f.alive?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
while EM.reactor_running?
|
64
|
+
EM.stop
|
65
|
+
sleep 0.5
|
66
|
+
end
|
67
|
+
ignorant_run example_group_instance, reporter
|
68
|
+
elsif @options[:no_fiber]
|
36
69
|
EM.run do
|
37
70
|
df = EM::DefaultDeferrable.new
|
38
71
|
result = nil
|
39
|
-
df.callback { |x| EM.stop_event_loop;
|
72
|
+
df.callback { |x| EM.stop_event_loop; result }
|
40
73
|
result = ignorant_run example_group_instance, reporter
|
41
74
|
df.succeed
|
42
75
|
end
|
43
|
-
|
76
|
+
else
|
77
|
+
f = Fiber.new do
|
78
|
+
EM.run do
|
79
|
+
df = EM::DefaultDeferrable.new
|
80
|
+
result = nil
|
81
|
+
df.callback { |x| EM.stop_event_loop; Fiber.yield result }
|
82
|
+
result = ignorant_run example_group_instance, reporter
|
83
|
+
df.succeed
|
84
|
+
end
|
85
|
+
end
|
86
|
+
self.class.examples_fibers << f
|
87
|
+
f.resume
|
88
|
+
end
|
44
89
|
end
|
45
90
|
end
|
46
|
-
|