Fix spacing and merging issues in adtran_olt flow_entry
Change-Id: I1fd9350b33205fc42a9325755edef3f3e2922b0d
diff --git a/voltha/adapters/adtran_olt/flow/flow_entry.py b/voltha/adapters/adtran_olt/flow/flow_entry.py
index 6fa658e..f031064 100644
--- a/voltha/adapters/adtran_olt/flow/flow_entry.py
+++ b/voltha/adapters/adtran_olt/flow/flow_entry.py
@@ -25,10 +25,10 @@
# IP Protocol numbers
_supported_ip_protocols = [
- 1, # ICMP
- 2, # IGMP
- 6, # TCP
- 17, # UDP
+ 1, # ICMP
+ 2, # IGMP
+ 6, # TCP
+ 17, # UDP
]
@@ -277,579 +277,544 @@
log.exception('flow-entry-processing', e=e)
return None, None
-@staticmethod
-def _create_evc_and_maps(evc, downstream_flow, upstream_flows):
- """
- Give a set of flows, find (or create) the EVC and any needed EVC-MAPs
+ @staticmethod
+ def _create_evc_and_maps(evc, downstream_flow, upstream_flows):
+ """
+ Give a set of flows, find (or create) the EVC and any needed EVC-MAPs
- :param evc: (EVC) Existing EVC for downstream flow. May be null if not created
- :param downstream_flow: (FlowEntry) NNI -> UNI flow (provides much of the EVC values)
- :param upstream_flows: (list of FlowEntry) UNI -> NNI flows (provides much of the EVC-MAP values)
+ :param evc: (EVC) Existing EVC for downstream flow. May be null if not created
+ :param downstream_flow: (FlowEntry) NNI -> UNI flow (provides much of the EVC values)
+ :param upstream_flows: (list of FlowEntry) UNI -> NNI flows (provides much of the EVC-MAP values)
- :return: EVC object
- """
- log.debug('flow-evc-and-maps', downstream_flow=downstream_flow,
- upstream_flows=upstream_flows)
-
- if (evc is None and downstream_flow is None) or upstream_flows is None:
- return None
-
- # Get any existing EVC if a flow is already created
- if downstream_flow.evc is None:
- if evc is not None:
- downstream_flow.evc = evc
-
- elif downstream_flow.is_multicast_flow:
- from mcast import MCastEVC
- downstream_flow.evc = MCastEVC.create(downstream_flow)
-
- elif downstream_flow.is_acl_flow:
- downstream_flow.evc = downstream_flow.get_utility_evc()
- else:
- downstream_flow.evc = EVC(downstream_flow)
-
- if not downstream_flow.evc.valid:
- log.debug('flow-evc-and-maps-downstream-invalid',
- downstream_flow=downstream_flow,
+ :return: EVC object
+ """
+ log.debug('flow-evc-and-maps', downstream_flow=downstream_flow,
upstream_flows=upstream_flows)
- return None
- # Create EVC-MAPs. Note upstream_flows is empty list for multicast
- # For Packet In/Out support. The upstream flows for will have matching
- # signatures. So the first one to get created should create the EVC and
- # if it needs and ACL, do so then. The second one should just reference
- # the first map.
- #
- # If the second has and ACL, then it should add it to the map.
- # TODO: What to do if the second (or third, ...) is the data one.
- # What should it do then?
- sig_map_map = {f.signature: f.evc_map for f in upstream_flows
- if f.evc_map is not None}
+ if (evc is None and downstream_flow is None) or upstream_flows is None:
+ return None
- for flow in upstream_flows:
- if flow.evc_map is None:
- if flow.signature in sig_map_map:
- # Found an explicitly matching existing EVC-MAP. Add flow to this EVC-MAP
- flow.evc_map = sig_map_map[flow.signature].add_flow(flow, downstream_flow.evc)
+ # Get any existing EVC if a flow is already created
+ if downstream_flow.evc is None:
+ if evc is not None:
+ downstream_flow.evc = evc
+
+ elif downstream_flow.is_multicast_flow:
+ from mcast import MCastEVC
+ downstream_flow.evc = MCastEVC.create(downstream_flow)
+
+ elif downstream_flow.is_acl_flow:
+ downstream_flow.evc = downstream_flow.get_utility_evc()
else:
- # May need to create a MAP or search for an existing ACL/user EVC-Map
- # upstream_flow_table = _existing_upstream_flow_entries[flow.device_id]
- upstream_flow_table = flow.handler.upstream_flows
- existing_flow = EVCMap.find_matching_ingress_flow(flow, upstream_flow_table)
+ downstream_flow.evc = EVC(downstream_flow)
- if existing_flow is None:
- flow.evc_map = EVCMap.create_ingress_map(flow, downstream_flow.evc)
- else:
- flow.evc_map = existing_flow.add_flow(flow, downstream_flow.evc)
+ if not downstream_flow.evc.valid:
+ log.debug('flow-evc-and-maps-downstream-invalid',
+ downstream_flow=downstream_flow,
+ upstream_flows=upstream_flows)
+ return None
- all_maps_valid = all(flow.evc_map.valid for flow in upstream_flows) \
- or downstream_flow.is_multicast_flow
-
- log.debug('flow-evc-and-maps-downstream',
- downstream_flow=downstream_flow,
- upstream_flows=upstream_flows, all_valid=all_maps_valid)
-
- return downstream_flow.evc if all_maps_valid else None
-
-def get_utility_evc(self, use_default_vlan_id=False):
- assert self.is_acl_flow, 'Utility evcs are for acl flows only'
- return UtilityEVC.create(self, use_default_vlan_id)
-
-@property
-def _needs_acl_support(self):
- if self.ipv4_dst is not None: # In case MCAST downstream has ACL on it
- return False
-
- return self.eth_type is not None or self.ip_protocol is not None or\
- self.ipv4_dst is not None or self.udp_dst is not None or self.udp_src is not None
-
-@property
-def signature(self):
- if self._signature is None:
- # These are not exact, just ones that may be put together to make an EVC. The
- # basic rules are:
+ # Create EVC-MAPs. Note upstream_flows is empty list for multicast
+ # For Packet In/Out support. The upstream flows for will have matching
+ # signatures. So the first one to get created should create the EVC and
+ # if it needs and ACL, do so then. The second one should just reference
+ # the first map.
#
- # 1 - Port numbers in increasing order
- ports = sorted(filter(None, [self.in_port, self.output]))
- assert len(ports) == 2, 'Invalid port count: {}'.format(len(ports))
+ # If the second has and ACL, then it should add it to the map.
+ # TODO: What to do if the second (or third, ...) is the data one.
+ # What should it do then?
+ sig_map_map = {f.signature: f.evc_map for f in upstream_flows
+ if f.evc_map is not None}
- # 3 - The outer VID
- # 4 - The inner VID. Wildcard if downstream
- if self.push_vlan_id is None:
- outer = self.vlan_id
- inner = self.inner_vid
- else:
- outer = self.push_vlan_id
- inner = self.vlan_id
+ for flow in upstream_flows:
+ if flow.evc_map is None:
+ if flow.signature in sig_map_map:
+ # Found an explicitly matching existing EVC-MAP. Add flow to this EVC-MAP
+ flow.evc_map = sig_map_map[flow.signature].add_flow(flow, downstream_flow.evc)
+ else:
+ # May need to create a MAP or search for an existing ACL/user EVC-Map
+ # upstream_flow_table = _existing_upstream_flow_entries[flow.device_id]
+ upstream_flow_table = flow.handler.upstream_flows
+ existing_flow = EVCMap.find_matching_ingress_flow(flow, upstream_flow_table)
- downstream_sig = '.'.join(map(str, (
- ports[0],
- ports[1] if self.handler.is_nni_port(ports[1]) else '*',
- outer,
- '*'
- )))
+ if existing_flow is None:
+ flow.evc_map = EVCMap.create_ingress_map(flow, downstream_flow.evc)
+ else:
+ flow.evc_map = existing_flow.add_flow(flow, downstream_flow.evc)
- if self._flow_direction in FlowEntry.downstream_flow_types:
- self._signature = downstream_sig
- elif self._flow_direction in FlowEntry.upstream_flow_types:
- self._signature = '.'.join(map(str, (ports[0], ports[1], outer, inner)))
- self.downstream_signature = downstream_sig
- else:
- log.error('unsupported-flow')
- return self._signature
+ all_maps_valid = all(flow.evc_map.valid for flow in upstream_flows) \
+ or downstream_flow.is_multicast_flow
-def _decode(self, flow):
- """
- Examine flow rules and extract appropriate settings
- """
- log.debug('start-decode')
- status = self._decode_traffic_selector(flow) and self._decode_traffic_treatment(flow)
+ log.debug('flow-evc-and-maps-downstream',
+ downstream_flow=downstream_flow,
+ upstream_flows=upstream_flows, all_valid=all_maps_valid)
- # Determine direction of the flow and apply appropriate modifications
- # to the decoded flows
- if status:
- if not self._decode_flow_direction():
+ return downstream_flow.evc if all_maps_valid else None
+
+ def get_utility_evc(self, use_default_vlan_id=False):
+ assert self.is_acl_flow, 'Utility evcs are for acl flows only'
+ return UtilityEVC.create(self, use_default_vlan_id)
+
+ @property
+ def _needs_acl_support(self):
+ if self.ipv4_dst is not None: # In case MCAST downstream has ACL on it
return False
- if self._flow_direction in FlowEntry.downstream_flow_types:
- status = self._apply_downstream_mods()
+ return self.eth_type is not None or self.ip_protocol is not None or\
+ self.ipv4_dst is not None or self.udp_dst is not None or self.udp_src is not None
- elif self._flow_direction in FlowEntry.upstream_flow_types:
- status = self._apply_upstream_mods()
+ @property
+ def signature(self):
+ if self._signature is None:
+ # These are not exact, just ones that may be put together to make an EVC. The
+ # basic rules are:
+ #
+ # 1 - Port numbers in increasing order
+ ports = sorted(filter(None, [self.in_port, self.output]))
+ assert len(ports) == 2, 'Invalid port count: {}'.format(len(ports))
- else:
- # TODO: Need to code this - Perhaps this is an NNI_PON for Multicast support?
- log.error('unsupported-flow-direction')
- status = False
-
- log.debug('flow-evc-decode', direction=self._flow_direction, is_acl=self._is_acl_flow,
- inner_vid=self.inner_vid, vlan_id=self.vlan_id, pop_vlan=self.pop_vlan,
- push_vid=self.push_vlan_id, status=status)
-
- # Create a signature that will help locate related flow entries on a device.
- if status:
- # These are not exact, just ones that may be put together to make an EVC. The
- # basic rules are:
- #
- # 1 - Port numbers in increasing order
- ports = [self.in_port, self.output]
- ports.sort()
- assert len(ports) == 2, 'Invalid port count: {}'.format(len(ports))
-
- # 3 - The outer VID
- # 4 - The inner VID. Wildcard if downstream
- if self.push_vlan_id is None:
- outer = self.vlan_id
- inner = self.inner_vid
- else:
- outer = self.push_vlan_id
- inner = self.vlan_id
-
- upstream_sig = '{}'.format(ports[0])
- downstream_sig = '{}'.format(ports[0])
- upstream_sig += '.{}'.format(ports[1])
- downstream_sig += '.{}'.format(ports[1] if self.handler.is_nni_port(ports[1]) else '*')
-
- upstream_sig += '.{}.{}'.format(outer, inner)
- downstream_sig += '.{}.*'.format(outer)
-
- if self._flow_direction in FlowEntry.downstream_flow_types:
- self.signature = downstream_sig
-
- elif self._flow_direction in FlowEntry.upstream_flow_types:
- self.signature = upstream_sig
- self.downstream_signature = downstream_sig
-
- else:
- log.error('unsupported-flow')
- status = False
-
- log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
- return status
-
-def _decode_traffic_selector(self, flow):
- """
- Extract EVC related traffic selection settings
- """
- self.in_port = fd.get_in_port(flow)
-
- if self.in_port > OFPP_MAX:
- log.warn('logical-input-ports-not-supported', in_port=self.in_port)
- return False
-
- for field in fd.get_ofb_fields(flow):
- if field.type == IN_PORT:
- if self._handler.is_nni_port(self.in_port) or self._handler.is_uni_port(self.in_port):
- self._logical_port = self.in_port
-
- elif field.type == VLAN_VID:
- if field.vlan_vid >= OFPVID_PRESENT + 4095:
- self.vlan_id = None # pre-ONOS v1.13.5 or old EAPOL Rule
+ # 3 - The outer VID
+ # 4 - The inner VID. Wildcard if downstream
+ if self.push_vlan_id is None:
+ outer = self.vlan_id
+ inner = self.inner_vid
else:
- self.vlan_id = field.vlan_vid & 0xfff
+ outer = self.push_vlan_id
+ inner = self.vlan_id
- log.debug('*** field.type == VLAN_VID', value=field.vlan_vid, vlan_id=self.vlan_id)
+ downstream_sig = '.'.join(map(str, (
+ ports[0],
+ ports[1] if self.handler.is_nni_port(ports[1]) else '*',
+ outer,
+ '*'
+ )))
- elif field.type == VLAN_PCP:
- log.debug('*** field.type == VLAN_PCP', value=field.vlan_pcp)
- self.pcp = field.vlan_pcp
+ if self._flow_direction in FlowEntry.downstream_flow_types:
+ self._signature = downstream_sig
+ elif self._flow_direction in FlowEntry.upstream_flow_types:
+ self._signature = '.'.join(map(str, (ports[0], ports[1], outer, inner)))
+ self.downstream_signature = downstream_sig
+ else:
+ log.error('unsupported-flow')
+ return self._signature
- elif field.type == ETH_TYPE:
- log.debug('*** field.type == ETH_TYPE', value=field.eth_type)
- self.eth_type = field.eth_type
+ def _decode(self, flow):
+ """
+ Examine flow rules and extract appropriate settings
+ """
+ log.debug('start-decode')
+ status = self._decode_traffic_selector(flow) and self._decode_traffic_treatment(flow)
- elif field.type == IP_PROTO:
- log.debug('*** field.type == IP_PROTO', value=field.ip_proto)
- self.ip_protocol = field.ip_proto
-
- if self.ip_protocol not in _supported_ip_protocols:
- log.error('Unsupported IP Protocol', protocol=self.ip_protocol)
+ # Determine direction of the flow and apply appropriate modifications
+ # to the decoded flows
+ if status:
+ if not self._decode_flow_direction():
return False
- elif field.type == IPV4_DST:
- log.debug('*** field.type == IPV4_DST', value=field.ipv4_dst)
- self.ipv4_dst = field.ipv4_dst
+ if self._flow_direction in FlowEntry.downstream_flow_types:
+ status = self._apply_downstream_mods()
- elif field.type == UDP_DST:
- log.debug('*** field.type == UDP_DST', value=field.udp_dst)
- self.udp_dst = field.udp_dst
+ elif self._flow_direction in FlowEntry.upstream_flow_types:
+ status = self._apply_upstream_mods()
- elif field.type == UDP_SRC:
- log.debug('*** field.type == UDP_SRC', value=field.udp_src)
- self.udp_src = field.udp_src
-
- elif field.type == METADATA:
- if self._handler.is_nni_port(self.in_port):
- # Downstream flow
- log.debug('*** field.type == METADATA', value=field.table_metadata)
-
- if field.table_metadata > 4095:
- # ONOS v1.13.5 or later. c-vid in upper 32-bits
- vid = field.table_metadata & 0x0FFF
- if vid > 0:
- self.inner_vid = vid # CTag is never '0'
-
- elif field.table_metadata > 0:
- # Pre-ONOS v1.13.5 (vid without the 4096 offset)
- self.inner_vid = field.table_metadata
-
else:
- # Upstream flow
- pass # Not used upstream at this time
+ # TODO: Need to code this - Perhaps this is an NNI_PON for Multicast support?
+ log.error('unsupported-flow-direction')
+ status = False
- log.debug('*** field.type == METADATA', value=field.table_metadata,
- inner_vid=self.inner_vid)
- else:
- log.warn('unsupported-selection-field', type=field.type)
- self._status_message = 'Unsupported field.type={}'.format(field.type)
+ log.debug('flow-evc-decode', direction=self._flow_direction, is_acl=self._is_acl_flow,
+ inner_vid=self.inner_vid, vlan_id=self.vlan_id, pop_vlan=self.pop_vlan,
+ push_vid=self.push_vlan_id, status=status)
+
+ # Create a signature that will help locate related flow entries on a device.
+ if status:
+ status = self.signature is not None
+ log.debug('flow-evc-decode', upstream_sig=self.signature, downstream_sig=self.downstream_signature)
+ return status
+
+ def _decode_traffic_selector(self, flow):
+ """
+ Extract EVC related traffic selection settings
+ """
+ self.in_port = fd.get_in_port(flow)
+
+ if self.in_port > OFPP_MAX:
+ log.warn('logical-input-ports-not-supported', in_port=self.in_port)
return False
- return True
+ for field in fd.get_ofb_fields(flow):
+ if field.type == IN_PORT:
+ if self._handler.is_nni_port(self.in_port) or self._handler.is_uni_port(self.in_port):
+ self._logical_port = self.in_port
-def _decode_traffic_treatment(self, flow):
- # Loop through traffic treatment
- for act in fd.get_actions(flow):
- if act.type == fd.OUTPUT:
- self.output = act.output.port
+ elif field.type == VLAN_VID:
+ if field.vlan_vid >= OFPVID_PRESENT + 4095:
+ self.vlan_id = None # pre-ONOS v1.13.5 or old EAPOL Rule
+ else:
+ self.vlan_id = field.vlan_vid & 0xfff
- elif act.type == POP_VLAN:
- log.debug('*** action.type == POP_VLAN')
- self.pop_vlan = True
+ log.debug('*** field.type == VLAN_VID', value=field.vlan_vid, vlan_id=self.vlan_id)
- elif act.type == PUSH_VLAN:
- log.debug('*** action.type == PUSH_VLAN', value=act.push)
- tpid = act.push.ethertype
- self.push_vlan_tpid = tpid
+ elif field.type == VLAN_PCP:
+ log.debug('*** field.type == VLAN_PCP', value=field.vlan_pcp)
+ self.pcp = field.vlan_pcp
- elif act.type == SET_FIELD:
- log.debug('*** action.type == SET_FIELD', value=act.set_field.field)
- assert (act.set_field.field.oxm_class == OFPXMC_OPENFLOW_BASIC)
- field = act.set_field.field.ofb_field
+ elif field.type == ETH_TYPE:
+ log.debug('*** field.type == ETH_TYPE', value=field.eth_type)
+ self.eth_type = field.eth_type
- if field.type == VLAN_VID:
- self.push_vlan_id = field.vlan_vid & 0xfff
+ elif field.type == IP_PROTO:
+ log.debug('*** field.type == IP_PROTO', value=field.ip_proto)
+ self.ip_protocol = field.ip_proto
+
+ if self.ip_protocol not in _supported_ip_protocols:
+ log.error('Unsupported IP Protocol', protocol=self.ip_protocol)
+ return False
+
+ elif field.type == IPV4_DST:
+ log.debug('*** field.type == IPV4_DST', value=field.ipv4_dst)
+ self.ipv4_dst = field.ipv4_dst
+
+ elif field.type == UDP_DST:
+ log.debug('*** field.type == UDP_DST', value=field.udp_dst)
+ self.udp_dst = field.udp_dst
+
+ elif field.type == UDP_SRC:
+ log.debug('*** field.type == UDP_SRC', value=field.udp_src)
+ self.udp_src = field.udp_src
+
+ elif field.type == METADATA:
+ if self._handler.is_nni_port(self.in_port):
+ # Downstream flow
+ log.debug('*** field.type == METADATA', value=field.table_metadata)
+
+ if field.table_metadata > 4095:
+ # ONOS v1.13.5 or later. c-vid in upper 32-bits
+ vid = field.table_metadata & 0x0FFF
+ if vid > 0:
+ self.inner_vid = vid # CTag is never '0'
+
+ elif field.table_metadata > 0:
+ # Pre-ONOS v1.13.5 (vid without the 4096 offset)
+ self.inner_vid = field.table_metadata
+
+ else:
+ # Upstream flow
+ pass # Not used upstream at this time
+
+ log.debug('*** field.type == METADATA', value=field.table_metadata,
+ inner_vid=self.inner_vid)
else:
- log.debug('unsupported-set-field')
- else:
- log.warn('unsupported-action', action=act)
- self._status_message = 'Unsupported action.type={}'.format(act.type)
- return False
-
- return True
-
-def _decode_flow_direction(self):
- # Determine direction of the flow
- def port_type(port_number):
- if port_number in self._handler.northbound_ports:
- return FlowEntry.PortType.NNI
-
- elif port_number in self._handler.southbound_ports:
- return FlowEntry.PortType.PON
-
- elif port_number <= OFPP_MAX:
- return FlowEntry.PortType.UNI
-
- elif port_number in {OFPP_CONTROLLER, 0xFFFFFFFD}: # OFPP_CONTROLLER is wrong in proto-file
- return FlowEntry.PortType.CONTROLLER
-
- return FlowEntry.PortType.OTHER
-
- flow_dir_map = {
- (FlowEntry.PortType.UNI, FlowEntry.PortType.NNI): FlowEntry.FlowDirection.UPSTREAM,
- (FlowEntry.PortType.NNI, FlowEntry.PortType.UNI): FlowEntry.FlowDirection.DOWNSTREAM,
- (FlowEntry.PortType.UNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_UNI,
- (FlowEntry.PortType.NNI, FlowEntry.PortType.PON): FlowEntry.FlowDirection.NNI_PON,
- # The following are not yet supported
- # (FlowEntry.PortType.NNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_NNI,
- # (FlowEntry.PortType.PON, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_PON,
- # (FlowEntry.PortType.NNI, FlowEntry.PortType.NNI): FlowEntry.FlowDirection.NNI_NNI,
- # (FlowEntry.PortType.UNI, FlowEntry.PortType.UNI): FlowEntry.FlowDirection.UNI_UNI,
- }
- self._flow_direction = flow_dir_map.get((port_type(self.in_port), port_type(self.output)),
- FlowEntry.FlowDirection.OTHER)
- return self._flow_direction != FlowEntry.FlowDirection.OTHER
-
-def _apply_downstream_mods(self):
- # This is a downstream flow. It could be any one of the following:
- #
- # Legacy control VLAN:
- # This is the old VLAN 4000 that was used to attach EAPOL and other
- # controller flows to. Eventually these will change to CONTROLLER_UNI
- # flows. For these, use the 'utility' VLAN instead so 4000 if available
- # for other uses (AT&T uses it for downstream multicast video).
- #
- # Multicast VLAN:
- # This is downstream multicast data.
- # TODO: Test this to see if this needs to be in a separate NNI_PON mod-method
- #
- # User Data flow:
- # This is for user data. Eventually we may need to support ACLs?
- #
- # May be for to controller flow downstream (no ethType)
- 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
- self._is_acl_flow = True
-
- else:
- # Currently do not support ACLs on user data flows downstream
- assert not self._needs_acl_support # User data, no special modifications needed at this time
-
- return True
-
-def _apply_upstream_mods(self):
- #
- # This is an upstream flow. It could be any of the following
- #
- # ACL/Packet capture:
- # This is either a legacy (FlowDirection.UPSTREAM) or a new one
- # that specifies an output port of controller (FlowDirection.CONTROLLER_UNI).
- # Either way, these need to be placed on the Utility VLAN if the ONU attached
- # does not have a user-data flow (C-Tag). If there is a C-Tag available,
- # then place it on that VLAN.
- #
- # Once a user-data flow is established, move any of the ONUs ACL flows
- # over to that VLAN (this is handled elsewhere).
- #
- # User Data flows:
- # No special modifications are needed
- #
- try:
- # Do not handle PON level ACLs in this method
- assert(self._flow_direction != FlowEntry.FlowDirection.CONTROLLER_PON)
-
- # Is this a legacy (VLAN 4000) upstream to-controller flow
- if self._needs_acl_support and FlowEntry.LEGACY_CONTROL_VLAN == self.push_vlan_id:
- self._flow_direction = FlowEntry.FlowDirection.CONTROLLER_UNI
- self._is_acl_flow = True
- self.push_vlan_id = self.handler.utility_vlan
+ log.warn('unsupported-selection-field', type=field.type)
+ self._status_message = 'Unsupported field.type={}'.format(field.type)
+ return False
return True
- except Exception as e:
- # TODO: Need to support flow retry if the ONU is not yet activated !!!!
- log.exception('tag-fixup', e=e)
- return False
+ def _decode_traffic_treatment(self, flow):
+ # Loop through traffic treatment
+ for act in fd.get_actions(flow):
+ if act.type == fd.OUTPUT:
+ self.output = act.output.port
-@staticmethod
-def drop_missing_flows(handler, valid_flow_ids):
- dl = []
- try:
- flow_table = handler.upstream_flows
- flows_to_drop = [flow for flow_id, flow in flow_table.items()
- if flow_id not in valid_flow_ids]
- dl.extend([flow.remove() for flow in flows_to_drop])
+ elif act.type == POP_VLAN:
+ log.debug('*** action.type == POP_VLAN')
+ self.pop_vlan = True
- for sig_table in handler.downstream_flows.itervalues():
- flows_to_drop = [flow for flow_id, flow in sig_table.flows.items()
- if isinstance(flow, FlowEntry) and flow_id not in valid_flow_ids]
+ elif act.type == PUSH_VLAN:
+ log.debug('*** action.type == PUSH_VLAN', value=act.push)
+ tpid = act.push.ethertype
+ self.push_vlan_tpid = tpid
+
+ elif act.type == SET_FIELD:
+ log.debug('*** action.type == SET_FIELD', value=act.set_field.field)
+ assert (act.set_field.field.oxm_class == OFPXMC_OPENFLOW_BASIC)
+ field = act.set_field.field.ofb_field
+
+ if field.type == VLAN_VID:
+ self.push_vlan_id = field.vlan_vid & 0xfff
+ else:
+ log.debug('unsupported-set-field')
+ else:
+ log.warn('unsupported-action', action=act)
+ self._status_message = 'Unsupported action.type={}'.format(act.type)
+ return False
+
+ return True
+
+ def _decode_flow_direction(self):
+ # Determine direction of the flow
+ def port_type(port_number):
+ if port_number in self._handler.northbound_ports:
+ return FlowEntry.PortType.NNI
+
+ elif port_number in self._handler.southbound_ports:
+ return FlowEntry.PortType.PON
+
+ elif port_number <= OFPP_MAX:
+ return FlowEntry.PortType.UNI
+
+ elif port_number in {OFPP_CONTROLLER, 0xFFFFFFFD}: # OFPP_CONTROLLER is wrong in proto-file
+ return FlowEntry.PortType.CONTROLLER
+
+ return FlowEntry.PortType.OTHER
+
+ flow_dir_map = {
+ (FlowEntry.PortType.UNI, FlowEntry.PortType.NNI): FlowEntry.FlowDirection.UPSTREAM,
+ (FlowEntry.PortType.NNI, FlowEntry.PortType.UNI): FlowEntry.FlowDirection.DOWNSTREAM,
+ (FlowEntry.PortType.UNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_UNI,
+ (FlowEntry.PortType.NNI, FlowEntry.PortType.PON): FlowEntry.FlowDirection.NNI_PON,
+ # The following are not yet supported
+ # (FlowEntry.PortType.NNI, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_NNI,
+ # (FlowEntry.PortType.PON, FlowEntry.PortType.CONTROLLER): FlowEntry.FlowDirection.CONTROLLER_PON,
+ # (FlowEntry.PortType.NNI, FlowEntry.PortType.NNI): FlowEntry.FlowDirection.NNI_NNI,
+ # (FlowEntry.PortType.UNI, FlowEntry.PortType.UNI): FlowEntry.FlowDirection.UNI_UNI,
+ }
+ self._flow_direction = flow_dir_map.get((port_type(self.in_port), port_type(self.output)),
+ FlowEntry.FlowDirection.OTHER)
+ return self._flow_direction != FlowEntry.FlowDirection.OTHER
+
+ def _apply_downstream_mods(self):
+ # This is a downstream flow. It could be any one of the following:
+ #
+ # Legacy control VLAN:
+ # This is the old VLAN 4000 that was used to attach EAPOL and other
+ # controller flows to. Eventually these will change to CONTROLLER_UNI
+ # flows. For these, use the 'utility' VLAN instead so 4000 if available
+ # for other uses (AT&T uses it for downstream multicast video).
+ #
+ # Multicast VLAN:
+ # This is downstream multicast data.
+ # TODO: Test this to see if this needs to be in a separate NNI_PON mod-method
+ #
+ # User Data flow:
+ # This is for user data. Eventually we may need to support ACLs?
+ #
+ # May be for to controller flow downstream (no ethType)
+ 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
+ self._is_acl_flow = True
+
+ else:
+ # Currently do not support ACLs on user data flows downstream
+ assert not self._needs_acl_support # User data, no special modifications needed at this time
+
+ return True
+
+ def _apply_upstream_mods(self):
+ #
+ # This is an upstream flow. It could be any of the following
+ #
+ # ACL/Packet capture:
+ # This is either a legacy (FlowDirection.UPSTREAM) or a new one
+ # that specifies an output port of controller (FlowDirection.CONTROLLER_UNI).
+ # Either way, these need to be placed on the Utility VLAN if the ONU attached
+ # does not have a user-data flow (C-Tag). If there is a C-Tag available,
+ # then place it on that VLAN.
+ #
+ # Once a user-data flow is established, move any of the ONUs ACL flows
+ # over to that VLAN (this is handled elsewhere).
+ #
+ # User Data flows:
+ # No special modifications are needed
+ #
+ try:
+ # Do not handle PON level ACLs in this method
+ assert(self._flow_direction != FlowEntry.FlowDirection.CONTROLLER_PON)
+
+ # Is this a legacy (VLAN 4000) upstream to-controller flow
+ if self._needs_acl_support and FlowEntry.LEGACY_CONTROL_VLAN == self.push_vlan_id:
+ self._flow_direction = FlowEntry.FlowDirection.CONTROLLER_UNI
+ self._is_acl_flow = True
+ self.push_vlan_id = self.handler.utility_vlan
+
+ return True
+
+ except Exception as e:
+ # TODO: Need to support flow retry if the ONU is not yet activated !!!!
+ log.exception('tag-fixup', e=e)
+ return False
+
+ @staticmethod
+ def drop_missing_flows(handler, valid_flow_ids):
+ dl = []
+ try:
+ flow_table = handler.upstream_flows
+ flows_to_drop = [flow for flow_id, flow in flow_table.items()
+ if flow_id not in valid_flow_ids]
dl.extend([flow.remove() for flow in flows_to_drop])
- except Exception as _e:
- pass
+ for sig_table in handler.downstream_flows.itervalues():
+ flows_to_drop = [flow for flow_id, flow in sig_table.flows.items()
+ if isinstance(flow, FlowEntry) and flow_id not in valid_flow_ids]
+ dl.extend([flow.remove() for flow in flows_to_drop])
- return gatherResults(dl, consumeErrors=True) if len(dl) > 0 else returnValue('no-flows-to-drop')
+ except Exception as _e:
+ pass
-@inlineCallbacks
-def remove(self):
- """
- Remove this flow entry from the list of existing entries and drop EVC
- if needed
- """
- # Remove from exiting table list
- flow_id = self.flow_id
- flow_table = None
+ return gatherResults(dl, consumeErrors=True) if len(dl) > 0 else returnValue('no-flows-to-drop')
- if self.flow_direction in FlowEntry.upstream_flow_types:
- flow_table = self._handler.upstream_flows
+ @inlineCallbacks
+ def remove(self):
+ """
+ Remove this flow entry from the list of existing entries and drop EVC
+ if needed
+ """
+ # Remove from exiting table list
+ flow_id = self.flow_id
+ flow_table = None
- elif self.flow_direction in FlowEntry.downstream_flow_types:
- sig_table = self._handler.downstream_flows.get(self.signature)
- flow_table = sig_table.flows if sig_table is not None else None
+ if self.flow_direction in FlowEntry.upstream_flow_types:
+ flow_table = self._handler.upstream_flows
- if flow_table is None or flow_id not in flow_table.keys():
- returnValue('NOP')
+ elif self.flow_direction in FlowEntry.downstream_flow_types:
+ sig_table = self._handler.downstream_flows.get(self.signature)
+ flow_table = sig_table.flows if sig_table is not None else None
- # Remove from flow table and clean up flow table if empty
- flow_table.remove(flow_id)
- evc_map, self.evc_map = self.evc_map, None
- evc = None
+ if flow_table is None or flow_id not in flow_table.keys():
+ returnValue('NOP')
- if self.flow_direction in FlowEntry.downstream_flow_types:
- sig_table = self._handler.downstream_flows.get(self.signature)
- if len(flow_table) == 0: # Only 'evc' entry present
- evc = sig_table.evc
- else:
- assert sig_table.evc is not None, 'EVC flow re-assignment error'
+ # Remove from flow table and clean up flow table if empty
+ flow_table.remove(flow_id)
+ evc_map, self.evc_map = self.evc_map, None
+ evc = None
- # Remove flow from the hardware
- try:
- dl = []
- if evc_map is not None:
- dl.append(evc_map.delete(self))
+ if self.flow_direction in FlowEntry.downstream_flow_types:
+ sig_table = self._handler.downstream_flows.get(self.signature)
+ if len(flow_table) == 0: # Only 'evc' entry present
+ evc = sig_table.evc
+ else:
+ assert sig_table.evc is not None, 'EVC flow re-assignment error'
- if evc is not None:
- dl.append(evc.delete())
+ # Remove flow from the hardware
+ try:
+ dl = []
+ if evc_map is not None:
+ dl.append(evc_map.delete(self))
- yield gatherResults(dl, consumeErrors=True)
-
- except Exception as e:
- log.exception('removal', e=e)
-
- if self.flow_direction in FlowEntry.downstream_flow_types:
- # If this flow owns the EVC, assign it to a remaining flow
- sig_table = self._handler.downstream_flows.get(self.signature)
- flow_evc = sig_table.evc
-
- if flow_evc is not None and flow_evc.flow_entry is not None and flow_id == flow_evc.flow_entry.flow_id:
- flow_evc.flow_entry = next((_flow for _flow in flow_table.itervalues()
- if isinstance(_flow, FlowEntry)
- and _flow.flow_id != flow_id), None)
-
- # If evc was deleted, remove the signature table since now flows exist with
- # that signature
- if evc is not None:
- self._handler.downstream_flows.remove(self.signature)
-
- self.evc = None
- returnValue('Done')
-
-@staticmethod
-def find_evc_map_flows(onu):
- """
- For a given OLT, find all the EVC Maps for a specific ONU
- :param onu: (Onu) onu
- :return: (list) of matching flows
- """
- # EVCs are only in the downstream table, EVC Maps are in upstream
- onu_ports = onu.uni_ports
-
- all_flow_entries = onu.olt.upstream_flows
- evc_maps = [flow_entry.evc_map for flow_entry in all_flow_entries.itervalues()
- if flow_entry.in_port in onu_ports
- and flow_entry.evc_map is not None
- and flow_entry.evc_map.valid]
-
- return evc_maps
-
-@staticmethod
-def sync_flows_by_onu(onu, reflow=False):
- """
- Check status of all flows on a per-ONU basis. Called when values
- within the ONU are modified that may affect traffic.
-
- :param onu: (Onu) ONU to examine
- :param reflow: (boolean) Flag, if True, requests that the flow be sent to
- hardware even if the values in hardware are
- consistent with the current flow settings
- """
- evc_maps = FlowEntry.find_evc_map_flows(onu)
- evcs = {}
-
- for evc_map in evc_maps:
- if reflow or evc_map.reflow_needed():
- evc_map.needs_update = False
-
- if not evc_map.installed:
- evc = evc_map.evc
if evc is not None:
- evcs[evc.name] = evc
+ dl.append(evc.delete())
- for evc in evcs.itervalues():
- evc.installed = False
- evc.schedule_install(delay=2)
+ yield gatherResults(dl, consumeErrors=True)
-######################################################
-# Bulk operations
+ except Exception as e:
+ log.exception('removal', e=e)
-@staticmethod
-def clear_all(handler):
- """
- Remove all flows for the device.
+ if self.flow_direction in FlowEntry.downstream_flow_types:
+ # If this flow owns the EVC, assign it to a remaining flow
+ sig_table = self._handler.downstream_flows.get(self.signature)
+ flow_evc = sig_table.evc
- :param handler: voltha adapter device handler
- """
- handler.downstream_flows.clear()
- handler.upstream_flows.clear()
+ if flow_evc is not None and flow_evc.flow_entry is not None and flow_id == flow_evc.flow_entry.flow_id:
+ flow_evc.flow_entry = next((_flow for _flow in flow_table.itervalues()
+ if isinstance(_flow, FlowEntry)
+ and _flow.flow_id != flow_id), None)
-@staticmethod
-def get_packetout_info(handler, logical_port):
- """
- Find parameters needed to send packet out successfully to the OLT.
+ # If evc was deleted, remove the signature table since now flows exist with
+ # that signature
+ if evc is not None:
+ self._handler.downstream_flows.remove(self.signature)
- :param handler: voltha adapter device handler
- :param logical_port: (int) logical port number for packet to go out.
+ self.evc = None
+ returnValue('Done')
- :return: physical port number, ctag, stag, evcmap name
- """
- from ..onu import Onu
+ @staticmethod
+ def find_evc_map_flows(onu):
+ """
+ For a given OLT, find all the EVC Maps for a specific ONU
+ :param onu: (Onu) onu
+ :return: (list) of matching flows
+ """
+ # EVCs are only in the downstream table, EVC Maps are in upstream
+ onu_ports = onu.uni_ports
- for flow_entry in handler.upstream_flows.itervalues():
- log.debug('get-packetout-info', flow_entry=flow_entry)
+ all_flow_entries = onu.olt.upstream_flows
+ evc_maps = [flow_entry.evc_map for flow_entry in all_flow_entries.itervalues()
+ if flow_entry.in_port in onu_ports
+ and flow_entry.evc_map is not None
+ and flow_entry.evc_map.valid]
- # match logical port
- if flow_entry.evc_map is not None and flow_entry.evc_map.valid and \
- flow_entry.logical_port == logical_port:
- evc_map = flow_entry.evc_map
- gem_ids_and_vid = evc_map.gem_ids_and_vid
+ return evc_maps
- # must have valid gem id
- if len(gem_ids_and_vid) > 0:
- for onu_id, gem_ids_with_vid in gem_ids_and_vid.iteritems():
- log.debug('get-packetout-info', onu_id=onu_id,
- gem_ids_with_vid=gem_ids_with_vid)
- 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 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
+ @staticmethod
+ def sync_flows_by_onu(onu, reflow=False):
+ """
+ Check status of all flows on a per-ONU basis. Called when values
+ within the ONU are modified that may affect traffic.
+
+ :param onu: (Onu) ONU to examine
+ :param reflow: (boolean) Flag, if True, requests that the flow be sent to
+ hardware even if the values in hardware are
+ consistent with the current flow settings
+ """
+ evc_maps = FlowEntry.find_evc_map_flows(onu)
+ evcs = {}
+
+ for evc_map in evc_maps:
+ if reflow or evc_map.reflow_needed():
+ evc_map.needs_update = False
+
+ if not evc_map.installed:
+ evc = evc_map.evc
+ if evc is not None:
+ evcs[evc.name] = evc
+
+ for evc in evcs.itervalues():
+ evc.installed = False
+ evc.schedule_install(delay=2)
+
+ ######################################################
+ # Bulk operations
+
+ @staticmethod
+ def clear_all(handler):
+ """
+ Remove all flows for the device.
+
+ :param handler: voltha adapter device handler
+ """
+ handler.downstream_flows.clear()
+ handler.upstream_flows.clear()
+
+ @staticmethod
+ def get_packetout_info(handler, logical_port):
+ """
+ Find parameters needed to send packet out successfully to the OLT.
+
+ :param handler: voltha adapter device handler
+ :param logical_port: (int) logical port number for packet to go out.
+
+ :return: physical port number, ctag, stag, evcmap name
+ """
+ from ..onu import Onu
+
+ for flow_entry in handler.upstream_flows.itervalues():
+ log.debug('get-packetout-info', flow_entry=flow_entry)
+
+ # match logical port
+ if flow_entry.evc_map is not None and flow_entry.evc_map.valid and \
+ flow_entry.logical_port == logical_port:
+ evc_map = flow_entry.evc_map
+ gem_ids_and_vid = evc_map.gem_ids_and_vid
+
+ # must have valid gem id
+ if len(gem_ids_and_vid) > 0:
+ for onu_id, gem_ids_with_vid in gem_ids_and_vid.iteritems():
+ log.debug('get-packetout-info', onu_id=onu_id,
+ gem_ids_with_vid=gem_ids_with_vid)
+ 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 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