blob: 28d99d5555a08a222b32793ed25bea524ca6bfe9 [file] [log] [blame]
Shad Ansari2825d012018-02-22 23:57:46 +00001#
2# Copyright 2018 the original author or authors.
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
17import structlog
18import threading
19import grpc
20import collections
nick47b74372018-05-25 18:22:49 -040021import time
Shad Ansari2825d012018-02-22 23:57:46 +000022
Shad Ansari15928d12018-04-17 02:42:13 +000023from twisted.internet import reactor
Shad Ansari0346f0d2018-04-26 06:54:09 +000024from scapy.layers.l2 import Ether, Dot1Q
25import binascii
Shad Ansari22efe832018-05-19 05:37:03 +000026from transitions import Machine
Shad Ansari15928d12018-04-17 02:42:13 +000027
Shad Ansari2825d012018-02-22 23:57:46 +000028from voltha.protos.device_pb2 import Port, Device
29from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
30from voltha.protos.logical_device_pb2 import LogicalDevice
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -040031from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN, \
Shad Ansari2825d012018-02-22 23:57:46 +000032 OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
Shad Ansari2dda4f32018-05-17 07:16:07 +000033 OFPC_FLOW_STATS, ofp_switch_features, ofp_port
Shad Ansari2825d012018-02-22 23:57:46 +000034from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
35from voltha.core.logical_device_agent import mac_str_to_tuple
36from voltha.registry import registry
37from voltha.adapters.openolt.protos import openolt_pb2_grpc, openolt_pb2
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -040038from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
39from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
40from voltha.protos.bbf_fiber_base_pb2 import VEnetConfig
Shad Ansari2825d012018-02-22 23:57:46 +000041import voltha.core.flow_decomposer as fd
42
Shad Ansari22920932018-05-17 00:33:34 +000043import openolt_platform as platform
Shad Ansari2dda4f32018-05-17 07:16:07 +000044from openolt_flow_mgr import OpenOltFlowMgr
Shad Ansari801f7372018-04-27 02:15:41 +000045
nick47b74372018-05-25 18:22:49 -040046MAX_HEARTBEAT_MISS = 3
47HEARTBEAT_PERIOD = 1
48GRPC_TIMEOUT = 5
Shad Ansari2825d012018-02-22 23:57:46 +000049
50"""
51OpenoltDevice represents an OLT.
52"""
53class OpenoltDevice(object):
54
Shad Ansari22efe832018-05-19 05:37:03 +000055 states = ['up', 'down']
56 transitions = [
57 { 'trigger': 'olt_up', 'source': 'down', 'dest': 'up', 'before': 'olt_indication_up' },
58 { 'trigger': 'olt_down', 'source': 'up', 'dest': 'down', 'before': 'olt_indication_down' }
59 ]
60
Shad Ansari2825d012018-02-22 23:57:46 +000061 def __init__(self, **kwargs):
62 super(OpenoltDevice, self).__init__()
63
64 self.adapter_agent = kwargs['adapter_agent']
Shad Ansari5dbc9c82018-05-10 03:29:31 +000065 self.device_num = kwargs['device_num']
Shad Ansari2825d012018-02-22 23:57:46 +000066 device = kwargs['device']
67 self.device_id = device.id
68 self.host_and_port = device.host_and_port
69 self.log = structlog.get_logger(id=self.device_id, ip=self.host_and_port)
Shad Ansari2825d012018-02-22 23:57:46 +000070 self.nni_oper_state = dict() #intf_id -> oper_state
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -040071 self.proxy = registry('core').get_proxy('/')
Shad Ansari2825d012018-02-22 23:57:46 +000072
Shad Ansari2825d012018-02-22 23:57:46 +000073 # Update device
74 device.root = True
Shad Ansari2825d012018-02-22 23:57:46 +000075 device.serial_number = self.host_and_port # FIXME
Shad Ansari2825d012018-02-22 23:57:46 +000076 device.connect_status = ConnectStatus.REACHABLE
77 device.oper_status = OperStatus.ACTIVATING
78 self.adapter_agent.update_device(device)
79
Shad Ansari22efe832018-05-19 05:37:03 +000080 # Initialize the OLT state machine
81 self.machine = Machine(model=self, states=OpenoltDevice.states,
82 transitions=OpenoltDevice.transitions,
83 send_event=True, initial='down', ignore_invalid_triggers=True)
84 self.machine.add_transition(trigger='olt_ind_up', source='down', dest='up')
85 self.machine.add_transition(trigger='olt_ind_loss', source='up', dest='down')
86
Shad Ansari2825d012018-02-22 23:57:46 +000087 # Initialize gRPC
88 self.channel = grpc.insecure_channel(self.host_and_port)
Shad Ansari8f1b2532018-04-21 07:51:39 +000089 self.channel_ready_future = grpc.channel_ready_future(self.channel)
nick47b74372018-05-25 18:22:49 -040090 self.stub = openolt_pb2_grpc.OpenoltStub(self.channel)
91
92 self.flow_mgr = OpenOltFlowMgr(self.log, self.stub)
Shad Ansari2825d012018-02-22 23:57:46 +000093
nick369a5062018-05-29 17:11:06 -040094 # Indications thread plcaholder (started by heartbeat thread)
95 self.indications_thread = None
96 self.indications_thread_active = False
Shad Ansari2825d012018-02-22 23:57:46 +000097
nick47b74372018-05-25 18:22:49 -040098 # Start heartbeat thread
99 self.heartbeat_thread = threading.Thread(target=self.heartbeat)
100 self.heartbeat_thread.setDaemon(True)
101 self.heartbeat_thread_active = True
102 self.heartbeat_miss = 0
103 self.heartbeat_signature = None
104 self.heartbeat_thread.start()
105
Shad Ansari8f1b2532018-04-21 07:51:39 +0000106 def process_indications(self):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000107
nick47b74372018-05-25 18:22:49 -0400108 self.log.debug('starting-indications-thread')
Shad Ansari2dda4f32018-05-17 07:16:07 +0000109
Shad Ansari15928d12018-04-17 02:42:13 +0000110 self.indications = self.stub.EnableIndication(openolt_pb2.Empty())
Shad Ansari2dda4f32018-05-17 07:16:07 +0000111
nick47b74372018-05-25 18:22:49 -0400112 while self.indications_thread_active:
113 try:
114 # get the next indication from olt
115 ind = next(self.indications)
116 except Exception as e:
117 self.log.warn('GRPC-connection-lost-stoping-indications-thread', error=e)
118 self.indications_thread_active = False
119 else:
120 self.log.debug("rx indication", indication=ind)
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000121
nick47b74372018-05-25 18:22:49 -0400122 # indication handlers run in the main event loop
123 if ind.HasField('olt_ind'):
124 reactor.callFromThread(self.olt_indication, ind.olt_ind)
125 elif ind.HasField('intf_ind'):
126 reactor.callFromThread(self.intf_indication, ind.intf_ind)
127 elif ind.HasField('intf_oper_ind'):
128 reactor.callFromThread(self.intf_oper_indication, ind.intf_oper_ind)
129 elif ind.HasField('onu_disc_ind'):
130 reactor.callFromThread(self.onu_discovery_indication, ind.onu_disc_ind)
131 elif ind.HasField('onu_ind'):
132 reactor.callFromThread(self.onu_indication, ind.onu_ind)
133 elif ind.HasField('omci_ind'):
134 reactor.callFromThread(self.omci_indication, ind.omci_ind)
135 elif ind.HasField('pkt_ind'):
136 reactor.callFromThread(self.packet_indication, ind.pkt_ind)
137
138 self.log.debug('stopping-indications-thread', device_id=self.device_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000139
140 def olt_indication(self, olt_indication):
Shad Ansari22efe832018-05-19 05:37:03 +0000141 if olt_indication.oper_state == "up":
142 self.olt_up(ind=olt_indication)
143 elif olt_indication.oper_state == "down":
144 self.olt_down(ind=olt_indication)
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000145
Shad Ansari22efe832018-05-19 05:37:03 +0000146 def olt_indication_up(self, event):
147 olt_indication = event.kwargs.get('ind', None)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400148 self.log.debug("olt indication", olt_ind=olt_indication)
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000149
nick47b74372018-05-25 18:22:49 -0400150 device = self.adapter_agent.get_device(self.device_id)
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000151
nick47b74372018-05-25 18:22:49 -0400152 # If logical device does not exist create it
153 if len(device.parent_id) == 0:
154
155 dpid = '00:00:' + self.ip_hex(self.host_and_port.split(":")[0])
156
157 # Create logical OF device
158 ld = LogicalDevice(
159 root_device_id=self.device_id,
160 switch_features=ofp_switch_features(
161 n_buffers=256, # TODO fake for now
162 n_tables=2, # TODO ditto
163 capabilities=( # TODO and ditto
164 OFPC_FLOW_STATS
165 | OFPC_TABLE_STATS
166 | OFPC_PORT_STATS
167 | OFPC_GROUP_STATS
168 )
Jonathan Hart7687e0a2018-05-16 10:54:47 -0700169 )
170 )
nick47b74372018-05-25 18:22:49 -0400171 ld_initialized = self.adapter_agent.create_logical_device(ld, dpid=dpid)
172 self.logical_device_id = ld_initialized.id
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000173
174 # Update phys OF device
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000175 device.parent_id = self.logical_device_id
176 device.oper_status = OperStatus.ACTIVE
177 self.adapter_agent.update_device(device)
178
Shad Ansari22efe832018-05-19 05:37:03 +0000179 def olt_indication_down(self, event):
180 olt_indication = event.kwargs.get('ind', None)
nick47b74372018-05-25 18:22:49 -0400181 new_admin_state = event.kwargs.get('admin_state', None)
182 new_oper_state = event.kwargs.get('oper_state', None)
183 new_connect_state = event.kwargs.get('connect_state', None)
184 self.log.debug("olt indication", olt_ind=olt_indication, admin_state=new_admin_state, oper_state=new_oper_state,
185 connect_state=new_connect_state)
186
nick369a5062018-05-29 17:11:06 -0400187 # Propagating to the children
188
189 # Children ports
190 child_devices = self.adapter_agent.get_child_devices(self.device_id)
191 for onu_device in child_devices:
192 uni_no = platform.mk_uni_port_num(onu_device.proxy_address.channel_id, onu_device.proxy_address.onu_id)
193 uni_name = self.port_name(uni_no, Port.ETHERNET_UNI, serial_number=onu_device.serial_number)
194
195 self.onu_ports_down(onu_device, uni_no, uni_name, new_oper_state)
196 # Children devices
197 self.adapter_agent.update_child_devices_state(self.device_id, oper_status=new_oper_state,
198 connect_status=ConnectStatus.UNREACHABLE,
199 admin_state=new_admin_state)
200 # Device Ports
201 device_ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
202 logical_ports_ids = [port.label for port in device_ports]
203 device_ports += self.adapter_agent.get_ports(self.device_id, Port.PON_OLT)
204
205 for port in device_ports:
206 if new_admin_state is not None:
207 port.admin_state = new_admin_state
208 if new_oper_state is not None:
209 port.oper_status = new_oper_state
210 self.adapter_agent.add_port(self.device_id, port)
211
212 # Device logical port
213 for logical_port_id in logical_ports_ids:
214 logical_port = self.adapter_agent.get_logical_port(self.logical_device_id, logical_port_id)
215 logical_port.ofp_port.state = OFPPS_LINK_DOWN
216 self.adapter_agent.update_logical_port(self.logical_device_id, logical_port)
217
218 # Device
nick47b74372018-05-25 18:22:49 -0400219 device = self.adapter_agent.get_device(self.device_id)
nick369a5062018-05-29 17:11:06 -0400220 if new_admin_state is not None:
nick47b74372018-05-25 18:22:49 -0400221 device.admin_state = new_admin_state
222 if new_oper_state is not None:
223 device.oper_status = new_oper_state
224 if new_connect_state is not None:
225 device.connect_status = new_connect_state
226
227 self.adapter_agent.update_device(device)
Shad Ansari2825d012018-02-22 23:57:46 +0000228
229 def intf_indication(self, intf_indication):
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400230 self.log.debug("intf indication", intf_id=intf_indication.intf_id,
Shad Ansari2825d012018-02-22 23:57:46 +0000231 oper_state=intf_indication.oper_state)
232
233 if intf_indication.oper_state == "up":
234 oper_status = OperStatus.ACTIVE
235 else:
236 oper_status = OperStatus.DISCOVERED
237
nick47b74372018-05-25 18:22:49 -0400238 # add_port update the port if it exists
Shad Ansari2825d012018-02-22 23:57:46 +0000239 self.add_port(intf_indication.intf_id, Port.PON_OLT, oper_status)
240
241 def intf_oper_indication(self, intf_oper_indication):
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400242 self.log.debug("Received interface oper state change indication", intf_id=intf_oper_indication.intf_id,
243 type=intf_oper_indication.type, oper_state=intf_oper_indication.oper_state)
Shad Ansari2825d012018-02-22 23:57:46 +0000244
245 if intf_oper_indication.oper_state == "up":
246 oper_state = OperStatus.ACTIVE
247 else:
248 oper_state = OperStatus.DISCOVERED
249
250 if intf_oper_indication.type == "nni":
251
Shad Ansari0346f0d2018-04-26 06:54:09 +0000252 # FIXME - creating logical port for 2nd interface throws exception!
Shad Ansari2825d012018-02-22 23:57:46 +0000253 if intf_oper_indication.intf_id != 0:
254 return
255
256 if intf_oper_indication.intf_id not in self.nni_oper_state:
257 self.nni_oper_state[intf_oper_indication.intf_id] = oper_state
258 port_no, label = self.add_port(intf_oper_indication.intf_id, Port.ETHERNET_NNI, oper_state)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400259 self.log.debug("int_oper_indication", port_no=port_no, label=label)
Shad Ansari4a232ca2018-05-05 05:24:17 +0000260 self.add_logical_port(port_no, intf_oper_indication.intf_id) # FIXME - add oper_state
Shad Ansari2825d012018-02-22 23:57:46 +0000261 elif intf_oper_indication.intf_id != self.nni_oper_state:
262 # FIXME - handle subsequent NNI oper state change
263 pass
264
265 elif intf_oper_indication.type == "pon":
266 # FIXME - handle PON oper state change
267 pass
268
269 def onu_discovery_indication(self, onu_disc_indication):
Shad Ansari803900a2018-05-02 06:26:00 +0000270 intf_id = onu_disc_indication.intf_id
271 serial_number=onu_disc_indication.serial_number
Shad Ansari2825d012018-02-22 23:57:46 +0000272
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400273 serial_number_str = self.stringify_serial_number(serial_number)
Shad Ansari803900a2018-05-02 06:26:00 +0000274
Nicolas Palpacuer36a93442018-05-23 17:38:57 -0400275 self.log.debug("onu discovery indication", intf_id=intf_id, serial_number=serial_number_str)
276
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400277 onu_device = self.adapter_agent.get_child_device(self.device_id, serial_number=serial_number_str)
278
279 if onu_device is None:
Shad Ansari803900a2018-05-02 06:26:00 +0000280 onu_id = self.new_onu_id(intf_id)
Shad Ansari15928d12018-04-17 02:42:13 +0000281 try:
Shad Ansari803900a2018-05-02 06:26:00 +0000282 self.add_onu_device(intf_id,
Shad Ansari22920932018-05-17 00:33:34 +0000283 platform.intf_id_to_port_no(intf_id, Port.PON_OLT),
Shad Ansari803900a2018-05-02 06:26:00 +0000284 onu_id, serial_number)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400285 self.log.info("activate-onu", intf_id=intf_id, onu_id=onu_id,
Nicolas Palpacuer36a93442018-05-23 17:38:57 -0400286 serial_number=serial_number_str)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400287 onu = openolt_pb2.Onu(intf_id=intf_id, onu_id=onu_id,serial_number=serial_number)
288 self.stub.ActivateOnu(onu)
289 except Exception as e:
290 self.log.exception('onu-activation-failed', e=e)
291
Shad Ansari2825d012018-02-22 23:57:46 +0000292 else:
nick47b74372018-05-25 18:22:49 -0400293 if onu_device.connect_status != ConnectStatus.REACHABLE:
294 onu_device.connect_status = ConnectStatus.REACHABLE
295 self.adapter_agent.update_device(onu_device)
296
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400297 onu_id = onu_device.proxy_address.onu_id
Nicolas Palpacuer36a93442018-05-23 17:38:57 -0400298 if onu_device.oper_status == OperStatus.DISCOVERED or onu_device.oper_status == OperStatus.ACTIVATING:
299 self.log.debug("ignore onu discovery indication, the onu has been discovered and should be \
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400300 activating shorlty", intf_id=intf_id, onu_id=onu_id, state=onu_device.oper_status)
301 elif onu_device.oper_status== OperStatus.ACTIVE:
302 self.log.warn("onu discovery indication whereas onu is supposed to be active",
303 intf_id=intf_id, onu_id=onu_id, state=onu_device.oper_status)
nick47b74372018-05-25 18:22:49 -0400304 elif onu_device.oper_status == OperStatus.UNKNOWN:
305 self.log.info("onu-in-unknow-state-recovering-form-olt-reboot-activate-onu", intf_id=intf_id, onu_id=onu_id,
306 serial_number=serial_number_str)
307
308 onu_device.oper_status = OperStatus.DISCOVERED
309 self.adapter_agent.update_device(onu_device)
310
311 onu = openolt_pb2.Onu(intf_id=intf_id, onu_id=onu_id, serial_number=serial_number)
312 self.stub.ActivateOnu(onu)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400313 else:
314 self.log.warn('unexpected state', onu_id=onu_id, onu_device_oper_state=onu_device.oper_status)
Shad Ansari2825d012018-02-22 23:57:46 +0000315
Shad Ansari2825d012018-02-22 23:57:46 +0000316 def onu_indication(self, onu_indication):
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400317 self.log.debug("onu-indication", intf_id=onu_indication.intf_id,
318 onu_id=onu_indication.onu_id, serial_number=onu_indication.serial_number,
319 oper_state=onu_indication.oper_state, admin_state=onu_indication.admin_state)
Shad Ansari2825d012018-02-22 23:57:46 +0000320
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400321 serial_number_str = self.stringify_serial_number(onu_indication.serial_number)
322
323 if serial_number_str == '000000000000':
324 self.log.debug('serial-number-was-not-provided-or-default-serial-number-provided-identifying-onu-by-onu_id')
325 #FIXME: if multiple PON ports onu_id is not a sufficient key
326 onu_device = self.adapter_agent.get_child_device(
327 self.device_id,
328 onu_id=onu_indication.onu_id)
329 else :
330 onu_device = self.adapter_agent.get_child_device(
331 self.device_id,
332 serial_number=serial_number_str)
333
334 self.log.debug('onu-device', olt_device_id=self.device_id, device=onu_device)
Shad Ansari803900a2018-05-02 06:26:00 +0000335
336 # FIXME - handle serial_number mismatch
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400337 # assert key is not None
338 # assert onu_device is not None
339 if onu_device is None:
340 self.log.warn('onu-device-is-none-invalid-message')
Shad Ansari803900a2018-05-02 06:26:00 +0000341 return
342
nick47b74372018-05-25 18:22:49 -0400343 if onu_device.connect_status != ConnectStatus.REACHABLE:
344 onu_device.connect_status = ConnectStatus.REACHABLE
345 self.adapter_agent.update_device(onu_device)
346
Nicolas Palpacuer36a93442018-05-23 17:38:57 -0400347 if platform.intf_id_from_pon_port_no(onu_device.parent_port_no) != onu_indication.intf_id:
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400348 self.log.warn('ONU-is-on-a-different-intf-id-now',
Nicolas Palpacuer36a93442018-05-23 17:38:57 -0400349 previous_intf_id=platform.intf_id_from_pon_port_no(onu_device.parent_port_no),
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400350 current_intf_id=onu_indication.intf_id)
351 # FIXME - handle intf_id mismatch (ONU move?)
Shad Ansari2825d012018-02-22 23:57:46 +0000352
Shad Ansari2825d012018-02-22 23:57:46 +0000353
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400354 if onu_device.proxy_address.onu_id != onu_indication.onu_id:
355 # FIXME - handle onu id mismatch
356 self.log.warn('ONU-id-mismatch', expected_onu_id=onu_device.proxy_address.onu_id,
357 received_onu_id=onu_indication.onu_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000358
Shad Ansari22920932018-05-17 00:33:34 +0000359 uni_no = platform.mk_uni_port_num(onu_indication.intf_id, onu_indication.onu_id)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400360 uni_name = self.port_name(uni_no, Port.ETHERNET_UNI, serial_number=serial_number_str)
Shad Ansari2825d012018-02-22 23:57:46 +0000361
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400362 self.log.debug('port-number-ready', uni_no=uni_no, uni_name=uni_name)
Shad Ansari2825d012018-02-22 23:57:46 +0000363
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400364 #Admin state
365 if onu_indication.admin_state == 'down':
366 if onu_indication.oper_state != 'down':
367 self.log.error('ONU-admin-state-down-and-oper-status-not-down', oper_state=onu_indication.oper_state)
368 onu_indication.oper_state = 'down' # Forcing the oper state change code to execute
369
370 if onu_device.admin_state != AdminState.DISABLED:
371 onu_device.admin_state = AdminState.DISABLED
372 self.adapter_agent.update(onu_device)
373 self.log.debug('putting-onu-in-disabled-state', onu_serial_number=onu_device.serial_number)
374
375 #Port and logical port update is taken care of by oper state block
376
377 elif onu_indication.admin_state == 'up':
378 if onu_device.admin_state != AdminState.ENABLED:
379 onu_device.admin_state = AdminState.ENABLED
380 self.adapter_agent.update(onu_device)
381 self.log.debug('putting-onu-in-enabled-state', onu_serial_number=onu_device.serial_number)
382
383 else:
384 self.log.warn('Invalid-or-not-implemented-admin-state', received_admin_state=onu_indication.admin_state)
385
386 self.log.debug('admin-state-dealt-with')
387
388 #Operating state
389 if onu_indication.oper_state == 'down':
390 #Move to discovered state
391 self.log.debug('onu-oper-state-is-down')
392
393 if onu_device.oper_status != OperStatus.DISCOVERED:
394 onu_device.oper_status = OperStatus.DISCOVERED
395 self.adapter_agent.update_device(onu_device)
396 #Set port oper state to Discovered
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400397
nick47b74372018-05-25 18:22:49 -0400398 self.onu_ports_down(onu_device, uni_no, uni_name, OperStatus.DISCOVERED)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400399
400 elif onu_indication.oper_state == 'up':
401
402 if onu_device.oper_status != OperStatus.DISCOVERED:
403 self.log.debug("ignore onu indication", intf_id=onu_indication.intf_id,
nick47b74372018-05-25 18:22:49 -0400404 onu_id=onu_indication.onu_id, state=onu_device.oper_status,
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400405 msg_oper_state=onu_indication.oper_state)
406 return
407
408 #Device was in Discovered state, setting it to active
409
410
411
412 onu_adapter_agent = registry('adapter_loader').get_agent(onu_device.adapter)
413 if onu_adapter_agent is None:
414 self.log.error('onu_adapter_agent-could-not-be-retrieved', onu_device=onu_device)
415 return
416
417 #Prepare onu configuration
418
419 # onu initialization, base configuration (bridge setup ...)
420 def onu_initialization():
421
422 #FIXME: that's definitely cheating
423 if onu_device.adapter == 'broadcom_onu':
424 onu_adapter_agent.adapter.devices_handlers[onu_device.id].message_exchange()
425 self.log.debug('broadcom-message-exchange-started')
426
427 # tcont creation (onu)
428 tcont = TcontsConfigData()
429 tcont.alloc_id = platform.mk_alloc_id(onu_indication.onu_id)
430
431 # gem port creation
432 gem_port = GemportsConfigData()
433 gem_port.gemport_id = platform.mk_gemport_id(onu_indication.onu_id)
434
435 #ports creation/update
436 def port_config():
437
438 # "v_enet" creation (olt)
439
440 #add_port update port when it exists
441 self.adapter_agent.add_port(
442 self.device_id,
443 Port(
444 port_no=uni_no,
445 label=uni_name,
446 type=Port.ETHERNET_UNI,
447 admin_state=AdminState.ENABLED,
448 oper_status=OperStatus.ACTIVE))
449
450 # v_enet creation (onu)
451
452 venet = VEnetConfig(name=uni_name)
453 venet.interface.name = uni_name
454 onu_adapter_agent.create_interface(onu_device, venet)
455
456 # ONU device status update in the datastore
457 def onu_update_oper_status():
458 onu_device.oper_status = OperStatus.ACTIVE
459 onu_device.connect_status = ConnectStatus.REACHABLE
460 self.adapter_agent.update_device(onu_device)
461
462 # FIXME : the asynchronicity has to be taken care of properly
463 onu_initialization()
464 reactor.callLater(10, onu_adapter_agent.create_tcont, device=onu_device,
465 tcont_data=tcont, traffic_descriptor_data=None)
466 reactor.callLater(11, onu_adapter_agent.create_gemport, onu_device, gem_port)
467 reactor.callLater(12, port_config)
468 reactor.callLater(12, onu_update_oper_status)
469
470 else:
471 self.log.warn('Not-implemented-or-invalid-value-of-oper-state', oper_state=onu_indication.oper_state)
Shad Ansari2825d012018-02-22 23:57:46 +0000472
nick47b74372018-05-25 18:22:49 -0400473 def onu_ports_down(self, onu_device, uni_no, uni_name, oper_state):
474 # Set port oper state to Discovered
475 # add port will update port if it exists
476 self.adapter_agent.add_port(
477 self.device_id,
478 Port(
479 port_no=uni_no,
480 label=uni_name,
481 type=Port.ETHERNET_UNI,
482 admin_state=onu_device.admin_state,
483 oper_status=oper_state))
484
485 # Disable logical port
nick47b74372018-05-25 18:22:49 -0400486 onu_ports = self.proxy.get('devices/{}/ports'.format(onu_device.id))
487 onu_port_id = None
488 for onu_port in onu_ports:
489 if onu_port.port_no == uni_no:
490 onu_port_id = onu_port.label
491 if onu_port_id is None:
492 self.log.error('matching-onu-port-label-not-found', onu_id=onu_device.id, olt_id=self.device_id,
493 onu_ports=onu_ports)
494 return
495 try:
nick369a5062018-05-29 17:11:06 -0400496 onu_logical_port = self.adapter_agent.get_logical_port(logical_device_id=self.logical_device_id,
nick47b74372018-05-25 18:22:49 -0400497 port_id=onu_port_id)
498 onu_logical_port.ofp_port.state = OFPPS_LINK_DOWN
nick369a5062018-05-29 17:11:06 -0400499 self.adapter_agent.update_logical_port(logical_device_id=self.logical_device_id, port=onu_logical_port)
nick47b74372018-05-25 18:22:49 -0400500 self.log.debug('cascading-oper-state-to-port-and-logical-port')
501 except KeyError as e:
502 self.log.error('matching-onu-port-label-invalid', onu_id=onu_device.id, olt_id=self.device_id,
503 onu_ports=onu_ports, onu_port_id=onu_port_id, error=e)
504
Shad Ansari2825d012018-02-22 23:57:46 +0000505 def omci_indication(self, omci_indication):
506
507 self.log.debug("omci indication", intf_id=omci_indication.intf_id,
508 onu_id=omci_indication.onu_id)
509
Shad Ansari0efa6512018-04-28 06:42:54 +0000510 onu_device = self.adapter_agent.get_child_device(self.device_id,
511 onu_id=omci_indication.onu_id)
512
513 self.adapter_agent.receive_proxied_message(onu_device.proxy_address,
514 omci_indication.pkt)
Shad Ansari2825d012018-02-22 23:57:46 +0000515
Shad Ansari42db7342018-04-25 21:39:46 +0000516 def packet_indication(self, pkt_indication):
517
518 self.log.debug("packet indication", intf_id=pkt_indication.intf_id,
519 gemport_id=pkt_indication.gemport_id,
520 flow_id=pkt_indication.flow_id)
521
Shad Ansari22920932018-05-17 00:33:34 +0000522 onu_id = platform.onu_id_from_gemport_id(pkt_indication.gemport_id)
523 logical_port_num = platform.mk_uni_port_num(pkt_indication.intf_id, onu_id)
Shad Ansari42db7342018-04-25 21:39:46 +0000524
525 pkt = Ether(pkt_indication.pkt)
Shad Ansari0efa6512018-04-28 06:42:54 +0000526 kw = dict(logical_device_id=self.logical_device_id,
527 logical_port_no=logical_port_num)
Shad Ansari42db7342018-04-25 21:39:46 +0000528 self.adapter_agent.send_packet_in(packet=str(pkt), **kw)
529
nick47b74372018-05-25 18:22:49 -0400530 def olt_reachable(self):
531 device = self.adapter_agent.get_device(self.device_id)
532 device.connect_status = ConnectStatus.REACHABLE
533 self.adapter_agent.update_device(device)
534 # Not changing its child devices state, we cannot guaranty that
535
536 def heartbeat(self):
537
nick369a5062018-05-29 17:11:06 -0400538 self.channel_ready_future.result() # blocks till gRPC connection is complete
539
nick47b74372018-05-25 18:22:49 -0400540 while self.heartbeat_thread_active:
541
542 try:
543 heartbeat = self.stub.HeartbeatCheck(openolt_pb2.Empty(), timeout=GRPC_TIMEOUT)
544 except Exception as e:
545 self.heartbeat_miss += 1
546 self.log.warn('heartbeat-miss', missed_heartbeat=self.heartbeat_miss, error=e)
547 if self.heartbeat_miss == MAX_HEARTBEAT_MISS:
548 self.log.error('lost-connectivity-to-olt')
549 #TODO : send alarm/notify monitoring system
550 # Using reactor to synchronize update
551 # flagging it as unreachable and in unknow state
nick369a5062018-05-29 17:11:06 -0400552 reactor.callFromThread(self.olt_down, oper_state=OperStatus.UNKNOWN,
nick47b74372018-05-25 18:22:49 -0400553 connect_state=ConnectStatus.UNREACHABLE)
554
555 else:
556 # heartbeat received
557 if self.heartbeat_signature is None:
558 # Initialize heartbeat signature
559 self.heartbeat_signature = heartbeat.heartbeat_signature
560 self.log.debug('heartbeat-signature', device_id=self.device_id,
561 heartbeat_signature=self.heartbeat_signature)
562 # Check if signature is different
563 if self.heartbeat_signature != heartbeat.heartbeat_signature:
564 # OLT has rebooted
565 self.log.warn('OLT-was-rebooted', device_id=self.device_id)
566 #TODO: notify monitoring system
567 self.heartbeat_signature = heartbeat.heartbeat_signature
568
569 else:
570 self.log.debug('valid-heartbeat-received')
571
572 if self.heartbeat_miss > MAX_HEARTBEAT_MISS:
573 self.log.info('OLT-connection-restored')
574 #TODO : suppress alarm/notify monitoring system
575 # flagging it as reachable again
nick369a5062018-05-29 17:11:06 -0400576 reactor.callFromThread(self.olt_reachable)
nick47b74372018-05-25 18:22:49 -0400577
578 if not self.indications_thread_active:
nick369a5062018-05-29 17:11:06 -0400579 self.log.info('(re)starting-indications-thread')
nick47b74372018-05-25 18:22:49 -0400580 # reset indications thread
581 self.indications_thread = threading.Thread(target=self.process_indications)
582 self.indications_thread.setDaemon(True)
583 self.indications_thread_active = True
584 self.indications_thread.start()
585
586 self.heartbeat_miss = 0
587
588 time.sleep(HEARTBEAT_PERIOD)
589
590 self.log.debug('stopping-heartbeat-thread', device_id=self.device_id)
591
592
593
Shad Ansari42db7342018-04-25 21:39:46 +0000594 def packet_out(self, egress_port, msg):
Shad Ansari0346f0d2018-04-26 06:54:09 +0000595 pkt = Ether(msg)
596 self.log.info('packet out', egress_port=egress_port,
597 packet=str(pkt).encode("HEX"))
598
599 if pkt.haslayer(Dot1Q):
600 outer_shim = pkt.getlayer(Dot1Q)
601 if isinstance(outer_shim.payload, Dot1Q):
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400602 #If double tag, remove the outer tag
Shad Ansari0346f0d2018-04-26 06:54:09 +0000603 payload = (
604 Ether(src=pkt.src, dst=pkt.dst, type=outer_shim.type) /
605 outer_shim.payload
606 )
607 else:
608 payload = pkt
609 else:
610 payload = pkt
611
612 self.log.info('sending-packet-to-device', egress_port=egress_port,
613 packet=str(payload).encode("HEX"))
614
615 send_pkt = binascii.unhexlify(str(payload).encode("HEX"))
616
Nicolas Palpacuer36a93442018-05-23 17:38:57 -0400617 onu_pkt = openolt_pb2.OnuPacket(intf_id=platform.intf_id_from_pon_port_no(egress_port),
Shad Ansari22920932018-05-17 00:33:34 +0000618 onu_id=platform.onu_id_from_port_num(egress_port), pkt=send_pkt)
Shad Ansari0346f0d2018-04-26 06:54:09 +0000619
Jonathan Harta58dc382018-05-14 16:29:19 -0700620 self.stub.OnuPacketOut(onu_pkt)
Shad Ansari42db7342018-04-25 21:39:46 +0000621
Shad Ansari2825d012018-02-22 23:57:46 +0000622 def send_proxied_message(self, proxy_address, msg):
Shad Ansari0efa6512018-04-28 06:42:54 +0000623 omci = openolt_pb2.OmciMsg(intf_id=proxy_address.channel_id, # intf_id
624 onu_id=proxy_address.onu_id, pkt=str(msg))
Shad Ansari2825d012018-02-22 23:57:46 +0000625 self.stub.OmciMsgOut(omci)
626
627 def add_onu_device(self, intf_id, port_no, onu_id, serial_number):
Shad Ansari2825d012018-02-22 23:57:46 +0000628 self.log.info("Adding ONU", port_no=port_no, onu_id=onu_id,
Shad Ansari0efa6512018-04-28 06:42:54 +0000629 serial_number=serial_number)
Shad Ansari2825d012018-02-22 23:57:46 +0000630
631 # NOTE - channel_id of onu is set to intf_id
Shad Ansari0efa6512018-04-28 06:42:54 +0000632 proxy_address = Device.ProxyAddress(device_id=self.device_id,
633 channel_id=intf_id, onu_id=onu_id, onu_session_id=onu_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000634
635 self.log.info("Adding ONU", proxy_address=proxy_address)
636
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400637 serial_number_str = self.stringify_serial_number(serial_number)
Shad Ansari2825d012018-02-22 23:57:46 +0000638
Shad Ansari0efa6512018-04-28 06:42:54 +0000639 self.adapter_agent.add_onu_device(parent_device_id=self.device_id,
640 parent_port_no=port_no, vendor_id=serial_number.vendor_id,
641 proxy_address=proxy_address, root=True,
642 serial_number=serial_number_str, admin_state=AdminState.ENABLED)
Shad Ansari2825d012018-02-22 23:57:46 +0000643
Shad Ansari1fd9eb22018-05-15 05:13:49 +0000644 def port_name(self, port_no, port_type, intf_id=None, serial_number=None):
Shad Ansari2825d012018-02-22 23:57:46 +0000645 if port_type is Port.ETHERNET_NNI:
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400646 return "nni-" + str(port_no)
Shad Ansari2825d012018-02-22 23:57:46 +0000647 elif port_type is Port.PON_OLT:
Shad Ansari4a232ca2018-05-05 05:24:17 +0000648 return "pon" + str(intf_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000649 elif port_type is Port.ETHERNET_UNI:
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400650 if serial_number is not None:
651 return serial_number
652 else:
653 return "uni-{}".format(port_no)
Shad Ansari2825d012018-02-22 23:57:46 +0000654
Shad Ansari4a232ca2018-05-05 05:24:17 +0000655 def add_logical_port(self, port_no, intf_id):
Shad Ansari2825d012018-02-22 23:57:46 +0000656 self.log.info('adding-logical-port', port_no=port_no)
657
658 label = self.port_name(port_no, Port.ETHERNET_NNI)
659
660 cap = OFPPF_1GB_FD | OFPPF_FIBER
661 curr_speed = OFPPF_1GB_FD
662 max_speed = OFPPF_1GB_FD
663
Shad Ansari0efa6512018-04-28 06:42:54 +0000664 ofp = ofp_port(port_no=port_no,
665 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
666 name=label, config=0, state=OFPPS_LIVE, curr=cap,
667 advertised=cap, peer=cap, curr_speed=curr_speed,
668 max_speed=max_speed)
Shad Ansari2825d012018-02-22 23:57:46 +0000669
Shad Ansari0efa6512018-04-28 06:42:54 +0000670 logical_port = LogicalPort(id=label, ofp_port=ofp,
671 device_id=self.device_id, device_port_no=port_no,
672 root_port=True)
Shad Ansari2825d012018-02-22 23:57:46 +0000673
674 self.adapter_agent.add_logical_port(self.logical_device_id, logical_port)
675
676 def add_port(self, intf_id, port_type, oper_status):
Shad Ansari22920932018-05-17 00:33:34 +0000677 port_no = platform.intf_id_to_port_no(intf_id, port_type)
Shad Ansari2825d012018-02-22 23:57:46 +0000678
Shad Ansari4a232ca2018-05-05 05:24:17 +0000679 label = self.port_name(port_no, port_type, intf_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000680
Shad Ansari0efa6512018-04-28 06:42:54 +0000681 self.log.info('adding-port', port_no=port_no, label=label,
682 port_type=port_type)
683
684 port = Port(port_no=port_no, label=label, type=port_type,
685 admin_state=AdminState.ENABLED, oper_status=oper_status)
686
Shad Ansari2825d012018-02-22 23:57:46 +0000687 self.adapter_agent.add_port(self.device_id, port)
Shad Ansari0efa6512018-04-28 06:42:54 +0000688
Shad Ansari2825d012018-02-22 23:57:46 +0000689 return port_no, label
690
Shad Ansari2825d012018-02-22 23:57:46 +0000691 def new_onu_id(self, intf_id):
692 onu_id = None
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400693 onu_devices = self.adapter_agent.get_child_devices(self.device_id)
Shad Ansari5dbc9c82018-05-10 03:29:31 +0000694 for i in range(1, 512):
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400695 id_not_taken = True
696 for child_device in onu_devices:
697 if child_device.proxy_address.onu_id == i:
698 id_not_taken = False
699 break
700 if id_not_taken:
Shad Ansari2825d012018-02-22 23:57:46 +0000701 onu_id = i
702 break
703 return onu_id
704
705 def stringify_vendor_specific(self, vendor_specific):
706 return ''.join(str(i) for i in [
Shad Ansari1fd9eb22018-05-15 05:13:49 +0000707 hex(ord(vendor_specific[0])>>4 & 0x0f)[2:],
708 hex(ord(vendor_specific[0]) & 0x0f)[2:],
709 hex(ord(vendor_specific[1])>>4 & 0x0f)[2:],
710 hex(ord(vendor_specific[1]) & 0x0f)[2:],
711 hex(ord(vendor_specific[2])>>4 & 0x0f)[2:],
712 hex(ord(vendor_specific[2]) & 0x0f)[2:],
713 hex(ord(vendor_specific[3])>>4 & 0x0f)[2:],
714 hex(ord(vendor_specific[3]) & 0x0f)[2:]])
Shad Ansari2825d012018-02-22 23:57:46 +0000715
Shad Ansari2825d012018-02-22 23:57:46 +0000716
717 def update_flow_table(self, flows):
718 device = self.adapter_agent.get_device(self.device_id)
Shad Ansaricd2e8ff2018-05-11 20:26:22 +0000719 self.log.debug('update flow table')
Shad Ansari2dda4f32018-05-17 07:16:07 +0000720 in_port = None
Shad Ansari2825d012018-02-22 23:57:46 +0000721
722 for flow in flows:
Shad Ansari2825d012018-02-22 23:57:46 +0000723 is_down_stream = None
Shad Ansari2dda4f32018-05-17 07:16:07 +0000724 in_port = fd.get_in_port(flow)
725 assert in_port is not None
726 # Right now there is only one NNI port. Get the NNI PORT and compare
727 # with IN_PUT port number. Need to find better way.
728 ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_NNI)
Shad Ansari2825d012018-02-22 23:57:46 +0000729
Shad Ansari2dda4f32018-05-17 07:16:07 +0000730 for port in ports:
731 if (port.port_no == in_port):
732 self.log.info('downstream-flow')
733 is_down_stream = True
734 break
735 if is_down_stream is None:
736 is_down_stream = False
737 self.log.info('upstream-flow')
Shad Ansari2825d012018-02-22 23:57:46 +0000738
Shad Ansari2dda4f32018-05-17 07:16:07 +0000739 for flow in flows:
740 try:
741 self.flow_mgr.add_flow(flow, is_down_stream)
742 except Exception as e:
743 self.log.exception('failed-to-install-flow', e=e, flow=flow)
Shad Ansari89b09d52018-05-21 07:28:14 +0000744
745 # There has to be a better way to do this
746 def ip_hex(self, ip):
747 octets = ip.split(".")
748 hex_ip = []
749 for octet in octets:
750 octet_hex = hex(int(octet))
751 octet_hex = octet_hex.split('0x')[1]
752 octet_hex = octet_hex.rjust(2, '0')
753 hex_ip.append(octet_hex)
754 return ":".join(hex_ip)
Nicolas Palpacuer65de6a42018-05-22 17:28:29 -0400755
756 def stringify_serial_number(self, serial_number):
757 return ''.join([serial_number.vendor_id,
Shad Ansari5c596a52018-05-23 19:49:57 +0000758 self.stringify_vendor_specific(serial_number.vendor_specific)])