blob: 804bc0e1fd34465d3361179a1fb1d807c4b2bd28 [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
21import time
22
Shad Ansari15928d12018-04-17 02:42:13 +000023from twisted.internet import reactor
24
Shad Ansari2825d012018-02-22 23:57:46 +000025from voltha.protos.device_pb2 import Port, Device
26from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
27from voltha.protos.logical_device_pb2 import LogicalDevice
28from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN, \
29 OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
30 OFPC_FLOW_STATS, ofp_switch_features, ofp_desc, ofp_port, \
31 OFPXMC_OPENFLOW_BASIC
32from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
33from voltha.core.logical_device_agent import mac_str_to_tuple
34from voltha.registry import registry
35from voltha.adapters.openolt.protos import openolt_pb2_grpc, openolt_pb2
36from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
37import voltha.core.flow_decomposer as fd
38
39ASFVOLT_HSIA_ID = 13 # FIXME
40
41Onu = collections.namedtuple("Onu", ["intf_id", "onu_id"])
42
43"""
44OpenoltDevice represents an OLT.
45"""
46class OpenoltDevice(object):
47
48 def __init__(self, **kwargs):
49 super(OpenoltDevice, self).__init__()
50
51 self.adapter_agent = kwargs['adapter_agent']
52 device = kwargs['device']
53 self.device_id = device.id
54 self.host_and_port = device.host_and_port
55 self.log = structlog.get_logger(id=self.device_id, ip=self.host_and_port)
56 self.oper_state = 'unknown'
57 self.nni_oper_state = dict() #intf_id -> oper_state
58 self.onus = {} # Onu -> serial_number
59 self.uni_port_num = 20 # FIXME
60
61 # Create logical device
62 ld = LogicalDevice(
63 desc=ofp_desc(
64 mfr_desc='FIXME', hw_desc='FIXME',
65 sw_desc='FIXME', serial_num='FIXME',
66 dp_desc='n/a'),
67 switch_features=ofp_switch_features(
68 n_buffers=256, n_tables=2,
69 capabilities=(
70 OFPC_FLOW_STATS | OFPC_TABLE_STATS |
71 OFPC_GROUP_STATS | OFPC_PORT_STATS)),
72 root_device_id=self.device_id)
73 # FIXME
74 ld_initialized = self.adapter_agent.create_logical_device(ld, dpid='de:ad:be:ef:fe:ed') # FIXME
75 self.logical_device_id = ld_initialized.id
76
77 # Update device
78 device.root = True
79 device.vendor = 'Edgecore'
80 device.model = 'ASFvOLT16'
81 device.serial_number = self.host_and_port # FIXME
82 device.parent_id = self.logical_device_id
83 device.connect_status = ConnectStatus.REACHABLE
84 device.oper_status = OperStatus.ACTIVATING
85 self.adapter_agent.update_device(device)
86
87 # Initialize gRPC
88 self.channel = grpc.insecure_channel(self.host_and_port)
89 self.stub = openolt_pb2_grpc.OpenoltStub(self.channel)
90
91 # Start indications thread
Shad Ansari2825d012018-02-22 23:57:46 +000092 self.indications_thread = threading.Thread(target=self.process_indication)
93 self.indications_thread.daemon = True
94 self.indications_thread.start()
95
96 def process_indication(self):
Shad Ansari15928d12018-04-17 02:42:13 +000097 self.indications = self.stub.EnableIndication(openolt_pb2.Empty())
Shad Ansari2825d012018-02-22 23:57:46 +000098 while 1:
Shad Ansari15928d12018-04-17 02:42:13 +000099 # get the next indication from olt
Shad Ansari2825d012018-02-22 23:57:46 +0000100 ind = next(self.indications)
101 self.log.debug("rx indication", indication=ind)
Shad Ansari15928d12018-04-17 02:42:13 +0000102 # schedule indication handlers to be run in the main event loop
Shad Ansari2825d012018-02-22 23:57:46 +0000103 if ind.HasField('olt_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000104 reactor.callFromThread(self.olt_indication, ind.olt_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000105 elif ind.HasField('intf_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000106 reactor.callFromThread(self.intf_indication, ind.intf_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000107 elif ind.HasField('intf_oper_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000108 reactor.callFromThread(self.intf_oper_indication, ind.intf_oper_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000109 elif ind.HasField('onu_disc_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000110 reactor.callFromThread(self.onu_discovery_indication, ind.onu_disc_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000111 elif ind.HasField('onu_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000112 reactor.callFromThread(self.onu_indication, ind.onu_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000113 elif ind.HasField('omci_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000114 reactor.callFromThread(self.omci_indication, ind.omci_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000115
116 def olt_indication(self, olt_indication):
117 self.log.debug("olt indication", olt_ind=olt_indication)
118 self.set_oper_state(olt_indication.oper_state)
119
120 def intf_indication(self, intf_indication):
121 self.log.debug("intf indication", intf_id=intf_indication.intf_id,
122 oper_state=intf_indication.oper_state)
123
124 if intf_indication.oper_state == "up":
125 oper_status = OperStatus.ACTIVE
126 else:
127 oper_status = OperStatus.DISCOVERED
128
129 # FIXME - If port exists, update oper state
130 self.add_port(intf_indication.intf_id, Port.PON_OLT, oper_status)
131
132 def intf_oper_indication(self, intf_oper_indication):
133 self.log.debug("Received interface oper state change indication", intf_id=intf_oper_indication.intf_id,
134 type=intf_oper_indication.type, oper_state=intf_oper_indication.oper_state)
135
136 if intf_oper_indication.oper_state == "up":
137 oper_state = OperStatus.ACTIVE
138 else:
139 oper_state = OperStatus.DISCOVERED
140
141 if intf_oper_indication.type == "nni":
142
143 # FIXME - Ignore all nni ports except nni port 0
144 if intf_oper_indication.intf_id != 0:
145 return
146
147 if intf_oper_indication.intf_id not in self.nni_oper_state:
148 self.nni_oper_state[intf_oper_indication.intf_id] = oper_state
149 port_no, label = self.add_port(intf_oper_indication.intf_id, Port.ETHERNET_NNI, oper_state)
150 self.log.debug("int_oper_indication", port_no=port_no, label=label)
151 self.add_logical_port(port_no) # FIXME - add oper_state
152 elif intf_oper_indication.intf_id != self.nni_oper_state:
153 # FIXME - handle subsequent NNI oper state change
154 pass
155
156 elif intf_oper_indication.type == "pon":
157 # FIXME - handle PON oper state change
158 pass
159
160 def onu_discovery_indication(self, onu_disc_indication):
161 self.log.debug("onu discovery indication", intf_id=onu_disc_indication.intf_id,
162 serial_number=onu_disc_indication.serial_number)
163
164 onu_id = self.lookup_onu(serial_number=onu_disc_indication.serial_number)
165
166 if onu_id is None:
167 onu_id = self.new_onu_id(onu_disc_indication.intf_id)
Shad Ansari15928d12018-04-17 02:42:13 +0000168 try:
169 self.add_onu_device(
170 onu_disc_indication.intf_id,
171 self.intf_id_to_port_no(onu_disc_indication.intf_id, Port.PON_OLT),
172 onu_id,
173 onu_disc_indication.serial_number)
174 except Exception as e:
175 self.log.exception('onu activation failed', e=e)
176 else:
177 self.activate_onu(
178 onu_disc_indication.intf_id, onu_id,
179 serial_number=onu_disc_indication.serial_number)
Shad Ansari2825d012018-02-22 23:57:46 +0000180 else:
181 # FIXME - handle discovery of already activated onu
182 self.log.info("onu activation in progress",
183 intf_id=onu_disc_indication.intf_id, onu_id=onu_id)
184
185 def _get_next_uni_port(self):
186 self.uni_port_num += 1
187 return self.uni_port_num
188
189 def onu_indication(self, onu_indication):
190
191 self.log.debug("onu indication", intf_id=onu_indication.intf_id,
192 onu_id=onu_indication.onu_id)
193
194 # FIXME - handle onu_id/serial_number mismatch
195 assert onu_indication.onu_id == self.lookup_onu(serial_number=onu_indication.serial_number)
196
197 onu_device = self.adapter_agent.get_child_device(
198 self.device_id, onu_id=onu_indication.onu_id)
199 assert onu_device is not None
200
201 msg = {'proxy_address':onu_device.proxy_address,
202 'event':'activation-completed',
203 'event_data':{'activation_successful':True}}
204 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
205
206 #
207 # tcont create (onu)
208 #
209 alloc_id = self.mk_alloc_id(onu_indication.onu_id)
210 msg = {'proxy_address':onu_device.proxy_address,
211 'event':'create-tcont',
212 'event_data':{'alloc_id':alloc_id}}
213 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
214
215 #
216 # v_enet create (olt)
217 #
218 uni_no = self._get_next_uni_port()
219 uni_name = self.port_name(uni_no, Port.ETHERNET_UNI)
220 self.adapter_agent.add_port(
221 self.device_id,
222 Port(
223 port_no=uni_no,
224 label=uni_name,
225 type=Port.ETHERNET_UNI,
226 admin_state=AdminState.ENABLED,
227 oper_status=OperStatus.ACTIVE))
228
229 #
230 # v_enet create (onu)
231 #
232 interface_name = self.port_name(onu_indication.intf_id, Port.PON_OLT)
233 msg = {'proxy_address':onu_device.proxy_address,
234 'event':'create-venet',
235 'event_data':{'uni_name':uni_name, 'interface_name':uni_name}}
236 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
237
238 #
239 # gem port create
240 #
241 gemport_id = self.mk_gemport_id(onu_indication.onu_id)
242 msg = {'proxy_address':onu_device.proxy_address,
243 'event':'create-gemport',
244 'event_data':{'gemport_id':gemport_id}}
245 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
246
247 def mk_gemport_id(self, onu_id):
248 return 1023 + onu_id # FIXME
249
250 def mk_alloc_id(self, onu_id):
251 return 1023 + onu_id # FIXME
252
253 def omci_indication(self, omci_indication):
254
255 self.log.debug("omci indication", intf_id=omci_indication.intf_id,
256 onu_id=omci_indication.onu_id)
257
258 onu_device = self.adapter_agent.get_child_device(
259 self.device_id,
260 onu_id=omci_indication.onu_id)
261 self.adapter_agent.receive_proxied_message(
262 onu_device.proxy_address,
263 omci_indication.pkt)
264
265 def activate_onu(self, intf_id, onu_id, serial_number):
266
267 self.log.info("activate onu", intf_id=intf_id, onu_id=onu_id,
268 serial_number=serial_number)
269
270 self.onus[Onu(intf_id=intf_id, onu_id=onu_id)] = serial_number
271
272 onu = openolt_pb2.Onu(
273 intf_id=intf_id, onu_id=onu_id, serial_number=serial_number)
274
275 self.stub.ActivateOnu(onu)
276
277
278 def send_proxied_message(self, proxy_address, msg):
279 omci = openolt_pb2.OmciMsg(
280 intf_id=proxy_address.channel_id, # intf_id
281 onu_id=proxy_address.onu_id,
282 pkt=str(msg))
283 self.stub.OmciMsgOut(omci)
284
285 def add_onu_device(self, intf_id, port_no, onu_id, serial_number):
286
287 self.log.info("Adding ONU", port_no=port_no, onu_id=onu_id,
288 serial_number=serial_number)
289
290 # NOTE - channel_id of onu is set to intf_id
291 proxy_address = Device.ProxyAddress(
292 device_id=self.device_id,
293 channel_id=intf_id,
294 onu_id=onu_id,
295 onu_session_id=onu_id)
296
297 self.log.info("Adding ONU", proxy_address=proxy_address)
298
299 serial_number_str = ''.join([
300 serial_number.vendor_id,
301 self.stringify_vendor_specific(serial_number.vendor_specific)])
302
303 self.adapter_agent.add_onu_device(
304 parent_device_id=self.device_id, parent_port_no=port_no,
305 vendor_id=serial_number.vendor_id, proxy_address=proxy_address,
306 root=True, serial_number=serial_number_str,
307 admin_state=AdminState.ENABLED) # FIXME
308
309 def intf_id_to_port_no(self, intf_id, intf_type):
310 if intf_type is Port.ETHERNET_NNI:
311 # FIXME - Remove hardcoded '129'
312 return intf_id + 129
313 elif intf_type is Port.PON_OLT:
314 # Interface Ids (reported by device) are zero-based indexed
315 # OpenFlow port numbering is one-based.
316 return intf_id + 1
317 else:
318 raise Exception('Invalid port type')
319
320
321 def port_name(self, port_no, port_type):
322 if port_type is Port.ETHERNET_NNI:
323 prefix = "nni"
324 elif port_type is Port.PON_OLT:
325 prefix = "pon"
326 elif port_type is Port.ETHERNET_UNI:
327 prefix = "uni"
328 return prefix + "-" + str(port_no)
329
330 def update_device_status(self, connect_status=None, oper_status=None, reason=None):
331 device = self.adapter_agent.get_device(self.device_id)
332 if connect_status is not None:
333 device.connect_status = connect_status
334 if oper_status is not None:
335 device.oper_status = oper_status
336 if reason is not None:
337 device.reason = reason
338 self.adapter_agent.update_device(device)
339
340 def add_logical_port(self, port_no):
341 self.log.info('adding-logical-port', port_no=port_no)
342
343 label = self.port_name(port_no, Port.ETHERNET_NNI)
344
345 cap = OFPPF_1GB_FD | OFPPF_FIBER
346 curr_speed = OFPPF_1GB_FD
347 max_speed = OFPPF_1GB_FD
348
349 ofp = ofp_port(
350 port_no=port_no,
351 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
352 name=label,
353 config=0,
354 state=OFPPS_LIVE,
355 curr=cap,
356 advertised=cap,
357 peer=cap,
358 curr_speed=curr_speed,
359 max_speed=max_speed)
360
361 logical_port = LogicalPort(
362 id=label,
363 ofp_port=ofp,
364 device_id=self.device_id,
365 device_port_no=port_no,
366 root_port=True
367 )
368
369 self.adapter_agent.add_logical_port(self.logical_device_id, logical_port)
370
371 def add_port(self, intf_id, port_type, oper_status):
372 port_no = self.intf_id_to_port_no(intf_id, port_type)
373
374 label = self.port_name(port_no, port_type)
375
376 self.log.info('adding-port', port_no=port_no, label=label, port_type=port_type)
377 port = Port(
378 port_no=port_no,
379 label=label,
380 type=port_type,
381 admin_state=AdminState.ENABLED,
382 oper_status=oper_status
383 )
384 self.adapter_agent.add_port(self.device_id, port)
385 return port_no, label
386
387 def set_oper_state(self, new_state):
388 if self.oper_state != new_state:
389 if new_state == 'up':
390 self.update_device_status(
391 connect_status=ConnectStatus.REACHABLE,
392 oper_status=OperStatus.ACTIVE,
393 reason='OLT indication - operation state up')
394 elif new_state == 'down':
395 self.update_device_status(
396 connect_status=ConnectStatus.REACHABLE,
397 oper_status=OperStatus.FAILED,
398 reason='OLT indication - operation state down')
399 else:
400 raise ValueError('Invalid oper_state in olt_indication')
401
402 self.oper_state = new_state
403
404 def new_onu_id(self, intf_id):
405 onu_id = None
406 # onu_id is unique per PON.
407 # FIXME - Remove hardcoded limit on ONUs per PON (64)
408 for i in range(1, 64):
409 onu = Onu(intf_id=intf_id, onu_id=i)
410 if onu not in self.onus:
411 onu_id = i
412 break
413 return onu_id
414
415 def stringify_vendor_specific(self, vendor_specific):
416 return ''.join(str(i) for i in [
417 ord(vendor_specific[0])>>4 & 0x0f,
418 ord(vendor_specific[0]) & 0x0f,
419 ord(vendor_specific[1])>>4 & 0x0f,
420 ord(vendor_specific[1]) & 0x0f,
421 ord(vendor_specific[2])>>4 & 0x0f,
422 ord(vendor_specific[2]) & 0x0f,
423 ord(vendor_specific[3])>>4 & 0x0f,
424 ord(vendor_specific[3]) & 0x0f])
425
426 def lookup_onu(self, serial_number):
427 onu_id = None
428 for onu, s in self.onus.iteritems():
429 if s.vendor_id == serial_number.vendor_id:
430 str1 = self.stringify_vendor_specific(s.vendor_specific)
431 str2 = self.stringify_vendor_specific(serial_number.vendor_specific)
432 if str1 == str2:
433 onu_id = onu.onu_id
434 break
435 return onu_id
436
437 def update_flow_table(self, flows):
438 device = self.adapter_agent.get_device(self.device_id)
439 self.log.info('update flow table', flows=flows)
440
441 for flow in flows:
442 self.log.info('flow-details', device_id=self.device_id, flow=flow)
443 classifier_info = dict()
444 action_info = dict()
445 is_down_stream = None
446 _in_port = None
447 try:
448 _in_port = fd.get_in_port(flow)
449 assert _in_port is not None
450 # Right now there is only one NNI port. Get the NNI PORT and compare
451 # with IN_PUT port number. Need to find better way.
452 ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_NNI)
453
454 for port in ports:
455 if (port.port_no == _in_port):
456 self.log.info('downstream-flow')
457 is_down_stream = True
458 break
459 if is_down_stream is None:
460 is_down_stream = False
461 self.log.info('upstream-flow')
462
463 _out_port = fd.get_out_port(flow) # may be None
464 self.log.info('out-port', out_port=_out_port)
465
466 for field in fd.get_ofb_fields(flow):
467
468 if field.type == fd.ETH_TYPE:
469 classifier_info['eth_type'] = field.eth_type
470 self.log.info('field-type-eth-type',
471 eth_type=classifier_info['eth_type'])
472
473 elif field.type == fd.IP_PROTO:
474 classifier_info['ip_proto'] = field.ip_proto
475 self.log.info('field-type-ip-proto',
476 ip_proto=classifier_info['ip_proto'])
477
478 elif field.type == fd.IN_PORT:
479 classifier_info['in_port'] = field.port
480 self.log.info('field-type-in-port',
481 in_port=classifier_info['in_port'])
482
483 elif field.type == fd.VLAN_VID:
484 classifier_info['vlan_vid'] = field.vlan_vid & 0xfff
485 self.log.info('field-type-vlan-vid',
486 vlan=classifier_info['vlan_vid'])
487
488 elif field.type == fd.VLAN_PCP:
489 classifier_info['vlan_pcp'] = field.vlan_pcp
490 self.log.info('field-type-vlan-pcp',
491 pcp=classifier_info['vlan_pcp'])
492
493 elif field.type == fd.UDP_DST:
494 classifier_info['udp_dst'] = field.udp_dst
495 self.log.info('field-type-udp-dst',
496 udp_dst=classifier_info['udp_dst'])
497
498 elif field.type == fd.UDP_SRC:
499 classifier_info['udp_src'] = field.udp_src
500 self.log.info('field-type-udp-src',
501 udp_src=classifier_info['udp_src'])
502
503 elif field.type == fd.IPV4_DST:
504 classifier_info['ipv4_dst'] = field.ipv4_dst
505 self.log.info('field-type-ipv4-dst',
506 ipv4_dst=classifier_info['ipv4_dst'])
507
508 elif field.type == fd.IPV4_SRC:
509 classifier_info['ipv4_src'] = field.ipv4_src
510 self.log.info('field-type-ipv4-src',
511 ipv4_dst=classifier_info['ipv4_src'])
512
513 elif field.type == fd.METADATA:
514 classifier_info['metadata'] = field.table_metadata
515 self.log.info('field-type-metadata',
516 metadata=classifier_info['metadata'])
517
518 else:
519 raise NotImplementedError('field.type={}'.format(
520 field.type))
521
522 for action in fd.get_actions(flow):
523
524 if action.type == fd.OUTPUT:
525 action_info['output'] = action.output.port
526 self.log.info('action-type-output',
527 output=action_info['output'],
528 in_port=classifier_info['in_port'])
529
530 elif action.type == fd.POP_VLAN:
531 action_info['pop_vlan'] = True
532 self.log.info('action-type-pop-vlan',
533 in_port=_in_port)
534
535 elif action.type == fd.PUSH_VLAN:
536 action_info['push_vlan'] = True
537 action_info['tpid'] = action.push.ethertype
538 self.log.info('action-type-push-vlan',
539 push_tpid=action_info['tpid'],
540 in_port=_in_port)
541 if action.push.ethertype != 0x8100:
542 self.log.error('unhandled-tpid',
543 ethertype=action.push.ethertype)
544
545 elif action.type == fd.SET_FIELD:
546 # action_info['action_type'] = 'set_field'
547 _field = action.set_field.field.ofb_field
548 assert (action.set_field.field.oxm_class ==
549 OFPXMC_OPENFLOW_BASIC)
550 self.log.info('action-type-set-field',
551 field=_field, in_port=_in_port)
552 if _field.type == fd.VLAN_VID:
553 self.log.info('set-field-type-vlan-vid',
554 vlan_vid=_field.vlan_vid & 0xfff)
555 action_info['vlan_vid'] = (_field.vlan_vid & 0xfff)
556 else:
557 self.log.error('unsupported-action-set-field-type',
558 field_type=_field.type)
559 else:
560 self.log.error('unsupported-action-type',
561 action_type=action.type, in_port=_in_port)
562
563 if is_down_stream is False:
564 intf_id, onu_id = self.parse_port_no(classifier_info['in_port'])
565 self.divide_and_add_flow(onu_id, intf_id, classifier_info, action_info)
566 except Exception as e:
567 self.log.exception('failed-to-install-flow', e=e, flow=flow)
568
569 def parse_port_no(self, port_no):
570 return 0, 1 # FIXME
571
572 # This function will divide the upstream flow into both
573 # upstreand and downstream flow, as broadcom devices
574 # expects down stream flows to be added to handle
575 # packet_out messge from controller.
576 def divide_and_add_flow(self, onu_id, intf_id, classifier, action):
577 if 'ip_proto' in classifier:
578 if classifier['ip_proto'] == 17:
579 self.log.error('dhcp flow add ignored')
580 elif classifier['ip_proto'] == 2:
581 self.log.info('igmp flow add ignored')
582 else:
583 self.log.info("Invalid-Classifier-to-handle",
584 classifier=classifier,
585 action=action)
586 elif 'eth_type' in classifier:
587 if classifier['eth_type'] == 0x888e:
588 self.log.error('epol flow add ignored')
589 elif 'push_vlan' in action:
590 self.add_data_flow(onu_id, intf_id, classifier, action)
591 else:
592 self.log.info('Invalid-flow-type-to-handle',
593 classifier=classifier,
594 action=action)
595
596 def add_data_flow(self, onu_id, intf_id, uplink_classifier, uplink_action):
597
598 downlink_classifier = dict(uplink_classifier)
599 downlink_action = dict(uplink_action)
600
601 uplink_classifier['pkt_tag_type'] = 'single_tag'
602
603 downlink_classifier['pkt_tag_type'] = 'double_tag'
604 downlink_classifier['vlan_vid'] = uplink_action['vlan_vid']
605 downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
606 del downlink_action['push_vlan']
607 downlink_action['pop_vlan'] = True
608
609 # To-Do right now only one GEM port is supported, so below method
610 # will take care of handling all the p bits.
611 # We need to revisit when mulitple gem port per p bits is needed.
612 self.add_hsia_flow(onu_id, intf_id, uplink_classifier, uplink_action,
613 downlink_classifier, downlink_action,
614 ASFVOLT_HSIA_ID)
615
616 def mk_classifier(self, classifier_info):
617
618 classifier = openolt_pb2.Classifier()
619
620 if 'eth_type' in classifier_info:
621 classifier.eth_type = classifier_info['eth_type']
622 if 'ip_proto' in classifier_info:
623 classifier.ip_proto = classifier_info['ip_proto']
624 if 'vlan_vid' in classifier_info:
625 classifier.o_vid = classifier_info['vlan_vid']
626 if 'metadata' in classifier_info:
627 classifier.i_vid = classifier_info['metadata']
628 if 'vlan_pcp' in classifier_info:
629 classifier.o_pbits = classifier_info['vlan_pcp']
630 if 'udp_src' in classifier_info:
631 classifier.src_port = classifier_info['udp_src']
632 if 'udp_dst' in classifier_info:
633 classifier.dst_port = classifier_info['udp_dst']
634 if 'ipv4_dst' in classifier_info:
635 classifier.dst_ip = classifier_info['ipv4_dst']
636 if 'ipv4_src' in classifier_info:
637 classifier.src_ip = classifier_info['ipv4_src']
638 if 'pkt_tag_type' in classifier_info:
639 if classifier_info['pkt_tag_type'] == 'single_tag':
640 classifier.pkt_tag_type = 'single_tag'
641 elif classifier_info['pkt_tag_type'] == 'double_tag':
642 classifier.pkt_tag_type = 'double_tag'
643 elif classifier_info['pkt_tag_type'] == 'untagged':
644 classifier.pkt_tag_type = 'untagged'
645 else:
646 classifier.pkt_tag_type = 'none'
647
648 return classifier
649
650 def mk_action(self, action_info):
651 action = openolt_pb2.Action()
652
653 if 'pop_vlan' in action_info:
654 action.o_vid = action_info['vlan_vid']
655 action.cmd.remove_outer_tag = True
656 elif 'push_vlan' in action_info:
657 action.o_vid = action_info['vlan_vid']
658 action.cmd.add_outer_tag = True
659 elif 'trap_to_host' in action_info:
660 action.cmd.trap_to_host = True
661 else:
662 self.log.info('Invalid-action-field')
663 return
664 return action
665
666 def add_hsia_flow(self, onu_id, intf_id, uplink_classifier, uplink_action,
667 downlink_classifier, downlink_action, hsia_id):
668
669 gemport_id = self.mk_gemport_id(onu_id)
670 alloc_id = self.mk_alloc_id(onu_id)
671 flow_id = self.mk_flow_id(onu_id, intf_id, hsia_id)
672
673 self.log.info('add_hsia_flow',
674 onu_id=onu_id,
675 classifier=uplink_classifier,
676 action=uplink_action,
677 gemport_id=gemport_id,
678 flow_id=flow_id,
679 sched_info=alloc_id)
680
681 flow = openolt_pb2.Flow(
682 onu_id=onu_id,
683 flow_id=flow_id,
684 flow_type="upstream",
685 gemport_id=gemport_id,
686 classifier=self.mk_classifier(uplink_classifier),
687 action=self.mk_action(uplink_action))
688 self.stub.FlowAdd(flow)
689 time.sleep(0.1) # FIXME
690
691 self.log.info('Adding-ARP-downstream-flow',
692 classifier=downlink_classifier,
693 action=downlink_action,
694 gemport_id=gemport_id,
695 flow_id=flow_id)
696
697 flow = openolt_pb2.Flow(
698 onu_id=onu_id,
699 flow_id=flow_id,
700 flow_type="downstream",
701 access_intf_id=intf_id,
702 gemport_id=gemport_id,
703 classifier=self.mk_classifier(downlink_classifier),
704 action=self.mk_action(downlink_action))
705 self.stub.FlowAdd(flow)
706 time.sleep(0.1) # FIXME
707
708 def mk_flow_id(self, onu_id, intf_id, id):
709 # Tp-Do Need to generate unique flow ID using
710 # OnuID, IntfId, id
711 # BAL accepts flow_id till 16384. So we are
712 # using only onu_id and id to generate flow ID.
713 return ((onu_id << 5) | id)