Added ONU Set Response Handling and Modified to use TibitOUI
Change-Id: I7dcafe24d6fa9f789353d1ed3bbc57241d43ed45
diff --git a/voltha/adapters/tibit_onu/tibit_onu.py b/voltha/adapters/tibit_onu/tibit_onu.py
index 72a6f2a..849bf5a 100644
--- a/voltha/adapters/tibit_onu/tibit_onu.py
+++ b/voltha/adapters/tibit_onu/tibit_onu.py
@@ -19,6 +19,8 @@
"""
import json
+import time
+import struct
from uuid import uuid4
@@ -60,11 +62,30 @@
from voltha.extensions.eoam.EOAM_TLV import DeviceId
from voltha.extensions.eoam.EOAM_TLV import ClauseSubtypeEnum
from voltha.extensions.eoam.EOAM_TLV import RuleOperatorEnum
+from voltha.extensions.eoam.EOAM_TLV import DPoEVariableResponseCodes
+from voltha.extensions.eoam.EOAM_TLV import TibitOUI
from voltha.extensions.eoam.EOAM import EOAMPayload, CablelabsOUI
from voltha.extensions.eoam.EOAM import DPoEOpcode_GetRequest, DPoEOpcode_SetRequest
from voltha.extensions.eoam.EOAM import mcastIp2McastMac
+TIBIT_MSG_WAIT_TIME = 3
+
+### Received OAM Message Types
+RxedOamMsgTypeEnum = {
+ "Unknown": 0x00,
+ # Info PDU - not currently used
+ "Info": 0x01,
+ # Event Notification - Tibit or DPoE Event
+ "Event Notification": 0x02,
+ "DPoE Get Response": 0x03,
+ "DPoE Set Response": 0x04,
+ # Specifically - a File Transfer ACK
+ "DPoE File Transfer": 0x05,
+ # Contains an embedded OMCI message
+ "OMCI Message": 0x06,
+ }
+
@implementer(IAdapterInterface)
class TibitOnuAdapter(object):
@@ -220,6 +241,7 @@
def get_device_details(self, device):
raise NotImplementedError()
+ @inlineCallbacks
def update_flows_bulk(self, device, flows, groups):
log.info('########################################')
log.info('bulk-flow-update', device_id=device.id,
@@ -237,7 +259,7 @@
if in_port == 2:
log.info('#### Upstream Rule ####')
- dn_req = EOAMPayload(body=CablelabsOUI() /
+ dn_req = EOAMPayload(body=TibitOUI() /
DPoEOpcode_SetRequest())
for field in get_ofb_fields(flow):
@@ -343,16 +365,48 @@
b = int(hex(_ipv4_dst)[4:6], 16)
c = int(hex(_ipv4_dst)[6:8], 16)
d = int(hex(_ipv4_dst)[8:], 16)
- dn_req = EOAMPayload(body=CablelabsOUI() /
+ dn_req = EOAMPayload(body=TibitOUI() /
DPoEOpcode_SetRequest() /
AddStaticMacAddress(
mac=mcastIp2McastMac('%d.%d.%d.%d' % (a,b,c,d)))
)
+
# send message
+
log.info('ONU-send-proxied-message')
self.adapter_agent.send_proxied_message(device.proxy_address,
dn_req)
+ # Get and process the Set Response
+ ack = False
+ start_time = time.time()
+
+ # Loop until we have a set response or timeout
+ while not ack:
+ frame = yield self.incoming_messages.get()
+ #TODO - Need to add propoer timeout functionality
+ #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
+ # break # don't wait forever
+
+ respType = self._voltha_get_oam_msg_type(frame)
+ log.info('Received OAM Message 0x %s' % str(respType))
+
+ #Check that the message received is a Set Response
+ if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
+ ack = True
+ else:
+ # Handle unexpected events/OMCI messages
+ self._voltha_check_resp(frame)
+
+ # Verify Set Response
+ if ack:
+ (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
+ log.info('REturn from Set resp')
+ if (rc == True):
+ log.info('Set Response had no errors')
+ else:
+ log.info('Set Respose had errors')
+ log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
else:
raise NotImplementedError('field.type={}'.format(
@@ -401,6 +455,7 @@
proxy_address=proxy_address, msg=msg.show(dump=True))
self.incoming_messages.put(msg)
+
@inlineCallbacks
def _message_exchange(self, device):
@@ -412,20 +467,21 @@
_ = yield self.incoming_messages.get()
# construct message
- msg = EOAMPayload(body=CablelabsOUI() /
+ msg = EOAMPayload(body=TibitOUI() /
DPoEOpcode_GetRequest() /
DeviceId()
)
# send message
log.info('ONU-send-proxied-message')
+
self.adapter_agent.send_proxied_message(device.proxy_address, msg)
# wait till we detect incoming message
yield self.incoming_messages.get()
# construct install of igmp query address
- msg = EOAMPayload(body=CablelabsOUI() /
+ msg = EOAMPayload(body=TibitOUI() /
DPoEOpcode_SetRequest() /
AddStaticMacAddress(mac='01:00:5e:00:00:01')
)
@@ -435,7 +491,38 @@
self.adapter_agent.send_proxied_message(device.proxy_address, msg)
# wait till we detect incoming message
- yield self.incoming_messages.get()
+ #frame = yield self.incoming_messages.get()
+
+ # Get and process the Set Response
+ ack = False
+ start_time = time.time()
+
+ # Loop until we have a set response or timeout
+ while not ack:
+ frame = yield self.incoming_messages.get()
+ #TODO - Need to add propoer timeout functionality
+ #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
+ # break # don't wait forever
+
+ respType = self._voltha_get_oam_msg_type(frame)
+ log.info('Received OAM Message 0x %s' % str(respType))
+
+ #Check that the message received is a Set Response
+ if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
+ ack = True
+ else:
+ # Handle unexpected events/OMCI messages
+ self._voltha_check_resp(frame)
+
+ # Verify Set Response
+ if ack:
+ (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
+ log.info('REturn from Set resp')
+ if (rc == True):
+ log.info('Set Response had no errors')
+ else:
+ log.info('Set Respose had errors')
+ log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
# by returning we allow the device to be shown as active, which
# indirectly verified that message passing works
@@ -497,3 +584,272 @@
lc = LoopingCall(_collect, device_id, prefix)
lc.start(interval=15) # TODO make this configurable
+ def _voltha_get_oam_msg_type(self, frame):
+ respType = RxedOamMsgTypeEnum["Unknown"]
+ recv_frame = frame
+ payload = recv_frame.payload
+ if hasattr(payload, 'body'):
+ loadstr = payload.body.load
+ bytesRead = 0;
+ if (payload.opcode == 0xFE):
+
+ # Extract the OUI
+ (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
+ oui = (oui_hi << 16) | oui_lo
+ log.debug('oui: 0x %06x' % oui)
+ bytesRead += 3
+
+ # If this is the ITU OUI, then there is an embedded OMCI message
+ if (oui == 0x0019A7):
+ respType = RxedOamMsgTypeEnum["OMCI Message"]
+
+ # Treat Cablelabs OUI and Tibit OUI as the same
+ elif ((oui == 0x001000) or (oui == 0x2AEA15)):
+
+ (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
+ bytesRead += 1
+
+ # Get Response
+ if (dpoeOpcode == 0x02):
+ respType = RxedOamMsgTypeEnum["DPoE Get Response"]
+
+ # Set Response
+ elif (dpoeOpcode == 0x04):
+ respType = RxedOamMsgTypeEnum["DPoE Set Response"]
+
+ # File Transfer ACK
+ elif (dpoeOpcode == 0x09):
+ respType = RxedOamMsgTypeEnum["DPoE File Transfer"]
+
+ else:
+ log.info('Unsupported OAM OUI 0x{:0>6X}'.format(oui))
+
+ # Handle OAM Event Notification
+ elif (payload.opcode == 0x01):
+ respType = RxedOamMsgTypeEnum["Event Notification"]
+ else:
+ log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
+ else:
+ log.debug('received frame has no payload')
+
+ return respType
+
+
+ def _voltha_check_set_resp(self, frame):
+ rc = False
+ branch = 0
+ leaf = 0
+ status = 0
+
+ recv_frame = frame
+ payload = recv_frame.payload
+ if hasattr(payload, 'body'):
+ loadstr = payload.body.load
+ bytesRead = 0;
+ if (payload.opcode == 0xFE):
+
+ # Extract the OUI
+ (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
+ oui = (oui_hi << 16) | oui_lo
+ log.info('oui: 0x %06x' % oui)
+ bytesRead += 3
+
+ # Treat Cablelabs OUI and Tibit OUI as the same
+ if ((oui == 0x001000) or (oui == 0x2AEA15)):
+ (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
+ bytesRead += 1
+
+ startOfTlvs = bytesRead
+ # Set Response
+ if (dpoeOpcode == 0x04):
+ test =1
+ (rc,branch,leaf,status) = self._voltha_check_set_resp_attrs(loadstr, startOfTlvs)
+ if (rc == True):
+ log.info('Set Response had no errors')
+ else:
+ log.debug('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
+ else:
+ log.info('Unsupported DPoE Opcode: {} ({:0>2X})'.format(DPoEOpcodeEnum[dpoeOpcode], dpoeOpcode))
+ else:
+ log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
+ else:
+ log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
+ else:
+ log.debug('received frame has no payload')
+
+ return rc,branch,leaf,status
+
+
+ def _voltha_check_resp(self, frame):
+ recv_frame = frame
+ payload = recv_frame.payload
+ if hasattr(payload, 'body'):
+ loadstr = payload.body.load
+ bytesRead = 0;
+ if (payload.opcode == 0xFE):
+
+ # Extract the OUI
+ (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
+ oui = (oui_hi << 16) | oui_lo
+ log.info('oui: 0x %06x' % oui)
+ bytesRead += 3
+
+ # If this is the ITU OUI, then there is an embedded OMCI message
+ if (oui == 0x0019A7):
+ self._voltha_handle_omci(loadstr,bytesRead)
+
+ # Treat Cablelabs OUI and Tibit OUI as the same
+ elif ((oui == 0x001000) or (oui == 0x2AEA15)):
+ log.debug('Recieved Response OUI 0x{:0>6X}'. format(oui))
+ else:
+ log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
+
+ # Handle OAM Event Notification
+ elif (payload.opcode == 0x01):
+ self._voltha_handle_oam_event(loadstr, bytesRead)
+ else:
+ log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
+
+ else:
+ log.debug('received frame has no payload')
+
+
+ def _voltha_handle_oam_event(self, loadstr, startOfEvent):
+ bytesRead = startOfEvent
+ (seq_num, tlv_type, ev_len, oui_hi, oui_lo) = struct.unpack_from('>HBBBH', loadstr, bytesRead)
+ oui = (oui_hi << 16) | oui_lo
+
+ log.info('seq_num: 0x%04x' % seq_num)
+ log.info('tlv_type: 0x%' % tlv_type)
+ log.info('ev_len: 0x%x' % ev_len)
+ log.info('oui: 0x%06x"'% oui)
+
+ if (tlv_type != 0xFE):
+ log.debug('unexpected tlv_type 0x%x (expected 0xFE)' % tlv_type)
+ elif (oui == 0x001000):
+ log.debug('DPoE Event')
+ ## TODO - Handle DPoE Event/Alarm
+ elif (oui == 0x2AEA15):
+ log.debug('Tibit-specific Event')
+
+ # TODO - Handle addition/removal of links
+
+ bytesRead = 7
+
+ # TODO - Check OUI and parse Source and Reference Object Contexts
+
+
+ def _voltha_handle_omci(self, loadstr, startOfEvent):
+ bytesRead = startOfEvent
+ log.debug('OMCI Message')
+
+ # TODO - Handle OMCI message
+
+
+
+ def _voltha_handle_get_value(self, loadstr, startOfTlvs, queryBranch, queryLeaf):
+ retVal = False;
+ value = 0
+ branch = 0
+ leaf = 0
+ bytesRead = startOfTlvs
+ loadstrlen = len(loadstr)
+
+ while (bytesRead <= loadstrlen):
+ (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)
+
+ if (branch != 0):
+ bytesRead += 3
+ length = struct.unpack_from('>B', loadstr, bytesRead)[0]
+ bytesRead += 1
+
+ if (length == 1):
+ value = struct.unpack_from(">B", loadstr, bytesRead)[0]
+ elif (length == 2):
+ value = struct.unpack_from(">H", loadstr, bytesRead)[0]
+ elif (length == 4):
+ value = struct.unpack_from(">I", loadstr, bytesRead)[0]
+ elif (length == 8):
+ value = struct.unpack_from(">Q", loadstr, bytesRead)[0]
+ else:
+ if (length >= 0x80):
+ log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
+ # Set length to zero so bytesRead doesn't get mistakenly incremented below
+ length = 0
+ else:
+ # Attributes with a length of zero are actually 128 bytes long
+ if (length == 0):
+ length = 128;
+ valStr = ">{}s".format(length)
+ value = struct.unpack_from(valStr, loadstr, bytesRead)[0]
+
+ if (length > 0):
+ bytesRead += length
+
+ if (branch != 0xD6):
+ if ( ((queryBranch == 0) and (queryLeaf == 0)) or
+ ((queryBranch == branch) and (queryLeaf == leaf)) ):
+ # Prevent zero-lengthed values from returning success
+ if (length > 0):
+ retVal = True;
+ break
+ else:
+ break
+
+ if (retVal == False):
+ value = 0
+
+ return retVal,bytesRead,value,branch,leaf
+
+ def _voltha_check_set_resp_attrs(self, loadstr, startOfTlvs):
+ retVal = True;
+ branch = 0
+ leaf = 0
+ length = 0
+ bytesRead = startOfTlvs
+ loadstrlen = len(loadstr)
+
+ while (bytesRead <= loadstrlen):
+ (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)
+
+ if (branch != 0):
+ bytesRead += 3
+ length = struct.unpack_from('>B', loadstr, bytesRead)[0]
+ bytesRead += 1
+
+ if (length >= 0x80):
+ log.debug('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
+ if (length > 0x80):
+ retVal = False;
+ break;
+ else:
+ bytesRead += length
+
+ else:
+ break
+
+ return retVal,branch,leaf,length
+
+
+
+ def _voltha_handle_fx_ack(self, loadstr, startOfXfer, block_number):
+ retVal = False
+ (fx_opcode, acked_block, response_code) = struct.unpack_from('>BHB', loadstr, startOfXfer)
+
+ log.debug('fx_opcode: 0x%x' % fx_opcode)
+ log.debug('acked_block: 0x%x' % acked_block)
+ log.debug('response_code: 0x%x' % response_code)
+
+
+
+ if (fx_opcode != 0x03):
+ log.debug('unexpected fx_opcode 0x%x (expected 0x03)' % fx_opcode)
+ elif (acked_block != block_number):
+ log.debug('unexpected acked_block 0x%x (expected 0x%x)' % (acked_block, block_number))
+ elif (response_code != 0):
+ log.debug('unexpected response_code 0x%x (expected 0x00)' % response_code)
+ else:
+ retVal = True;
+
+
+