Skip to content

Commit

Permalink
ADTRAN Adapters: Bug fixes for device flows after deprecation of xPON
Browse files Browse the repository at this point in the history
Change-Id: I77ca3a2c0431293a3ddfb4576860b0554392c486
  • Loading branch information
Chip Boling committed Nov 30, 2018
1 parent f16726d commit 8b79a9b
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 112 deletions.
2 changes: 1 addition & 1 deletion voltha/adapters/adtran_olt/adtran_olt.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, adapter_agent, config):
self.descriptor = Adapter(
id=self.name,
vendor='ADTRAN, Inc.',
version='1.32',
version='1.33',
config=AdapterConfig(log_level=LogLevel.INFO)
)
log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
Expand Down
3 changes: 1 addition & 2 deletions voltha/adapters/adtran_olt/adtran_olt_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,8 +854,7 @@ def _create_utility_flow(self):
priority=200,
match_fields=[
in_port(nni_port),
vlan_vid(ofp.OFPVID_PRESENT + self.utility_vlan),
# eth_type(FlowEntry.EtherType.EAPOL) ?? TODO: is this needed
vlan_vid(ofp.OFPVID_PRESENT + self.utility_vlan)
],
actions=[output(pon_port)]
)
Expand Down
6 changes: 4 additions & 2 deletions voltha/adapters/adtran_olt/flow/evc.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,10 @@ def evc_map_names(self):

def add_evc_map(self, evc_map):
if self._evc_maps is None:
self._evc_maps = {}
self._evc_maps[evc_map.name] = evc_map
self._evc_maps = dict()

if evc_map.name not in self._evc_maps:
self._evc_maps[evc_map.name] = evc_map

def remove_evc_map(self, evc_map):
if self._evc_maps is not None and evc_map.name in self._evc_maps:
Expand Down
23 changes: 17 additions & 6 deletions voltha/adapters/adtran_olt/flow/evc_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def _xml_trailer():
return '</evc-map></evc-maps>'

def get_evcmap_name(self, onu_id, gem_id):
return'{}.{}.{}'.format(self.name, onu_id, gem_id)
return'{}.{}.{}.{}'.format(self.name, onu_id, self.pon_id, gem_id)

def _common_install_xml(self):
xml = '<enabled>{}</enabled>'.format('true' if self._enabled else 'false')
Expand Down Expand Up @@ -258,10 +258,11 @@ def _ingress_install_xml(self, onu_s_gem_ids_and_vid, acl_list, create):
xml += '<ce-vlan-id>{}</ce-vlan-id>'.format(Onu.gem_id_to_gvid(gem_id))

# GEM-IDs are a sorted list (ascending). First gemport handles downstream traffic
if first_gem_id and vid is not None:
if first_gem_id and (self._c_tag is not None or vid is not None):
first_gem_id = False
vlan = vid or self._c_tag
xml += '<network-ingress-filter>'
xml += '<men-ctag>{}</men-ctag>'.format(vid) # Added in August 2017 model
xml += '<men-ctag>{}</men-ctag>'.format(vlan) # Added in August 2017 model
xml += '</network-ingress-filter>'

if len(acl_list):
Expand Down Expand Up @@ -319,6 +320,7 @@ def gem_ports():
work_acls = self._new_acls.copy()
self._new_acls = dict()

log.debug('install-evc-map-acls', install_acls=len(work_acls))
for acl in work_acls.itervalues():
try:
yield acl.install()
Expand All @@ -328,6 +330,13 @@ def gem_ports():
self._new_acls.update(work_acls)
raise

# Any user-data flows attached to this map ?
c_tag = None
for flow_id, flow in self._flows.items():
c_tag = flow.inner_vid or flow.vlan_id or c_tag

self._c_tag = c_tag

# Now EVC-MAP
if not self._installed or self._needs_update:
log.debug('needs-install-or-update', installed=self._installed, update=self._needs_update)
Expand Down Expand Up @@ -493,7 +502,7 @@ def find_matching_ingress_flow(flow, upstream_flow_table):
def add_flow(self, flow, evc):
"""
Add a new flow to an existing EVC-MAP. This can be called to add:
o an ACL flow to an existing utility/untagged EVC, or
o an ACL flow to an existing utility EVC, or
o an ACL flow to an existing User Data Flow, or
o a User Data Flow to an existing ACL flow (and this needs the EVC updated
as well.
Expand Down Expand Up @@ -522,6 +531,7 @@ def add_flow(self, flow, evc):
self._flows[flow.flow_id] = flow
self._needs_update = True

# Are there ACLs to add to any existing (or empty) ACLs
if len(tmp_map._new_acls) > 0:
self._new_acls.update(tmp_map._new_acls) # New ACL flow
log.debug('add-acl-flows', map=str(self), new=tmp_map._new_acls)
Expand All @@ -533,6 +543,7 @@ def add_flow(self, flow, evc):
self._evc.remove_evc_map(self)
evc.add_evc_map(self)
self._evc = evc

return self

@inlineCallbacks
Expand Down Expand Up @@ -571,7 +582,7 @@ def _remove_flow(self, flow):
# or Untagged EVC from a user data EVC
if self._evc and not self._evc.service_evc and\
len(self._flows) > 0 and\
all(f.is_acl_flow for f in self._flows.itervalues()):
all(f.is_acl_flow for f in self._flows.itervalues()):

self._evc.remove_evc_map(self)
first_flow = self._flows.itervalues().next()
Expand Down Expand Up @@ -615,7 +626,7 @@ def decode_evc_map_name(name):
# Note: When actually installed into the OLT, the .onu_id.gem_port is
# appended to the name
return {'ingress-port': items[1],
'flow-id': items[2].split('.')[0]} if len(items) == 3 else dict()
'flow-id': items[2].split('.')[0]} if len(items) > 2 else dict()

def add_gem_port(self, gem_port, reflow=False):
# TODO: Refactor
Expand Down
9 changes: 7 additions & 2 deletions voltha/adapters/adtran_olt/flow/flow_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class FlowDirection(IntEnum):
UPSTREAM = 0 # UNI port to NNI Port
DOWNSTREAM = 1 # NNI port to UNI Port
CONTROLLER_UNI = 2 # Trap packet on UNI and send to controller
NNI_PON = 3 # NNI port to PON Port (all UNIs) - perhaps multicast?
NNI_PON = 3 # NNI port to PON Port (all UNIs) - Utility VLAN & multicast

# The following are not yet supported
CONTROLLER_NNI = 4 # Trap packet on NNI and send to controller
Expand Down Expand Up @@ -585,6 +585,11 @@ def _apply_downstream_mods(self):
if self.vlan_id == FlowEntry.LEGACY_CONTROL_VLAN and self.eth_type is None and self.pcp == 0:
return False # Do not install this flow. Utility VLAN is in charge

elif self.flow_direction == FlowEntry.FlowDirection.NNI_PON and \
self.vlan_id == self.handler.utility_vlan:
# Utility VLAN downstream flow/EVC
self._is_acl_flow = True

elif self.vlan_id in self._handler.multicast_vlans:
# multicast (ethType = IP) # TODO: May need to be an NNI_PON flow
self._is_multicast = True
Expand Down Expand Up @@ -800,7 +805,7 @@ def get_packetout_info(handler, logical_port):
if len(gem_ids_with_vid) > 0:
gem_ids = gem_ids_with_vid[0]
ctag = gem_ids_with_vid[1]
gem_id = gem_ids[0] # TODO: always grab fist in list
gem_id = gem_ids[0] # TODO: always grab first in list
return flow_entry.in_port, ctag, Onu.gem_id_to_gvid(gem_id), \
evc_map.get_evcmap_name(onu_id, gem_id)
return None, None, None, None
3 changes: 2 additions & 1 deletion voltha/adapters/adtran_olt/flow/flow_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def __len__(self):

def add(self, flow):
assert isinstance(flow, FlowEntry)
self._flow_table[flow.flow_id] = flow
if flow.flow_id not in self._flow_table:
self._flow_table[flow.flow_id] = flow
return flow

def get(self, item):
Expand Down
2 changes: 1 addition & 1 deletion voltha/adapters/adtran_onu/adtran_onu.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(self, adapter_agent, config):
device_handler_class=AdtranOnuHandler,
name='adtran_onu',
vendor='ADTRAN, Inc.',
version='1.21',
version='1.22',
device_type='adtran_onu',
vendor_id='ADTN',
accepts_add_remove_flow_updates=False), # TODO: Support flow-mods
Expand Down
97 changes: 51 additions & 46 deletions voltha/adapters/adtran_onu/adtran_onu_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,18 @@ def stop(self):
#self.adapter_agent.unregister_for_inter_adapter_messages()

# Heartbeat
self._heartbeat.stop()
self._heartbeat.enabled = False

# OMCI Communications
self._unsubscribe_to_events()
self._openomci.enabled = False

# Port shutdown
for port in self.uni_ports:
port.enabled = False

if self._pon is not None:
self._pon.enabled = False
self._openomci.enabled = False

def receive_message(self, msg):
if self.enabled:
Expand Down Expand Up @@ -335,8 +335,8 @@ def update_flow_table(self, flows):
if flow_entry.flow_id in self._flows:
valid_flows.add(flow_entry.flow_id)

if flow_entry is None or flow_entry.flow_direction not in {FlowEntry.upstream_flow_types,
FlowEntry.downstream_flow_types}:
if flow_entry is None or flow_entry.flow_direction not in \
FlowEntry.upstream_flow_types | FlowEntry.downstream_flow_types:
continue

is_upstream = flow_entry.flow_direction in FlowEntry.upstream_flow_types
Expand Down Expand Up @@ -467,50 +467,50 @@ def self_test_device(self, device):

def disable(self):
self.log.info('disabling', device_id=self.device_id)
self.enabled = False

try:
# Get the latest device reference
device = self.adapter_agent.get_device(self.device_id)

# Disable all ports on that device
self.adapter_agent.disable_all_ports(self.device_id)
device = self.adapter_agent.get_device(self.device_id)

# Update the device operational status to UNKNOWN
device.oper_status = OperStatus.UNKNOWN
device.connect_status = ConnectStatus.UNREACHABLE
device.reason = 'Disabled'
self.adapter_agent.update_device(device)
# Disable all ports on that device
self.adapter_agent.disable_all_ports(self.device_id)

# Update the device operational status to UNKNOWN
device.oper_status = OperStatus.UNKNOWN
device.connect_status = ConnectStatus.UNREACHABLE
device.reason = 'Disabled'
self.adapter_agent.update_device(device)

# Remove the uni logical port from the OLT, if still present
parent_device = self.adapter_agent.get_device(device.parent_id)
assert parent_device
# Remove the uni logical port from the OLT, if still present
parent_device = self.adapter_agent.get_device(device.parent_id)
assert parent_device

for uni in self.uni_ports:
# port_id = 'uni-{}'.format(uni.port_number)
port_id = uni.port_id_name()
for uni in self.uni_ports:
# port_id = 'uni-{}'.format(uni.port_number)
port_id = uni.port_id_name()
try:
logical_device_id = parent_device.parent_id
assert logical_device_id
port = self.adapter_agent.get_logical_port(logical_device_id,port_id)
self.adapter_agent.delete_logical_port(logical_device_id, port)
except KeyError:
self.log.info('logical-port-not-found', device_id=self.device_id,
portid=port_id)

# Remove pon port from parent and disable
if self._pon is not None:
self.adapter_agent.delete_port_reference_from_parent(self.device_id,
self._pon.get_port())
self._pon.enabled = False

try:
#TODO: there is no logical device if olt disables first
logical_device_id = parent_device.parent_id
assert logical_device_id
port = self.adapter_agent.get_logical_port(logical_device_id,
port_id)
self.adapter_agent.delete_logical_port(logical_device_id, port)
except KeyError:
self.log.info('logical-port-not-found', device_id=self.device_id,
portid=port_id)

# Remove pon port from parent and disable
if self._pon is not None:
self.adapter_agent.delete_port_reference_from_parent(self.device_id,
self._pon.get_port())
self._pon.enabled = False
# Unregister for proxied message
self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)

# Unregister for proxied message
self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
except Exception as _e:
pass # This is expected if OLT has deleted the ONU device handler

# TODO:
# 1) Remove all flows from the device? or is it done before we are called
# And disable OMCI as well
self.enabled = False
self.log.info('disabled', device_id=device.id)

def reenable(self):
Expand Down Expand Up @@ -568,15 +568,20 @@ def reenable(self):
def delete(self):
self.log.info('deleting', device_id=self.device_id)

for uni in self._unis.values():
uni.stop()
uni.delete()
try:
for uni in self._unis.values():
uni.stop()
uni.delete()

self._pon.stop()
self._pon.delete()

self._pon.stop()
self._pon.delete()
except Exception as _e:
pass # Expected if the OLT deleted us from the device handler

# OpenOMCI cleanup
self._openomci.delete()
omci, self._openomci = self._openomci, None
omci.delete()

def add_uni_ports(self):
""" Called after in-sync achieved and not in xPON mode"""
Expand Down
3 changes: 3 additions & 0 deletions voltha/adapters/adtran_onu/flow/flow_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class FlowDirection(IntEnum):
(FlowDirection.ANI, FlowDirection.UNI): FlowDirection.DOWNSTREAM
}

upstream_flow_types = {FlowDirection.UPSTREAM}
downstream_flow_types = {FlowDirection.DOWNSTREAM}

# Well known EtherTypes
class EtherType(IntEnum):
EAPOL = 0x888E
Expand Down
Loading

0 comments on commit 8b79a9b

Please sign in to comment.