blob: 00477b4dfb36611d94adb805047ef0ef073d13fb [file] [log] [blame]
Zsolt Harasztied091602016-12-08 13:36:38 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Harasztied091602016-12-08 13:36:38 -08003#
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
17"""
18Tibit ONU device adapter
19"""
20
Zsolt Haraszti348d1932016-12-10 01:10:07 -080021import json
Paul Grayeee44382017-03-31 09:00:25 -070022import time
23import struct
Zsolt Haraszti348d1932016-12-10 01:10:07 -080024
25from uuid import uuid4
26
Zsolt Harasztic5f740b2017-01-18 09:53:17 -080027import arrow
Zsolt Harasztied091602016-12-08 13:36:38 -080028import structlog
Zsolt Harasztic5f740b2017-01-18 09:53:17 -080029from twisted.internet.task import LoopingCall
Zsolt Harasztied091602016-12-08 13:36:38 -080030from zope.interface import implementer
31
Zsolt Haraszti348d1932016-12-10 01:10:07 -080032from scapy.layers.inet import ICMP, IP
33from scapy.layers.l2 import Ether
34from twisted.internet.defer import DeferredQueue, inlineCallbacks
35from twisted.internet import reactor
36
Zsolt Haraszti85f12852016-12-24 08:30:58 -080037from voltha.core.flow_decomposer import *
Zsolt Haraszti348d1932016-12-10 01:10:07 -080038from voltha.core.logical_device_agent import mac_str_to_tuple
Nathan Knutha1a11932017-01-12 16:59:58 -080039from common.frameio.frameio import BpfProgramFilter, hexify
Zsolt Harasztied091602016-12-08 13:36:38 -080040from voltha.adapters.interface import IAdapterInterface
41from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
Zsolt Haraszti348d1932016-12-10 01:10:07 -080042from voltha.protos.device_pb2 import Port
Zsolt Harasztied091602016-12-08 13:36:38 -080043from voltha.protos.device_pb2 import DeviceType, DeviceTypes
Zsolt Harasztic5f740b2017-01-18 09:53:17 -080044from voltha.protos.events_pb2 import KpiEventType
45from voltha.protos.events_pb2 import MetricValuePairs, KpiEvent
Zsolt Harasztied091602016-12-08 13:36:38 -080046from voltha.protos.health_pb2 import HealthStatus
Zsolt Haraszti348d1932016-12-10 01:10:07 -080047from voltha.protos.common_pb2 import LogLevel, ConnectStatus
Zsolt Haraszti348d1932016-12-10 01:10:07 -080048from voltha.protos.common_pb2 import OperStatus, AdminState
49
50from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
51from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_10GB_FD, \
52 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
53 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
54
55from scapy.packet import Packet, bind_layers
56from scapy.fields import StrField
Zsolt Harasztied091602016-12-08 13:36:38 -080057
58log = structlog.get_logger()
59
Nathan Knuth31c36962016-12-27 10:04:49 -080060from voltha.extensions.eoam.EOAM_TLV import AddStaticMacAddress, DeleteStaticMacAddress
61from voltha.extensions.eoam.EOAM_TLV import ClearStaticMacTable
62from voltha.extensions.eoam.EOAM_TLV import DeviceId
Nathan Knuthd8285e62017-01-11 14:18:43 -060063from voltha.extensions.eoam.EOAM_TLV import ClauseSubtypeEnum
64from voltha.extensions.eoam.EOAM_TLV import RuleOperatorEnum
Paul Grayeee44382017-03-31 09:00:25 -070065from voltha.extensions.eoam.EOAM_TLV import DPoEVariableResponseCodes
66from voltha.extensions.eoam.EOAM_TLV import TibitOUI
Nathan Knuthd8285e62017-01-11 14:18:43 -060067
Nathan Knuth31c36962016-12-27 10:04:49 -080068from voltha.extensions.eoam.EOAM import EOAMPayload, CablelabsOUI
69from voltha.extensions.eoam.EOAM import DPoEOpcode_GetRequest, DPoEOpcode_SetRequest
Nathan Knuth5f4163e2017-01-11 18:21:10 -060070from voltha.extensions.eoam.EOAM import mcastIp2McastMac
Zsolt Harasztied091602016-12-08 13:36:38 -080071
Paul Grayeee44382017-03-31 09:00:25 -070072TIBIT_MSG_WAIT_TIME = 3
73
74### Received OAM Message Types
75RxedOamMsgTypeEnum = {
76 "Unknown": 0x00,
77 # Info PDU - not currently used
78 "Info": 0x01,
79 # Event Notification - Tibit or DPoE Event
80 "Event Notification": 0x02,
81 "DPoE Get Response": 0x03,
82 "DPoE Set Response": 0x04,
83 # Specifically - a File Transfer ACK
84 "DPoE File Transfer": 0x05,
85 # Contains an embedded OMCI message
86 "OMCI Message": 0x06,
87 }
88
Zsolt Harasztied091602016-12-08 13:36:38 -080089@implementer(IAdapterInterface)
90class TibitOnuAdapter(object):
91
92 name = 'tibit_onu'
93
94 supported_device_types = [
95 DeviceType(
96 id='tibit_onu',
97 adapter=name,
98 accepts_bulk_flow_update=True
99 )
100 ]
101
102 def __init__(self, adapter_agent, config):
103 self.adapter_agent = adapter_agent
104 self.config = config
105 self.descriptor = Adapter(
106 id=self.name,
107 vendor='Tibit Communications Inc.',
108 version='0.1',
109 config=AdapterConfig(log_level=LogLevel.INFO)
110 )
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800111 self.incoming_messages = DeferredQueue()
Zsolt Harasztied091602016-12-08 13:36:38 -0800112
113 def start(self):
114 log.debug('starting')
115 log.info('started')
116
117 def stop(self):
118 log.debug('stopping')
119 log.info('stopped')
120
121 def adapter_descriptor(self):
122 return self.descriptor
123
124 def device_types(self):
125 return DeviceTypes(items=self.supported_device_types)
126
127 def health(self):
128 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
129
130 def change_master_state(self, master):
131 raise NotImplementedError()
132
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500133 def update_pm_config(self, device, pm_configs):
134 raise NotImplementedError()
135
Zsolt Harasztied091602016-12-08 13:36:38 -0800136 def adopt_device(self, device):
137 log.info('adopt-device', device=device)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800138 reactor.callLater(0.1, self._onu_device_activation, device)
Zsolt Harasztied091602016-12-08 13:36:38 -0800139 return device
140
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800141 @inlineCallbacks
142 def _onu_device_activation(self, device):
143 # first we verify that we got parent reference and proxy info
144 assert device.parent_id
145 assert device.proxy_address.device_id
146 assert device.proxy_address.channel_id
147
148 # TODO: For now, pretend that we were able to contact the device and obtain
149 # additional information about it. Should add real message.
150 device.vendor = 'Tibit Communications, Inc.'
151 device.model = '10G GPON ONU'
152 device.hardware_version = 'fa161020'
Nathan Knuthd8285e62017-01-11 14:18:43 -0600153 device.firmware_version = '16.12.02'
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800154 device.software_version = '1.0'
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800155 device.serial_number = uuid4().hex
156 device.connect_status = ConnectStatus.REACHABLE
157 self.adapter_agent.update_device(device)
158
159 # then shortly after we create some ports for the device
160 uni_port = Port(
161 port_no=2,
162 label='UNI facing Ethernet port',
163 type=Port.ETHERNET_UNI,
164 admin_state=AdminState.ENABLED,
165 oper_status=OperStatus.ACTIVE
166 )
167 self.adapter_agent.add_port(device.id, uni_port)
168 self.adapter_agent.add_port(device.id, Port(
169 port_no=1,
170 label='PON port',
171 type=Port.PON_ONU,
172 admin_state=AdminState.ENABLED,
173 oper_status=OperStatus.ACTIVE,
174 peers=[
175 Port.PeerPort(
176 device_id=device.parent_id,
177 port_no=device.parent_port_no
178 )
179 ]
180 ))
181
182 # TODO adding vports to the logical device shall be done by agent?
183 # then we create the logical device port that corresponds to the UNI
184 # port of the device
185
186 # obtain logical device id
187 parent_device = self.adapter_agent.get_device(device.parent_id)
188 logical_device_id = parent_device.parent_id
189 assert logical_device_id
190
191 # we are going to use the proxy_address.channel_id as unique number
192 # and name for the virtual ports, as this is guaranteed to be unique
193 # in the context of the OLT port, so it is also unique in the context
194 # of the logical device
195 port_no = device.proxy_address.channel_id
196 cap = OFPPF_10GB_FD | OFPPF_FIBER
197 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
198 id=str(port_no),
199 ofp_port=ofp_port(
200 port_no=port_no,
201 hw_addr=mac_str_to_tuple(device.mac_address),
202 name='uni-{}'.format(port_no),
203 config=0,
204 state=OFPPS_LIVE,
205 curr=cap,
206 advertised=cap,
207 peer=cap,
208 curr_speed=OFPPF_10GB_FD,
209 max_speed=OFPPF_10GB_FD
210 ),
211 device_id=device.id,
212 device_port_no=uni_port.port_no
213 ))
214
Nathan Knutha1a11932017-01-12 16:59:58 -0800215
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800216 # simulate a proxied message sending and receving a reply
217 reply = yield self._message_exchange(device)
218
219 # and finally update to "ACTIVE"
220 device = self.adapter_agent.get_device(device.id)
221 device.oper_status = OperStatus.ACTIVE
222 self.adapter_agent.update_device(device)
223
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800224 self.start_kpi_collection(device.id)
225
Zsolt Harasztied091602016-12-08 13:36:38 -0800226 def abandon_device(self, device):
227 raise NotImplementedError(0
228 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500229 def disable_device(self, device):
230 raise NotImplementedError()
231
232 def reenable_device(self, device):
233 raise NotImplementedError()
234
235 def reboot_device(self, device):
236 raise NotImplementedError()
237
238 def delete_device(self, device):
239 raise NotImplementedError()
240
241 def get_device_details(self, device):
Zsolt Harasztied091602016-12-08 13:36:38 -0800242 raise NotImplementedError()
243
Paul Grayeee44382017-03-31 09:00:25 -0700244 @inlineCallbacks
Zsolt Harasztied091602016-12-08 13:36:38 -0800245 def update_flows_bulk(self, device, flows, groups):
Nathan Knuthd8285e62017-01-11 14:18:43 -0600246 log.info('########################################')
247 log.info('bulk-flow-update', device_id=device.id,
248 flows=flows, groups=groups)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800249 assert len(groups.items) == 0, "Cannot yet deal with groups"
250
Nathan Knuthd8285e62017-01-11 14:18:43 -0600251 Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()}
252 Operator = {v: k for k, v in RuleOperatorEnum.iteritems()}
253
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800254 for flow in flows.items:
255 in_port = get_in_port(flow)
256 assert in_port is not None
257
Nathan Knuthd8285e62017-01-11 14:18:43 -0600258 precedence = 255 - min(flow.priority / 256, 255)
259
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800260 if in_port == 2:
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600261 log.info('#### Upstream Rule ####')
Paul Grayeee44382017-03-31 09:00:25 -0700262 dn_req = EOAMPayload(body=TibitOUI() /
Nathan Knuthd8285e62017-01-11 14:18:43 -0600263 DPoEOpcode_SetRequest())
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800264
265 for field in get_ofb_fields(flow):
Nathan Knuthd8285e62017-01-11 14:18:43 -0600266
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800267 if field.type == ETH_TYPE:
268 _type = field.eth_type
Nathan Knuthd8285e62017-01-11 14:18:43 -0600269 log.info('#### field.type == ETH_TYPE ####',field_type=_type)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800270
271 elif field.type == IP_PROTO:
272 _proto = field.ip_proto
Nathan Knuthd8285e62017-01-11 14:18:43 -0600273 log.info('#### field.type == IP_PROTO ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800274
275 elif field.type == IN_PORT:
276 _port = field.port
Nathan Knuthd8285e62017-01-11 14:18:43 -0600277 log.info('#### field.type == IN_PORT ####', port=_port)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800278
279 elif field.type == VLAN_VID:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600280 _vlan_vid = field.vlan_vid & 0xfff
281 log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800282
283 elif field.type == VLAN_PCP:
284 _vlan_pcp = field.vlan_pcp
Nathan Knuthd8285e62017-01-11 14:18:43 -0600285 log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800286
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800287 elif field.type == UDP_DST:
288 _udp_dst = field.udp_dst
Nathan Knuthd8285e62017-01-11 14:18:43 -0600289 log.info('#### field.type == UDP_DST ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800290
Nathan Knuthd8285e62017-01-11 14:18:43 -0600291 elif field.type == IPV4_DST:
292 _ipv4_dst = field.ipv4_dst
Nathan Knuthd8285e62017-01-11 14:18:43 -0600293 log.info('#### field.type == IPV4_DST ####')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600294
295 else:
296 log.info('#### field.type == NOT IMPLEMENTED!! ####')
297 raise NotImplementedError('field.type={}'.format(
298 field.type))
299
300 for action in get_actions(flow):
301
302 if action.type == OUTPUT:
303 log.info('#### action.type == OUTPUT ####')
304
305 elif action.type == POP_VLAN:
306 log.info('#### action.type == POP_VLAN ####')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600307
308 elif action.type == PUSH_VLAN:
309 log.info('#### action.type == PUSH_VLAN ####')
310 if action.push.ethertype != 0x8100:
311 log.error('unhandled-tpid',
312 ethertype=action.push.ethertype)
313
314 elif action.type == SET_FIELD:
315 log.info('#### action.type == SET_FIELD ####')
316 assert (action.set_field.field.oxm_class ==
317 ofp.OFPXMC_OPENFLOW_BASIC)
318 field = action.set_field.field.ofb_field
319 if field.type == VLAN_VID:
320 pass
321 else:
322 log.error('unsupported-action-set-field-type',
323 field_type=field.type)
324 else:
325 log.error('UNSUPPORTED-ACTION-TYPE',
326 action_type=action.type)
327
Nathan Knuthd8285e62017-01-11 14:18:43 -0600328 elif in_port == 1:
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600329 log.info('#### Downstream Rule ####')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600330
331 #### Loop through fields again...
332
333 for field in get_ofb_fields(flow):
334
335 if field.type == ETH_TYPE:
336 _type = field.eth_type
337 log.info('#### field.type == ETH_TYPE ####', in_port=in_port,
338 match=_type)
339
340 elif field.type == IP_PROTO:
341 _proto = field.ip_proto
342 log.info('#### field.type == IP_PROTO ####', in_port=in_port,
343 ip_proto=ip_proto)
344
345 elif field.type == IN_PORT:
346 _port = field.port
347 log.info('#### field.type == IN_PORT ####')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600348
349 elif field.type == VLAN_VID:
350 _vlan_vid = field.vlan_vid & 0xfff
351 log.info('#### field.type == VLAN_VID ####')
352
353 elif field.type == VLAN_PCP:
354 _vlan_pcp = field.vlan_pcp
355 log.info('#### field.type == VLAN_PCP ####')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600356
357 elif field.type == UDP_DST:
358 _udp_dst = field.udp_dst
359 log.info('#### field.type == UDP_DST ####')
360
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600361 elif field.type == IPV4_DST:
362 _ipv4_dst = field.ipv4_dst
363 log.info('#### field.type == IPV4_DST ####')
364 a = int(hex(_ipv4_dst)[2:4], 16)
365 b = int(hex(_ipv4_dst)[4:6], 16)
366 c = int(hex(_ipv4_dst)[6:8], 16)
367 d = int(hex(_ipv4_dst)[8:], 16)
Paul Grayeee44382017-03-31 09:00:25 -0700368 dn_req = EOAMPayload(body=TibitOUI() /
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600369 DPoEOpcode_SetRequest() /
370 AddStaticMacAddress(
371 mac=mcastIp2McastMac('%d.%d.%d.%d' % (a,b,c,d)))
372 )
Paul Grayeee44382017-03-31 09:00:25 -0700373
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600374 # send message
Paul Grayeee44382017-03-31 09:00:25 -0700375
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600376 log.info('ONU-send-proxied-message')
377 self.adapter_agent.send_proxied_message(device.proxy_address,
378 dn_req)
379
Paul Grayeee44382017-03-31 09:00:25 -0700380 # Get and process the Set Response
381 ack = False
382 start_time = time.time()
383
384 # Loop until we have a set response or timeout
385 while not ack:
386 frame = yield self.incoming_messages.get()
387 #TODO - Need to add propoer timeout functionality
388 #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
389 # break # don't wait forever
390
391 respType = self._voltha_get_oam_msg_type(frame)
392 log.info('Received OAM Message 0x %s' % str(respType))
393
394 #Check that the message received is a Set Response
395 if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
396 ack = True
397 else:
398 # Handle unexpected events/OMCI messages
399 self._voltha_check_resp(frame)
400
401 # Verify Set Response
402 if ack:
403 (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
404 log.info('REturn from Set resp')
405 if (rc == True):
406 log.info('Set Response had no errors')
407 else:
408 log.info('Set Respose had errors')
409 log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600410
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800411 else:
412 raise NotImplementedError('field.type={}'.format(
413 field.type))
414
415 for action in get_actions(flow):
416
417 if action.type == OUTPUT:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600418 log.info('#### action.type == OUTPUT ####')
419
420 elif action.type == POP_VLAN:
421 log.info('#### action.type == POP_VLAN ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800422
423 elif action.type == PUSH_VLAN:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600424 log.info('#### action.type == PUSH_VLAN ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800425 if action.push.ethertype != 0x8100:
426 log.error('unhandled-ether-type',
427 ethertype=action.push.ethertype)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800428
429 elif action.type == SET_FIELD:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600430 log.info('#### action.type == SET_FIELD ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800431 assert (action.set_field.field.oxm_class ==
432 ofp.OFPXMC_OPENFLOW_BASIC)
433 field = action.set_field.field.ofb_field
434 if field.type == VLAN_VID:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600435 pass
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800436 else:
437 log.error('unsupported-action-set-field-type',
438 field_type=field.type)
439
440 else:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600441 log.error('UNSUPPORTED-ACTION-TYPE',
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800442 action_type=action.type)
443
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800444 else:
445 raise Exception('Port should be 1 or 2 by our convention')
446
Zsolt Harasztied091602016-12-08 13:36:38 -0800447 def update_flows_incrementally(self, device, flow_changes, group_changes):
448 raise NotImplementedError()
449
450 def send_proxied_message(self, proxy_address, msg):
451 raise NotImplementedError()
452
453 def receive_proxied_message(self, proxy_address, msg):
Nathan Knutha1a11932017-01-12 16:59:58 -0800454 log.info('receive-proxied-message',
455 proxy_address=proxy_address, msg=msg.show(dump=True))
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800456 self.incoming_messages.put(msg)
457
Paul Grayeee44382017-03-31 09:00:25 -0700458
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800459 @inlineCallbacks
460 def _message_exchange(self, device):
461
462 # register for receiving async messages
463 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
464
465 # reset incoming message queue
466 while self.incoming_messages.pending:
467 _ = yield self.incoming_messages.get()
468
469 # construct message
Paul Grayeee44382017-03-31 09:00:25 -0700470 msg = EOAMPayload(body=TibitOUI() /
Nathan Knuth6e57f332016-12-22 15:49:20 -0800471 DPoEOpcode_GetRequest() /
472 DeviceId()
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800473 )
474
475 # send message
Nathan Knuth6e57f332016-12-22 15:49:20 -0800476 log.info('ONU-send-proxied-message')
Paul Grayeee44382017-03-31 09:00:25 -0700477
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800478 self.adapter_agent.send_proxied_message(device.proxy_address, msg)
479
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800480 # wait till we detect incoming message
481 yield self.incoming_messages.get()
Nathan Knuthd8285e62017-01-11 14:18:43 -0600482
483 # construct install of igmp query address
Paul Grayeee44382017-03-31 09:00:25 -0700484 msg = EOAMPayload(body=TibitOUI() /
Nathan Knuthd8285e62017-01-11 14:18:43 -0600485 DPoEOpcode_SetRequest() /
486 AddStaticMacAddress(mac='01:00:5e:00:00:01')
487 )
488
489 # send message
490 log.info('ONU-send-proxied-message')
491 self.adapter_agent.send_proxied_message(device.proxy_address, msg)
492
493 # wait till we detect incoming message
Paul Grayeee44382017-03-31 09:00:25 -0700494 #frame = yield self.incoming_messages.get()
495
496 # Get and process the Set Response
497 ack = False
498 start_time = time.time()
499
500 # Loop until we have a set response or timeout
501 while not ack:
502 frame = yield self.incoming_messages.get()
503 #TODO - Need to add propoer timeout functionality
504 #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
505 # break # don't wait forever
506
507 respType = self._voltha_get_oam_msg_type(frame)
508 log.info('Received OAM Message 0x %s' % str(respType))
509
510 #Check that the message received is a Set Response
511 if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
512 ack = True
513 else:
514 # Handle unexpected events/OMCI messages
515 self._voltha_check_resp(frame)
516
517 # Verify Set Response
518 if ack:
519 (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
520 log.info('REturn from Set resp')
521 if (rc == True):
522 log.info('Set Response had no errors')
523 else:
524 log.info('Set Respose had errors')
525 log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800526
527 # by returning we allow the device to be shown as active, which
528 # indirectly verified that message passing works
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800529
530 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
531 log.info('packet-out', logical_device_id=logical_device_id,
532 egress_port_no=egress_port_no, msg_len=len(msg))
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800533
Peter Shafik9107f2e2017-05-02 15:54:39 -0400534 def receive_inter_adapter_message(self, msg):
535 raise NotImplementedError()
536
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800537 def start_kpi_collection(self, device_id):
538
539 """TMP Simulate periodic KPI metric collection from the device"""
540 import random
541
542 @inlineCallbacks # pretend that we need to do async calls
543 def _collect(device_id, prefix):
544
545 try:
546 # Step 1: gather metrics from device (pretend it here) - examples
547 uni_port_metrics = yield dict(
548 tx_pkts=random.randint(0, 100),
549 rx_pkts=random.randint(0, 100),
550 tx_bytes=random.randint(0, 100000),
551 rx_bytes=random.randint(0, 100000),
552 )
553 pon_port_metrics = yield dict(
554 tx_pkts=uni_port_metrics['rx_pkts'],
555 rx_pkts=uni_port_metrics['tx_pkts'],
556 tx_bytes=uni_port_metrics['rx_bytes'],
557 rx_bytes=uni_port_metrics['tx_bytes'],
558 )
559 onu_metrics = yield dict(
560 cpu_util=20 + 5 * random.random(),
561 buffer_util=10 + 10 * random.random()
562 )
563
564 # Step 2: prepare the KpiEvent for submission
565 # we can time-stamp them here (or could use time derived from OLT
566 ts = arrow.utcnow().timestamp
567 kpi_event = KpiEvent(
568 type=KpiEventType.slice,
569 ts=ts,
570 prefixes={
571 # OLT-level
572 prefix: MetricValuePairs(metrics=onu_metrics),
573 # OLT NNI port
574 prefix + '.nni': MetricValuePairs(metrics=uni_port_metrics),
575 # OLT PON port
576 prefix + '.pon': MetricValuePairs(metrics=pon_port_metrics)
577 }
578 )
579
580 # Step 3: submit
581 self.adapter_agent.submit_kpis(kpi_event)
582
583 except Exception as e:
584 log.exception('failed-to-submit-kpis', e=e)
585
586 prefix = 'voltha.{}.{}'.format(self.name, device_id)
587 lc = LoopingCall(_collect, device_id, prefix)
588 lc.start(interval=15) # TODO make this configurable
589
Paul Grayeee44382017-03-31 09:00:25 -0700590 def _voltha_get_oam_msg_type(self, frame):
591 respType = RxedOamMsgTypeEnum["Unknown"]
592 recv_frame = frame
593 payload = recv_frame.payload
594 if hasattr(payload, 'body'):
595 loadstr = payload.body.load
596 bytesRead = 0;
597 if (payload.opcode == 0xFE):
598
599 # Extract the OUI
600 (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
601 oui = (oui_hi << 16) | oui_lo
602 log.debug('oui: 0x %06x' % oui)
603 bytesRead += 3
604
605 # If this is the ITU OUI, then there is an embedded OMCI message
606 if (oui == 0x0019A7):
607 respType = RxedOamMsgTypeEnum["OMCI Message"]
608
609 # Treat Cablelabs OUI and Tibit OUI as the same
610 elif ((oui == 0x001000) or (oui == 0x2AEA15)):
611
612 (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
613 bytesRead += 1
614
615 # Get Response
616 if (dpoeOpcode == 0x02):
617 respType = RxedOamMsgTypeEnum["DPoE Get Response"]
618
619 # Set Response
620 elif (dpoeOpcode == 0x04):
621 respType = RxedOamMsgTypeEnum["DPoE Set Response"]
622
623 # File Transfer ACK
624 elif (dpoeOpcode == 0x09):
625 respType = RxedOamMsgTypeEnum["DPoE File Transfer"]
626
627 else:
628 log.info('Unsupported OAM OUI 0x{:0>6X}'.format(oui))
629
630 # Handle OAM Event Notification
631 elif (payload.opcode == 0x01):
632 respType = RxedOamMsgTypeEnum["Event Notification"]
633 else:
634 log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
635 else:
636 log.debug('received frame has no payload')
637
638 return respType
639
640
641 def _voltha_check_set_resp(self, frame):
642 rc = False
643 branch = 0
644 leaf = 0
645 status = 0
646
647 recv_frame = frame
648 payload = recv_frame.payload
649 if hasattr(payload, 'body'):
650 loadstr = payload.body.load
651 bytesRead = 0;
652 if (payload.opcode == 0xFE):
653
654 # Extract the OUI
655 (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
656 oui = (oui_hi << 16) | oui_lo
657 log.info('oui: 0x %06x' % oui)
658 bytesRead += 3
659
660 # Treat Cablelabs OUI and Tibit OUI as the same
661 if ((oui == 0x001000) or (oui == 0x2AEA15)):
662 (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
663 bytesRead += 1
664
665 startOfTlvs = bytesRead
666 # Set Response
667 if (dpoeOpcode == 0x04):
668 test =1
669 (rc,branch,leaf,status) = self._voltha_check_set_resp_attrs(loadstr, startOfTlvs)
670 if (rc == True):
671 log.info('Set Response had no errors')
672 else:
673 log.debug('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
674 else:
675 log.info('Unsupported DPoE Opcode: {} ({:0>2X})'.format(DPoEOpcodeEnum[dpoeOpcode], dpoeOpcode))
676 else:
677 log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
678 else:
679 log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
680 else:
681 log.debug('received frame has no payload')
682
683 return rc,branch,leaf,status
684
685
686 def _voltha_check_resp(self, frame):
687 recv_frame = frame
688 payload = recv_frame.payload
689 if hasattr(payload, 'body'):
690 loadstr = payload.body.load
691 bytesRead = 0;
692 if (payload.opcode == 0xFE):
693
694 # Extract the OUI
695 (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
696 oui = (oui_hi << 16) | oui_lo
697 log.info('oui: 0x %06x' % oui)
698 bytesRead += 3
699
700 # If this is the ITU OUI, then there is an embedded OMCI message
701 if (oui == 0x0019A7):
702 self._voltha_handle_omci(loadstr,bytesRead)
703
704 # Treat Cablelabs OUI and Tibit OUI as the same
705 elif ((oui == 0x001000) or (oui == 0x2AEA15)):
706 log.debug('Recieved Response OUI 0x{:0>6X}'. format(oui))
707 else:
708 log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
709
710 # Handle OAM Event Notification
711 elif (payload.opcode == 0x01):
712 self._voltha_handle_oam_event(loadstr, bytesRead)
713 else:
714 log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
715
716 else:
717 log.debug('received frame has no payload')
718
719
720 def _voltha_handle_oam_event(self, loadstr, startOfEvent):
721 bytesRead = startOfEvent
722 (seq_num, tlv_type, ev_len, oui_hi, oui_lo) = struct.unpack_from('>HBBBH', loadstr, bytesRead)
723 oui = (oui_hi << 16) | oui_lo
724
725 log.info('seq_num: 0x%04x' % seq_num)
726 log.info('tlv_type: 0x%' % tlv_type)
727 log.info('ev_len: 0x%x' % ev_len)
728 log.info('oui: 0x%06x"'% oui)
729
730 if (tlv_type != 0xFE):
731 log.debug('unexpected tlv_type 0x%x (expected 0xFE)' % tlv_type)
732 elif (oui == 0x001000):
733 log.debug('DPoE Event')
734 ## TODO - Handle DPoE Event/Alarm
735 elif (oui == 0x2AEA15):
736 log.debug('Tibit-specific Event')
737
738 # TODO - Handle addition/removal of links
739
740 bytesRead = 7
741
742 # TODO - Check OUI and parse Source and Reference Object Contexts
743
744
745 def _voltha_handle_omci(self, loadstr, startOfEvent):
746 bytesRead = startOfEvent
747 log.debug('OMCI Message')
748
749 # TODO - Handle OMCI message
750
751
752
753 def _voltha_handle_get_value(self, loadstr, startOfTlvs, queryBranch, queryLeaf):
754 retVal = False;
755 value = 0
756 branch = 0
757 leaf = 0
758 bytesRead = startOfTlvs
759 loadstrlen = len(loadstr)
760
761 while (bytesRead <= loadstrlen):
762 (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)
763
764 if (branch != 0):
765 bytesRead += 3
766 length = struct.unpack_from('>B', loadstr, bytesRead)[0]
767 bytesRead += 1
768
769 if (length == 1):
770 value = struct.unpack_from(">B", loadstr, bytesRead)[0]
771 elif (length == 2):
772 value = struct.unpack_from(">H", loadstr, bytesRead)[0]
773 elif (length == 4):
774 value = struct.unpack_from(">I", loadstr, bytesRead)[0]
775 elif (length == 8):
776 value = struct.unpack_from(">Q", loadstr, bytesRead)[0]
777 else:
778 if (length >= 0x80):
779 log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
780 # Set length to zero so bytesRead doesn't get mistakenly incremented below
781 length = 0
782 else:
783 # Attributes with a length of zero are actually 128 bytes long
784 if (length == 0):
785 length = 128;
786 valStr = ">{}s".format(length)
787 value = struct.unpack_from(valStr, loadstr, bytesRead)[0]
788
789 if (length > 0):
790 bytesRead += length
791
792 if (branch != 0xD6):
793 if ( ((queryBranch == 0) and (queryLeaf == 0)) or
794 ((queryBranch == branch) and (queryLeaf == leaf)) ):
795 # Prevent zero-lengthed values from returning success
796 if (length > 0):
797 retVal = True;
798 break
799 else:
800 break
801
802 if (retVal == False):
803 value = 0
804
805 return retVal,bytesRead,value,branch,leaf
806
807 def _voltha_check_set_resp_attrs(self, loadstr, startOfTlvs):
808 retVal = True;
809 branch = 0
810 leaf = 0
811 length = 0
812 bytesRead = startOfTlvs
813 loadstrlen = len(loadstr)
814
815 while (bytesRead <= loadstrlen):
816 (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)
817
818 if (branch != 0):
819 bytesRead += 3
820 length = struct.unpack_from('>B', loadstr, bytesRead)[0]
821 bytesRead += 1
822
823 if (length >= 0x80):
824 log.debug('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
825 if (length > 0x80):
826 retVal = False;
827 break;
828 else:
829 bytesRead += length
830
831 else:
832 break
833
834 return retVal,branch,leaf,length
835
836
837
838 def _voltha_handle_fx_ack(self, loadstr, startOfXfer, block_number):
839 retVal = False
840 (fx_opcode, acked_block, response_code) = struct.unpack_from('>BHB', loadstr, startOfXfer)
841
842 log.debug('fx_opcode: 0x%x' % fx_opcode)
843 log.debug('acked_block: 0x%x' % acked_block)
844 log.debug('response_code: 0x%x' % response_code)
845
846
847
848 if (fx_opcode != 0x03):
849 log.debug('unexpected fx_opcode 0x%x (expected 0x03)' % fx_opcode)
850 elif (acked_block != block_number):
851 log.debug('unexpected acked_block 0x%x (expected 0x%x)' % (acked_block, block_number))
852 elif (response_code != 0):
853 log.debug('unexpected response_code 0x%x (expected 0x00)' % response_code)
854 else:
855 retVal = True;
856
857
858