blob: bd35f68f9f994aaedf399d3cde9f803d518caca7 [file] [log] [blame]
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package devices
18
19import (
20 "context"
21 "errors"
22 "fmt"
23 "github.com/google/gopacket"
24 "github.com/google/gopacket/layers"
25 "github.com/opencord/bbsim/internal/bbsim/devices"
26 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
27 "github.com/opencord/bbsim/internal/common"
Matteo Scandolo3de9de02019-11-14 13:40:03 -080028 "github.com/opencord/voltha-protos/v2/go/openolt"
Matteo Scandolo40e067f2019-10-16 16:59:41 -070029 log "github.com/sirupsen/logrus"
30 "google.golang.org/grpc"
31 "io"
32 "reflect"
33 "time"
34)
35
36type OltMock struct {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080037 LastUsedOnuId map[uint32]uint32
38 Olt *devices.OltDevice
39 BBSimIp string
40 BBSimPort string
41 BBSimApiPort string
Matteo Scandolo40e067f2019-10-16 16:59:41 -070042
43 conn *grpc.ClientConn
44
45 TargetOnus int
46 CompletedOnus int // Number of ONUs that have received a DHCPAck
47}
48
49// trigger an enable call and start the same listeners on the gRPC stream that VOLTHA would create
50// this method is blocking
51func (o *OltMock) Start() {
52 log.Info("Starting Mock OLT")
53
54 for _, pon := range o.Olt.Pons {
55 for _, onu := range pon.Onus {
Matteo Scandolod32c3822019-11-26 15:57:46 -070056 if err := onu.InternalState.Event("initialize"); err != nil {
57 log.Fatalf("Error initializing ONU: %v", err)
58 }
Matteo Scandolo583f17d2020-02-13 10:35:17 -080059 log.Debugf("Created ONU: %s (%d:%d)", onu.Sn(), onu.STag, onu.CTag)
Matteo Scandolo40e067f2019-10-16 16:59:41 -070060 }
61 }
62
63 client, conn := Connect(o.BBSimIp, o.BBSimPort)
64 o.conn = conn
65 defer conn.Close()
66
67 deviceInfo, err := o.getDeviceInfo(client)
68
69 if err != nil {
70 log.WithFields(log.Fields{
71 "error": err,
72 }).Fatal("Can't read device info")
73 }
74
75 log.WithFields(log.Fields{
76 "Vendor": deviceInfo.Vendor,
77 "Model": deviceInfo.Model,
78 "DeviceSerialNumber": deviceInfo.DeviceSerialNumber,
79 "PonPorts": deviceInfo.PonPorts,
80 }).Info("Retrieved device info")
81
82 o.readIndications(client)
83
84}
85
86func (o *OltMock) getDeviceInfo(client openolt.OpenoltClient) (*openolt.DeviceInfo, error) {
87 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
88 defer cancel()
89
90 return client.GetDeviceInfo(ctx, new(openolt.Empty))
91}
92
93func (o *OltMock) getOnuByTags(sTag int, cTag int) (*devices.Onu, error) {
94
95 for _, pon := range o.Olt.Pons {
96 for _, onu := range pon.Onus {
97 if onu.STag == sTag && onu.CTag == cTag {
98 return onu, nil
99 }
100 }
101 }
102
103 return nil, errors.New("cant-find-onu-by-c-s-tags")
104}
105
106func (o *OltMock) readIndications(client openolt.OpenoltClient) {
107 defer func() {
108 log.Info("OLT readIndications done")
109 }()
110
111 // Tell the OLT to start sending indications
112 indications, err := client.EnableIndication(context.Background(), new(openolt.Empty))
113 if err != nil {
114 log.WithFields(log.Fields{
115 "error": err,
116 }).Error("Failed to enable indication stream")
117 return
118 }
119
120 // listen for indications
121 for {
122 indication, err := indications.Recv()
123 if err == io.EOF {
124 break
125 }
126 if err != nil {
127
128 // the connection is closed once we have sent the DHCP_ACK packet to all of the ONUs
129 // it means BBR completed, it's not an error
130
131 log.WithFields(log.Fields{
132 "error": err,
133 }).Debug("Failed to read from indications")
134 break
135 }
136
137 o.handleIndication(client, indication)
138 }
139}
140
141func (o *OltMock) handleIndication(client openolt.OpenoltClient, indication *openolt.Indication) {
142 switch indication.Data.(type) {
143 case *openolt.Indication_OltInd:
144 log.Info("Received Indication_OltInd")
145 case *openolt.Indication_IntfInd:
146 log.Info("Received Indication_IntfInd")
147 case *openolt.Indication_IntfOperInd:
148 log.Info("Received Indication_IntfOperInd")
149 case *openolt.Indication_OnuDiscInd:
150 onuDiscInd := indication.GetOnuDiscInd()
151 o.handleOnuDiscIndication(client, onuDiscInd)
152 case *openolt.Indication_OnuInd:
153 onuInd := indication.GetOnuInd()
154 o.handleOnuIndication(client, onuInd)
155 case *openolt.Indication_OmciInd:
156 omciIndication := indication.GetOmciInd()
157 o.handleOmciIndication(client, omciIndication)
158 case *openolt.Indication_PktInd:
159 pktIndication := indication.GetPktInd()
160 o.handlePktIndication(client, pktIndication)
161 case *openolt.Indication_PortStats:
162 case *openolt.Indication_FlowStats:
163 case *openolt.Indication_AlarmInd:
164 default:
165 log.WithFields(log.Fields{
166 "data": indication.Data,
167 "type": reflect.TypeOf(indication.Data),
168 }).Warn("Indication unsupported")
169 }
170}
171
172func (o *OltMock) handleOnuDiscIndication(client openolt.OpenoltClient, onuDiscInd *openolt.OnuDiscIndication) {
173 log.WithFields(log.Fields{
174 "IntfId": onuDiscInd.IntfId,
175 "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
176 }).Info("Received Onu discovery indication")
177
178 onu, err := o.Olt.FindOnuBySn(common.OnuSnToString(onuDiscInd.SerialNumber))
179
180 if err != nil {
181 log.WithFields(log.Fields{
182 "IntfId": onuDiscInd.IntfId,
183 "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800184 "Err": err,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700185 }).Fatal("Cannot find ONU")
186 }
187
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800188 // creating and storing ONU IDs
189 id := o.LastUsedOnuId[onuDiscInd.IntfId] + 1
190 o.LastUsedOnuId[onuDiscInd.IntfId] = o.LastUsedOnuId[onuDiscInd.IntfId] + 1
191 onu.SetID(id)
192
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700193 var pir uint32 = 1000000
194 Onu := openolt.Onu{
195 IntfId: onu.PonPortID,
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800196 OnuId: id,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700197 SerialNumber: onu.SerialNumber,
198 Pir: pir,
199 }
200
201 if _, err := client.ActivateOnu(context.Background(), &Onu); err != nil {
202 log.WithFields(log.Fields{
203 "IntfId": onuDiscInd.IntfId,
204 "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
205 }).Error("Failed to activate ONU")
206 }
207}
208
209func (o *OltMock) handleOnuIndication(client openolt.OpenoltClient, onuInd *openolt.OnuIndication) {
210 log.WithFields(log.Fields{
211 "IntfId": onuInd.IntfId,
212 "SerialNumber": common.OnuSnToString(onuInd.SerialNumber),
213 }).Info("Received Onu indication")
214
215 onu, err := o.Olt.FindOnuBySn(common.OnuSnToString(onuInd.SerialNumber))
216
217 if err != nil {
218 log.WithFields(log.Fields{
219 "IntfId": onuInd.IntfId,
220 "SerialNumber": common.OnuSnToString(onuInd.SerialNumber),
221 }).Fatal("Cannot find ONU")
222 }
223
David Bainbridge103cf022019-12-16 20:11:35 +0000224 ctx, cancel := context.WithCancel(context.TODO())
225 go onu.ProcessOnuMessages(ctx, nil, client)
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700226
227 go func() {
228
229 defer func() {
230 log.WithFields(log.Fields{
231 "onuSn": common.OnuSnToString(onuInd.SerialNumber),
232 "CompletedOnus": o.CompletedOnus,
233 "TargetOnus": o.TargetOnus,
234 }).Debugf("Onu done")
235
Matteo Scandolo569e7172019-12-20 11:51:51 -0800236 // close the ONU channel
237 cancel()
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700238 }()
239
240 for message := range onu.DoneChannel {
241 if message == true {
242 o.CompletedOnus++
243 if o.CompletedOnus == o.TargetOnus {
244 // NOTE once all the ONUs are completed, exit
245 // closing the connection is not the most elegant way,
246 // but I haven't found any other way to stop
247 // the indications.Recv() infinite loop
248 log.Info("Simulation Done")
249 ValidateAndClose(o)
250 }
251
252 break
253 }
254 }
255
256 }()
257
258 // TODO change the state instead of calling an ONU method from here
259 onu.StartOmci(client)
260}
261
262func (o *OltMock) handleOmciIndication(client openolt.OpenoltClient, omciInd *openolt.OmciIndication) {
263
264 pon, err := o.Olt.GetPonById(omciInd.IntfId)
265 if err != nil {
266 log.WithFields(log.Fields{
267 "OnuId": omciInd.OnuId,
268 "IntfId": omciInd.IntfId,
269 "err": err,
270 }).Fatal("Can't find PonPort")
271 }
272 onu, _ := pon.GetOnuById(omciInd.OnuId)
273 if err != nil {
274 log.WithFields(log.Fields{
275 "OnuId": omciInd.OnuId,
276 "IntfId": omciInd.IntfId,
277 "err": err,
278 }).Fatal("Can't find Onu")
279 }
280
281 log.WithFields(log.Fields{
282 "IntfId": onu.PonPortID,
283 "OnuId": onu.ID,
284 "OnuSn": onu.Sn(),
285 "Pkt": omciInd.Pkt,
286 }).Trace("Received Onu omci indication")
287
288 msg := devices.Message{
289 Type: devices.OmciIndication,
290 Data: devices.OmciIndicationMessage{
291 OnuSN: onu.SerialNumber,
292 OnuID: onu.ID,
293 OmciInd: omciInd,
294 },
295 }
296 onu.Channel <- msg
297}
298
299func (o *OltMock) handlePktIndication(client openolt.OpenoltClient, pktIndication *openolt.PacketIndication) {
300
301 pkt := gopacket.NewPacket(pktIndication.Pkt, layers.LayerTypeEthernet, gopacket.Default)
302
303 pktType, err := packetHandlers.IsEapolOrDhcp(pkt)
304
305 if err != nil {
306 log.Warnf("Ignoring packet as it's neither EAPOL or DHCP")
307 return
308 }
309
310 log.WithFields(log.Fields{
311 "IntfType": pktIndication.IntfType,
312 "IntfId": pktIndication.IntfId,
313 "GemportId": pktIndication.GemportId,
314 "FlowId": pktIndication.FlowId,
315 "PortNo": pktIndication.PortNo,
316 "Cookie": pktIndication.Cookie,
317 "pktType": pktType,
318 }).Trace("Received PktIndication")
319
320 msg := devices.Message{}
321 if pktIndication.IntfType == "nni" {
322 // This is an packet that is arriving from the NNI and needs to be sent to an ONU
323 // in this case we need to fin the ONU from the C/S tags
324 // TODO: handle errors in the untagging process
325 sTag, _ := packetHandlers.GetVlanTag(pkt)
326 singleTagPkt, _ := packetHandlers.PopSingleTag(pkt)
327 cTag, _ := packetHandlers.GetVlanTag(singleTagPkt)
328
329 onu, err := o.getOnuByTags(int(sTag), int(cTag))
330
331 if err != nil {
332 log.WithFields(log.Fields{
333 "sTag": sTag,
334 "cTag": cTag,
335 }).Fatalf("Can't find ONU from c/s tags")
336 }
337
338 msg = devices.Message{
339 Type: devices.OnuPacketIn,
340 Data: devices.OnuPacketMessage{
341 IntfId: pktIndication.IntfId,
342 OnuId: onu.ID,
343 Packet: pkt,
344 Type: pktType,
345 },
346 }
347 // NOTE we send it on the ONU channel so that is handled as all the others packets in a separate thread
348 onu.Channel <- msg
349 } else {
350 // TODO a very similar construct is used in many places,
351 // abstract this in an OLT method
352 pon, err := o.Olt.GetPonById(pktIndication.IntfId)
353 if err != nil {
354 log.WithFields(log.Fields{
355 "OnuId": pktIndication.PortNo,
356 "IntfId": pktIndication.IntfId,
357 "err": err,
358 }).Fatal("Can't find PonPort")
359 }
360 onu, err := pon.GetOnuById(pktIndication.PortNo)
361 if err != nil {
362 log.WithFields(log.Fields{
363 "OnuId": pktIndication.PortNo,
364 "IntfId": pktIndication.IntfId,
365 "err": err,
366 }).Fatal("Can't find Onu")
367 }
368 // NOTE when we push the EAPOL flow we set the PortNo = OnuId for convenience sake
369 // BBsim responds setting the port number that was sent with the flow
370 msg = devices.Message{
371 Type: devices.OnuPacketIn,
372 Data: devices.OnuPacketMessage{
373 IntfId: pktIndication.IntfId,
374 OnuId: pktIndication.PortNo,
375 Packet: pkt,
376 Type: pktType,
377 },
378 }
379 onu.Channel <- msg
380 }
381}
382
383// TODO Move in a different file
384func Connect(ip string, port string) (openolt.OpenoltClient, *grpc.ClientConn) {
385 server := fmt.Sprintf("%s:%s", ip, port)
386 conn, err := grpc.Dial(server, grpc.WithInsecure())
387
388 if err != nil {
389 log.Fatalf("did not connect: %v", err)
390 return nil, conn
391 }
392 return openolt.NewOpenoltClient(conn), conn
393}