blob: 34ed68a6712cb1da123d9b85fb27abe6812608a9 [file] [log] [blame]
Zack Williams41513bf2018-07-07 20:08:35 -07001/*
2 * Copyright 2017-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 */
Stephane Barbarie35595062018-02-08 08:34:39 -050016package core
17
18import (
19 "context"
Jeff70e8b2d2018-07-24 13:37:29 -070020 "strconv"
21 "strings"
22 "time"
23
Stephane Barbarie35595062018-02-08 08:34:39 -050024 "github.com/golang/protobuf/ptypes/empty"
25 "github.com/google/gopacket"
26 "github.com/opencord/voltha/ponsim/v2/common"
27 "github.com/opencord/voltha/protos/go/ponsim"
28 "github.com/sirupsen/logrus"
29 "google.golang.org/grpc"
30 "google.golang.org/grpc/connectivity"
Stephane Barbarie35595062018-02-08 08:34:39 -050031)
32
33// TODO: Pass-in the certificate information as a structure parameter
34
35/*
36PonSimOltDevice is the structure responsible for the handling of an OLT device
37*/
38type PonSimOltDevice struct {
39 PonSimDevice `json:pon_device`
40 VCoreEndpoint string `json:vcore_ep`
41 MaxOnuCount int `json:max_onu`
42 Onus map[int32]*OnuRegistree `json:onu_registrees`
43 outgoing chan []byte
44
45 counterLoop *common.IntervalHandler
46 alarmLoop *common.IntervalHandler
47}
48
49/*
50
51 */
52type OnuRegistree struct {
53 Device *PonSimOnuDevice `json:onu_device`
54 Conn *grpc.ClientConn `json:grpc_conn`
55 Client ponsim.PonSimCommonClient `json:client`
56 Stream ponsim.PonSimCommon_ProcessDataClient `json:stream`
57}
58
59const (
60 BASE_PORT_NUMBER = 128
61)
62
63/*
64NewPonSimOltDevice instantiates a new OLT device structure
65*/
66func NewPonSimOltDevice(device PonSimDevice) *PonSimOltDevice {
67 olt := &PonSimOltDevice{PonSimDevice: device}
68 return olt
69}
70
71/*
72forwardToONU defines a EGRESS function to forward a packet to a specific ONU
73*/
74func (o *PonSimOltDevice) forwardToONU(onuPort int32) func(int, gopacket.Packet) {
75 return func(port int, frame gopacket.Packet) {
76 ipAddress := common.GetInterfaceIP(o.ExternalIf)
77 incoming := &ponsim.IncomingData{
78 Id: "EGRESS.OLT." + ipAddress,
79 Address: ipAddress,
80 Port: int32(port),
81 Payload: frame.Data(),
82 }
83 common.Logger().WithFields(logrus.Fields{
84 "device": o,
85 "port": port,
86 "frame": frame,
87 }).Debug("Forwarding to ONU")
88
89 // Forward packet to ONU
90 if err := o.GetOnu(onuPort).Stream.Send(incoming); err != nil {
91 common.Logger().WithFields(logrus.Fields{
92 "device": o,
93 "frameDump": frame.Dump(),
94 "incoming": incoming,
95 "error": err.Error(),
96 }).Error("A problem occurred while forwarding to ONU")
97 }
98
99 }
100}
101
102/*
103forwardToLAN defines an INGRESS function to forward a packet to VOLTHA
104*/
105func (o *PonSimOltDevice) forwardToLAN() func(int, gopacket.Packet) {
106 return func(port int, frame gopacket.Packet) {
107 common.Logger().WithFields(logrus.Fields{
108 "frame": frame.Dump(),
109 }).Info("Sending packet")
110
111 select {
112 case o.outgoing <- frame.Data():
113 common.Logger().WithFields(logrus.Fields{
114 "frame": frame.Dump(),
115 }).Info("Sent packet")
116 default:
117 common.Logger().WithFields(logrus.Fields{
118 "frame": frame.Dump(),
119 }).Warn("Unable to send packet")
120 }
121 }
122}
123
124/*
125Start performs setup operations for an OLT device
126*/
127func (o *PonSimOltDevice) Start(ctx context.Context) {
128 common.Logger().Info("Starting OLT device...")
129 o.PonSimDevice.Start(ctx)
130
131 // Open network interfaces for listening
132 o.connectNetworkInterfaces()
133
134 o.outgoing = make(chan []byte, 1)
135
136 // Add INGRESS operation
137 o.AddLink(2, 0, o.forwardToLAN())
138
139 // Start PM counter logging
140 o.counterLoop = common.NewIntervalHandler(90, o.Counter.LogCounts)
141 o.counterLoop.Start()
142
143 // Start alarm simulation
144 if o.AlarmsOn {
145 common.Logger().WithFields(logrus.Fields{
146 "device": o,
147 }).Debug("Starting alarm simulation")
148
149 alarms := NewPonSimAlarm(o.InternalIf, o.VCoreEndpoint, o.forwardToLAN())
150 o.alarmLoop = common.NewIntervalHandler(o.AlarmsFreq, alarms.GenerateAlarm)
151 o.alarmLoop.Start()
152 }
153}
154
155/*
156Stop performs cleanup operations for an OLT device
157*/
158func (o *PonSimOltDevice) Stop(ctx context.Context) {
159 common.Logger().Info("Stopping OLT device...")
160
161 // Stop PM counters loop
162 o.counterLoop.Stop()
163 o.counterLoop = nil
164
165 // Stop alarm simulation
166 if o.AlarmsOn {
167 o.alarmLoop.Stop()
168 }
169 o.alarmLoop = nil
170
171 o.ingressHandler.Close()
172 o.egressHandler.Close()
173
174 o.PonSimDevice.Stop(ctx)
175}
176
177/*
178ConnectToRemoteOnu establishes communication to a remote ONU device
179*/
180func (o *PonSimOltDevice) ConnectToRemoteOnu(onu *OnuRegistree) error {
181 var err error
182
183 host := strings.Join([]string{
184 onu.Device.Address,
185 strconv.Itoa(int(onu.Device.Port)),
186 }, ":")
187
188 common.Logger().WithFields(logrus.Fields{
189 "device": o,
190 "host": host,
191 }).Debug("Formatting host address")
192
Stephane Barbarie35595062018-02-08 08:34:39 -0500193 // GRPC communication needs to be secured
194 if onu.Conn, err = grpc.DialContext(
195 context.Background(),
196 host,
Jeff70e8b2d2018-07-24 13:37:29 -0700197 grpc.WithInsecure(),
Stephane Barbarie35595062018-02-08 08:34:39 -0500198 ); err != nil {
199 common.Logger().WithFields(logrus.Fields{
200 "device": o,
201 "error": err.Error(),
202 }).Error("Problem with client connection")
203 }
204
205 return err
206}
207
208/*
209Listen waits for incoming EGRESS data on the internal interface
210*/
211func (o *PonSimOltDevice) Listen(ctx context.Context, port int32) {
212 var reply *empty.Empty
213 var err error
214
215 // Establish a GRPC connection with the ONU
216 onu := o.GetOnu(port)
217
218 common.Logger().WithFields(logrus.Fields{
219 "onu": onu,
220 }).Debug("Connecting to remote ONU")
221
222 if onu.Client = ponsim.NewPonSimCommonClient(onu.Conn); onu.Client == nil {
223 common.Logger().WithFields(logrus.Fields{
224 "device": o,
225 }).Error("Problem establishing client connection to ONU")
226 o.RemoveOnu(ctx, port)
227 return
228 }
229
230 // Prepare stream to ONU to forward incoming data as needed
231 if onu.Stream, err = onu.Client.ProcessData(ctx); err != nil {
232 common.Logger().WithFields(logrus.Fields{
233 "device": o,
234 }).Error("Problem establishing stream to ONU")
235 o.RemoveOnu(ctx, port)
236 return
237 }
238
239 defer o.egressHandler.Close()
240 packetSource := gopacket.NewPacketSource(o.egressHandler, o.egressHandler.LinkType())
241 common.Logger().WithFields(logrus.Fields{
242 "device": o,
243 "interface": o.InternalIf,
244 }).Debug("Listening to incoming EGRESS data")
245
246 // Wait for incoming EGRESS data
247 for packet := range packetSource.Packets() {
248 if dot1q := common.GetDot1QLayer(packet); dot1q != nil {
249 common.Logger().WithFields(logrus.Fields{
250 "device": o,
251 "packet": packet,
252 }).Debug("Received EGRESS packet")
253
254 o.Forward(ctx, 2, packet)
255 }
256 }
257
258 common.Logger().WithFields(logrus.Fields{
259 "device": o,
260 }).Debug("No more packets to process")
261
262 if reply, err = onu.Stream.CloseAndRecv(); err != nil {
263 common.Logger().WithFields(logrus.Fields{
264 "device": o,
265 "error": err.Error(),
266 }).Error("A problem occurred while closing client stream")
267 } else {
268 common.Logger().WithFields(logrus.Fields{
269 "device": o,
270 "reply": reply,
271 }).Warn("Client stream closed")
272 }
273}
274
275/*
276GetOnus returns the list of registered ONU devices
277*/
278func (o *PonSimOltDevice) GetOnus() map[int32]*OnuRegistree {
279 if o.Onus == nil {
280 o.Onus = make(map[int32]*OnuRegistree)
281 }
282
283 return o.Onus
284}
285
286/*
287GetOnu return a specific registered ONU
288*/
289func (o *PonSimOltDevice) GetOnu(index int32) *OnuRegistree {
290 var onu *OnuRegistree
291 var ok bool
292
293 if onu, ok = (o.GetOnus())[index]; ok {
294 return onu
295 }
296
297 return nil
298}
299
300func (o *PonSimOltDevice) GetOutgoing() chan []byte {
301 return o.outgoing
302}
303
304/*
305nextAvailablePort returns a port that is not already used by a registered ONU
306*/
307func (o *PonSimOltDevice) nextAvailablePort() int32 {
308 var port int32 = BASE_PORT_NUMBER
309
310 if len(o.GetOnus()) < o.MaxOnuCount {
311 for {
312 if o.GetOnu(port) != nil {
313 // port is already used
314 port += 1
315 } else {
316 // port is available... use it
317 return port
318 }
319 }
320 } else {
321 // OLT has reached its max number of ONUs
322 return -1
323 }
324}
325
326/*
327AddOnu registers an ONU device and sets up all required monitoring and connections
328*/
329func (o *PonSimOltDevice) AddOnu(onu *PonSimOnuDevice) (int32, error) {
330 var portNum int32
331 ctx := context.Background()
332
333 if portNum = o.nextAvailablePort(); portNum != -1 {
334 common.Logger().WithFields(logrus.Fields{
335 "device": o,
336 "port": portNum,
337 "onu": onu,
338 }).Info("Adding ONU")
339
340 registree := &OnuRegistree{Device: onu}
341
342 // Setup GRPC communication and check if it succeeded
343 if err := o.ConnectToRemoteOnu(registree); err == nil {
344 o.GetOnus()[portNum] = registree
345
346 o.AddLink(1, int(portNum), o.forwardToONU(portNum))
347 go o.MonitorOnu(ctx, portNum)
348 go o.Listen(ctx, portNum)
349 }
350
351 } else {
352 common.Logger().WithFields(logrus.Fields{
353 "device": o,
354 }).Warn("ONU Map is full")
355 }
356
357 return int32(portNum), nil
358}
359
360/*
361RemoveOnu removes the reference to a registered ONU
362*/
363func (o *PonSimOltDevice) RemoveOnu(ctx context.Context, onuIndex int32) error {
364 onu := o.GetOnu(onuIndex)
365 if err := onu.Conn.Close(); err != nil {
366 common.Logger().WithFields(logrus.Fields{
367 "device": o,
368 "onu": onu.Device,
369 "onuIndex": onuIndex,
370 }).Error("Problem closing connection to ONU")
371 }
372
373 common.Logger().WithFields(logrus.Fields{
374 "device": o,
375 "onu": onu,
376 "onuIndex": onuIndex,
377 }).Info("Removing ONU")
378
379 delete(o.Onus, onuIndex)
380
381 // Remove link entries for this ONU
382 o.RemoveLink(1, int(onuIndex))
383
384 return nil
385}
386
387/*
388MonitorOnu verifies the connection status of a specific ONU and cleans up as necessary
389*/
390func (o *PonSimOltDevice) MonitorOnu(ctx context.Context, onuIndex int32) {
391 for {
392 if o.GetOnu(onuIndex) != nil {
393 if conn := o.GetOnu(onuIndex).Conn; conn.GetState() == connectivity.Ready {
394 // Wait for any change to occur
395 conn.WaitForStateChange(ctx, conn.GetState())
396 // We lost communication with the ONU ... remove it
397 o.RemoveOnu(ctx, onuIndex)
398 return
399 }
400 common.Logger().WithFields(logrus.Fields{
401 "device": o,
402 "ctx": ctx,
403 "onuIndex": onuIndex,
404 }).Debug("ONU is not ready")
405 time.Sleep(1 * time.Second)
406 } else {
407 return
408 }
409 }
410}