| # |
| # Copyright 2018 the original author or authors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| import structlog |
| import threading |
| import grpc |
| import collections |
| import time |
| |
| from twisted.internet import reactor |
| |
| from voltha.protos.device_pb2 import Port, Device |
| from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus |
| from voltha.protos.logical_device_pb2 import LogicalDevice |
| from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN, \ |
| OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \ |
| OFPC_FLOW_STATS, ofp_switch_features, ofp_desc, ofp_port, \ |
| OFPXMC_OPENFLOW_BASIC |
| from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice |
| from voltha.core.logical_device_agent import mac_str_to_tuple |
| from voltha.registry import registry |
| from voltha.adapters.openolt.protos import openolt_pb2_grpc, openolt_pb2 |
| from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData |
| import voltha.core.flow_decomposer as fd |
| |
| ASFVOLT_HSIA_ID = 13 # FIXME |
| |
| Onu = collections.namedtuple("Onu", ["intf_id", "onu_id"]) |
| |
| """ |
| OpenoltDevice represents an OLT. |
| """ |
| class OpenoltDevice(object): |
| |
| def __init__(self, **kwargs): |
| super(OpenoltDevice, self).__init__() |
| |
| self.adapter_agent = kwargs['adapter_agent'] |
| device = kwargs['device'] |
| self.device_id = device.id |
| self.host_and_port = device.host_and_port |
| self.log = structlog.get_logger(id=self.device_id, ip=self.host_and_port) |
| self.oper_state = 'unknown' |
| self.nni_oper_state = dict() #intf_id -> oper_state |
| self.onus = {} # Onu -> serial_number |
| self.uni_port_num = 20 # FIXME |
| |
| # Create logical device |
| ld = LogicalDevice( |
| desc=ofp_desc( |
| mfr_desc='FIXME', hw_desc='FIXME', |
| sw_desc='FIXME', serial_num='FIXME', |
| dp_desc='n/a'), |
| switch_features=ofp_switch_features( |
| n_buffers=256, n_tables=2, |
| capabilities=( |
| OFPC_FLOW_STATS | OFPC_TABLE_STATS | |
| OFPC_GROUP_STATS | OFPC_PORT_STATS)), |
| root_device_id=self.device_id) |
| # FIXME |
| ld_initialized = self.adapter_agent.create_logical_device(ld, dpid='de:ad:be:ef:fe:ed') # FIXME |
| self.logical_device_id = ld_initialized.id |
| |
| # Update device |
| device.root = True |
| device.vendor = 'Edgecore' |
| device.model = 'ASFvOLT16' |
| device.serial_number = self.host_and_port # FIXME |
| device.parent_id = self.logical_device_id |
| device.connect_status = ConnectStatus.REACHABLE |
| device.oper_status = OperStatus.ACTIVATING |
| self.adapter_agent.update_device(device) |
| |
| |
| # Initialize gRPC |
| self.channel = grpc.insecure_channel(self.host_and_port) |
| self.channel_ready_future = grpc.channel_ready_future(self.channel) |
| |
| # Start indications thread |
| self.indications_thread = threading.Thread(target=self.process_indications) |
| self.indications_thread.daemon = True |
| self.indications_thread.start() |
| |
| def process_indications(self): |
| self.channel_ready_future.result() # blocks till gRPC connection is complete |
| self.stub = openolt_pb2_grpc.OpenoltStub(self.channel) |
| self.indications = self.stub.EnableIndication(openolt_pb2.Empty()) |
| while True: |
| # get the next indication from olt |
| ind = next(self.indications) |
| self.log.debug("rx indication", indication=ind) |
| # schedule indication handlers to be run in the main event loop |
| if ind.HasField('olt_ind'): |
| reactor.callFromThread(self.olt_indication, ind.olt_ind) |
| elif ind.HasField('intf_ind'): |
| reactor.callFromThread(self.intf_indication, ind.intf_ind) |
| elif ind.HasField('intf_oper_ind'): |
| reactor.callFromThread(self.intf_oper_indication, ind.intf_oper_ind) |
| elif ind.HasField('onu_disc_ind'): |
| reactor.callFromThread(self.onu_discovery_indication, ind.onu_disc_ind) |
| elif ind.HasField('onu_ind'): |
| reactor.callFromThread(self.onu_indication, ind.onu_ind) |
| elif ind.HasField('omci_ind'): |
| reactor.callFromThread(self.omci_indication, ind.omci_ind) |
| |
| def olt_indication(self, olt_indication): |
| self.log.debug("olt indication", olt_ind=olt_indication) |
| self.set_oper_state(olt_indication.oper_state) |
| |
| def intf_indication(self, intf_indication): |
| self.log.debug("intf indication", intf_id=intf_indication.intf_id, |
| oper_state=intf_indication.oper_state) |
| |
| if intf_indication.oper_state == "up": |
| oper_status = OperStatus.ACTIVE |
| else: |
| oper_status = OperStatus.DISCOVERED |
| |
| # FIXME - If port exists, update oper state |
| self.add_port(intf_indication.intf_id, Port.PON_OLT, oper_status) |
| |
| def intf_oper_indication(self, intf_oper_indication): |
| self.log.debug("Received interface oper state change indication", intf_id=intf_oper_indication.intf_id, |
| type=intf_oper_indication.type, oper_state=intf_oper_indication.oper_state) |
| |
| if intf_oper_indication.oper_state == "up": |
| oper_state = OperStatus.ACTIVE |
| else: |
| oper_state = OperStatus.DISCOVERED |
| |
| if intf_oper_indication.type == "nni": |
| |
| # FIXME - Ignore all nni ports except nni port 0 |
| if intf_oper_indication.intf_id != 0: |
| return |
| |
| if intf_oper_indication.intf_id not in self.nni_oper_state: |
| self.nni_oper_state[intf_oper_indication.intf_id] = oper_state |
| port_no, label = self.add_port(intf_oper_indication.intf_id, Port.ETHERNET_NNI, oper_state) |
| self.log.debug("int_oper_indication", port_no=port_no, label=label) |
| self.add_logical_port(port_no) # FIXME - add oper_state |
| elif intf_oper_indication.intf_id != self.nni_oper_state: |
| # FIXME - handle subsequent NNI oper state change |
| pass |
| |
| elif intf_oper_indication.type == "pon": |
| # FIXME - handle PON oper state change |
| pass |
| |
| def onu_discovery_indication(self, onu_disc_indication): |
| self.log.debug("onu discovery indication", intf_id=onu_disc_indication.intf_id, |
| serial_number=onu_disc_indication.serial_number) |
| |
| onu_id = self.lookup_onu(serial_number=onu_disc_indication.serial_number) |
| |
| if onu_id is None: |
| onu_id = self.new_onu_id(onu_disc_indication.intf_id) |
| try: |
| self.add_onu_device( |
| onu_disc_indication.intf_id, |
| self.intf_id_to_port_no(onu_disc_indication.intf_id, Port.PON_OLT), |
| onu_id, |
| onu_disc_indication.serial_number) |
| except Exception as e: |
| self.log.exception('onu activation failed', e=e) |
| else: |
| self.activate_onu( |
| onu_disc_indication.intf_id, onu_id, |
| serial_number=onu_disc_indication.serial_number) |
| else: |
| # FIXME - handle discovery of already activated onu |
| self.log.info("onu activation in progress", |
| intf_id=onu_disc_indication.intf_id, onu_id=onu_id) |
| |
| def _get_next_uni_port(self): |
| self.uni_port_num += 1 |
| return self.uni_port_num |
| |
| def onu_indication(self, onu_indication): |
| |
| self.log.debug("onu indication", intf_id=onu_indication.intf_id, |
| onu_id=onu_indication.onu_id) |
| |
| # FIXME - handle onu_id/serial_number mismatch |
| assert onu_indication.onu_id == self.lookup_onu(serial_number=onu_indication.serial_number) |
| |
| onu_device = self.adapter_agent.get_child_device( |
| self.device_id, onu_id=onu_indication.onu_id) |
| assert onu_device is not None |
| |
| msg = {'proxy_address':onu_device.proxy_address, |
| 'event':'activation-completed', |
| 'event_data':{'activation_successful':True}} |
| self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg) |
| |
| # |
| # tcont create (onu) |
| # |
| alloc_id = self.mk_alloc_id(onu_indication.onu_id) |
| msg = {'proxy_address':onu_device.proxy_address, |
| 'event':'create-tcont', |
| 'event_data':{'alloc_id':alloc_id}} |
| self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg) |
| |
| # |
| # v_enet create (olt) |
| # |
| uni_no = self._get_next_uni_port() |
| uni_name = self.port_name(uni_no, Port.ETHERNET_UNI) |
| self.adapter_agent.add_port( |
| self.device_id, |
| Port( |
| port_no=uni_no, |
| label=uni_name, |
| type=Port.ETHERNET_UNI, |
| admin_state=AdminState.ENABLED, |
| oper_status=OperStatus.ACTIVE)) |
| |
| # |
| # v_enet create (onu) |
| # |
| interface_name = self.port_name(onu_indication.intf_id, Port.PON_OLT) |
| msg = {'proxy_address':onu_device.proxy_address, |
| 'event':'create-venet', |
| 'event_data':{'uni_name':uni_name, 'interface_name':uni_name}} |
| self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg) |
| |
| # |
| # gem port create |
| # |
| gemport_id = self.mk_gemport_id(onu_indication.onu_id) |
| msg = {'proxy_address':onu_device.proxy_address, |
| 'event':'create-gemport', |
| 'event_data':{'gemport_id':gemport_id}} |
| self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg) |
| |
| def mk_gemport_id(self, onu_id): |
| return 1023 + onu_id # FIXME |
| |
| def mk_alloc_id(self, onu_id): |
| return 1023 + onu_id # FIXME |
| |
| def omci_indication(self, omci_indication): |
| |
| self.log.debug("omci indication", intf_id=omci_indication.intf_id, |
| onu_id=omci_indication.onu_id) |
| |
| onu_device = self.adapter_agent.get_child_device( |
| self.device_id, |
| onu_id=omci_indication.onu_id) |
| self.adapter_agent.receive_proxied_message( |
| onu_device.proxy_address, |
| omci_indication.pkt) |
| |
| def activate_onu(self, intf_id, onu_id, serial_number): |
| |
| self.log.info("activate onu", intf_id=intf_id, onu_id=onu_id, |
| serial_number=serial_number) |
| |
| self.onus[Onu(intf_id=intf_id, onu_id=onu_id)] = serial_number |
| |
| onu = openolt_pb2.Onu( |
| intf_id=intf_id, onu_id=onu_id, serial_number=serial_number) |
| |
| self.stub.ActivateOnu(onu) |
| |
| |
| def send_proxied_message(self, proxy_address, msg): |
| omci = openolt_pb2.OmciMsg( |
| intf_id=proxy_address.channel_id, # intf_id |
| onu_id=proxy_address.onu_id, |
| pkt=str(msg)) |
| self.stub.OmciMsgOut(omci) |
| |
| def add_onu_device(self, intf_id, port_no, onu_id, serial_number): |
| |
| self.log.info("Adding ONU", port_no=port_no, onu_id=onu_id, |
| serial_number=serial_number) |
| |
| # NOTE - channel_id of onu is set to intf_id |
| proxy_address = Device.ProxyAddress( |
| device_id=self.device_id, |
| channel_id=intf_id, |
| onu_id=onu_id, |
| onu_session_id=onu_id) |
| |
| self.log.info("Adding ONU", proxy_address=proxy_address) |
| |
| serial_number_str = ''.join([ |
| serial_number.vendor_id, |
| self.stringify_vendor_specific(serial_number.vendor_specific)]) |
| |
| self.adapter_agent.add_onu_device( |
| parent_device_id=self.device_id, parent_port_no=port_no, |
| vendor_id=serial_number.vendor_id, proxy_address=proxy_address, |
| root=True, serial_number=serial_number_str, |
| admin_state=AdminState.ENABLED) # FIXME |
| |
| def intf_id_to_port_no(self, intf_id, intf_type): |
| if intf_type is Port.ETHERNET_NNI: |
| # FIXME - Remove hardcoded '129' |
| return intf_id + 129 |
| elif intf_type is Port.PON_OLT: |
| # Interface Ids (reported by device) are zero-based indexed |
| # OpenFlow port numbering is one-based. |
| return intf_id + 1 |
| else: |
| raise Exception('Invalid port type') |
| |
| |
| def port_name(self, port_no, port_type): |
| if port_type is Port.ETHERNET_NNI: |
| prefix = "nni" |
| elif port_type is Port.PON_OLT: |
| prefix = "pon" |
| elif port_type is Port.ETHERNET_UNI: |
| prefix = "uni" |
| return prefix + "-" + str(port_no) |
| |
| def update_device_status(self, connect_status=None, oper_status=None, reason=None): |
| device = self.adapter_agent.get_device(self.device_id) |
| if connect_status is not None: |
| device.connect_status = connect_status |
| if oper_status is not None: |
| device.oper_status = oper_status |
| if reason is not None: |
| device.reason = reason |
| self.adapter_agent.update_device(device) |
| |
| def add_logical_port(self, port_no): |
| self.log.info('adding-logical-port', port_no=port_no) |
| |
| label = self.port_name(port_no, Port.ETHERNET_NNI) |
| |
| cap = OFPPF_1GB_FD | OFPPF_FIBER |
| curr_speed = OFPPF_1GB_FD |
| max_speed = OFPPF_1GB_FD |
| |
| ofp = ofp_port( |
| port_no=port_no, |
| hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no), |
| name=label, |
| config=0, |
| state=OFPPS_LIVE, |
| curr=cap, |
| advertised=cap, |
| peer=cap, |
| curr_speed=curr_speed, |
| max_speed=max_speed) |
| |
| logical_port = LogicalPort( |
| id=label, |
| ofp_port=ofp, |
| device_id=self.device_id, |
| device_port_no=port_no, |
| root_port=True |
| ) |
| |
| self.adapter_agent.add_logical_port(self.logical_device_id, logical_port) |
| |
| def add_port(self, intf_id, port_type, oper_status): |
| port_no = self.intf_id_to_port_no(intf_id, port_type) |
| |
| label = self.port_name(port_no, port_type) |
| |
| self.log.info('adding-port', port_no=port_no, label=label, port_type=port_type) |
| port = Port( |
| port_no=port_no, |
| label=label, |
| type=port_type, |
| admin_state=AdminState.ENABLED, |
| oper_status=oper_status |
| ) |
| self.adapter_agent.add_port(self.device_id, port) |
| return port_no, label |
| |
| def set_oper_state(self, new_state): |
| if self.oper_state != new_state: |
| if new_state == 'up': |
| self.update_device_status( |
| connect_status=ConnectStatus.REACHABLE, |
| oper_status=OperStatus.ACTIVE, |
| reason='OLT indication - operation state up') |
| elif new_state == 'down': |
| self.update_device_status( |
| connect_status=ConnectStatus.REACHABLE, |
| oper_status=OperStatus.FAILED, |
| reason='OLT indication - operation state down') |
| else: |
| raise ValueError('Invalid oper_state in olt_indication') |
| |
| self.oper_state = new_state |
| |
| def new_onu_id(self, intf_id): |
| onu_id = None |
| # onu_id is unique per PON. |
| # FIXME - Remove hardcoded limit on ONUs per PON (64) |
| for i in range(1, 64): |
| onu = Onu(intf_id=intf_id, onu_id=i) |
| if onu not in self.onus: |
| onu_id = i |
| break |
| return onu_id |
| |
| def stringify_vendor_specific(self, vendor_specific): |
| return ''.join(str(i) for i in [ |
| ord(vendor_specific[0])>>4 & 0x0f, |
| ord(vendor_specific[0]) & 0x0f, |
| ord(vendor_specific[1])>>4 & 0x0f, |
| ord(vendor_specific[1]) & 0x0f, |
| ord(vendor_specific[2])>>4 & 0x0f, |
| ord(vendor_specific[2]) & 0x0f, |
| ord(vendor_specific[3])>>4 & 0x0f, |
| ord(vendor_specific[3]) & 0x0f]) |
| |
| def lookup_onu(self, serial_number): |
| onu_id = None |
| for onu, s in self.onus.iteritems(): |
| if s.vendor_id == serial_number.vendor_id: |
| str1 = self.stringify_vendor_specific(s.vendor_specific) |
| str2 = self.stringify_vendor_specific(serial_number.vendor_specific) |
| if str1 == str2: |
| onu_id = onu.onu_id |
| break |
| return onu_id |
| |
| def update_flow_table(self, flows): |
| device = self.adapter_agent.get_device(self.device_id) |
| self.log.info('update flow table', flows=flows) |
| |
| for flow in flows: |
| self.log.info('flow-details', device_id=self.device_id, flow=flow) |
| classifier_info = dict() |
| action_info = dict() |
| is_down_stream = None |
| _in_port = None |
| try: |
| _in_port = fd.get_in_port(flow) |
| assert _in_port is not None |
| # Right now there is only one NNI port. Get the NNI PORT and compare |
| # with IN_PUT port number. Need to find better way. |
| ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_NNI) |
| |
| for port in ports: |
| if (port.port_no == _in_port): |
| self.log.info('downstream-flow') |
| is_down_stream = True |
| break |
| if is_down_stream is None: |
| is_down_stream = False |
| self.log.info('upstream-flow') |
| |
| _out_port = fd.get_out_port(flow) # may be None |
| self.log.info('out-port', out_port=_out_port) |
| |
| for field in fd.get_ofb_fields(flow): |
| |
| if field.type == fd.ETH_TYPE: |
| classifier_info['eth_type'] = field.eth_type |
| self.log.info('field-type-eth-type', |
| eth_type=classifier_info['eth_type']) |
| |
| elif field.type == fd.IP_PROTO: |
| classifier_info['ip_proto'] = field.ip_proto |
| self.log.info('field-type-ip-proto', |
| ip_proto=classifier_info['ip_proto']) |
| |
| elif field.type == fd.IN_PORT: |
| classifier_info['in_port'] = field.port |
| self.log.info('field-type-in-port', |
| in_port=classifier_info['in_port']) |
| |
| elif field.type == fd.VLAN_VID: |
| classifier_info['vlan_vid'] = field.vlan_vid & 0xfff |
| self.log.info('field-type-vlan-vid', |
| vlan=classifier_info['vlan_vid']) |
| |
| elif field.type == fd.VLAN_PCP: |
| classifier_info['vlan_pcp'] = field.vlan_pcp |
| self.log.info('field-type-vlan-pcp', |
| pcp=classifier_info['vlan_pcp']) |
| |
| elif field.type == fd.UDP_DST: |
| classifier_info['udp_dst'] = field.udp_dst |
| self.log.info('field-type-udp-dst', |
| udp_dst=classifier_info['udp_dst']) |
| |
| elif field.type == fd.UDP_SRC: |
| classifier_info['udp_src'] = field.udp_src |
| self.log.info('field-type-udp-src', |
| udp_src=classifier_info['udp_src']) |
| |
| elif field.type == fd.IPV4_DST: |
| classifier_info['ipv4_dst'] = field.ipv4_dst |
| self.log.info('field-type-ipv4-dst', |
| ipv4_dst=classifier_info['ipv4_dst']) |
| |
| elif field.type == fd.IPV4_SRC: |
| classifier_info['ipv4_src'] = field.ipv4_src |
| self.log.info('field-type-ipv4-src', |
| ipv4_dst=classifier_info['ipv4_src']) |
| |
| elif field.type == fd.METADATA: |
| classifier_info['metadata'] = field.table_metadata |
| self.log.info('field-type-metadata', |
| metadata=classifier_info['metadata']) |
| |
| else: |
| raise NotImplementedError('field.type={}'.format( |
| field.type)) |
| |
| for action in fd.get_actions(flow): |
| |
| if action.type == fd.OUTPUT: |
| action_info['output'] = action.output.port |
| self.log.info('action-type-output', |
| output=action_info['output'], |
| in_port=classifier_info['in_port']) |
| |
| elif action.type == fd.POP_VLAN: |
| action_info['pop_vlan'] = True |
| self.log.info('action-type-pop-vlan', |
| in_port=_in_port) |
| |
| elif action.type == fd.PUSH_VLAN: |
| action_info['push_vlan'] = True |
| action_info['tpid'] = action.push.ethertype |
| self.log.info('action-type-push-vlan', |
| push_tpid=action_info['tpid'], |
| in_port=_in_port) |
| if action.push.ethertype != 0x8100: |
| self.log.error('unhandled-tpid', |
| ethertype=action.push.ethertype) |
| |
| elif action.type == fd.SET_FIELD: |
| # action_info['action_type'] = 'set_field' |
| _field = action.set_field.field.ofb_field |
| assert (action.set_field.field.oxm_class == |
| OFPXMC_OPENFLOW_BASIC) |
| self.log.info('action-type-set-field', |
| field=_field, in_port=_in_port) |
| if _field.type == fd.VLAN_VID: |
| self.log.info('set-field-type-vlan-vid', |
| vlan_vid=_field.vlan_vid & 0xfff) |
| action_info['vlan_vid'] = (_field.vlan_vid & 0xfff) |
| else: |
| self.log.error('unsupported-action-set-field-type', |
| field_type=_field.type) |
| else: |
| self.log.error('unsupported-action-type', |
| action_type=action.type, in_port=_in_port) |
| |
| if is_down_stream is False: |
| intf_id, onu_id = self.parse_port_no(classifier_info['in_port']) |
| self.divide_and_add_flow(onu_id, intf_id, classifier_info, action_info) |
| except Exception as e: |
| self.log.exception('failed-to-install-flow', e=e, flow=flow) |
| |
| def parse_port_no(self, port_no): |
| return 0, 1 # FIXME |
| |
| # This function will divide the upstream flow into both |
| # upstreand and downstream flow, as broadcom devices |
| # expects down stream flows to be added to handle |
| # packet_out messge from controller. |
| def divide_and_add_flow(self, onu_id, intf_id, classifier, action): |
| if 'ip_proto' in classifier: |
| if classifier['ip_proto'] == 17: |
| self.log.error('dhcp flow add ignored') |
| elif classifier['ip_proto'] == 2: |
| self.log.info('igmp flow add ignored') |
| else: |
| self.log.info("Invalid-Classifier-to-handle", |
| classifier=classifier, |
| action=action) |
| elif 'eth_type' in classifier: |
| if classifier['eth_type'] == 0x888e: |
| self.log.error('epol flow add ignored') |
| elif 'push_vlan' in action: |
| self.add_data_flow(onu_id, intf_id, classifier, action) |
| else: |
| self.log.info('Invalid-flow-type-to-handle', |
| classifier=classifier, |
| action=action) |
| |
| def add_data_flow(self, onu_id, intf_id, uplink_classifier, uplink_action): |
| |
| downlink_classifier = dict(uplink_classifier) |
| downlink_action = dict(uplink_action) |
| |
| uplink_classifier['pkt_tag_type'] = 'single_tag' |
| |
| downlink_classifier['pkt_tag_type'] = 'double_tag' |
| downlink_classifier['vlan_vid'] = uplink_action['vlan_vid'] |
| downlink_classifier['metadata'] = uplink_classifier['vlan_vid'] |
| del downlink_action['push_vlan'] |
| downlink_action['pop_vlan'] = True |
| |
| # To-Do right now only one GEM port is supported, so below method |
| # will take care of handling all the p bits. |
| # We need to revisit when mulitple gem port per p bits is needed. |
| self.add_hsia_flow(onu_id, intf_id, uplink_classifier, uplink_action, |
| downlink_classifier, downlink_action, |
| ASFVOLT_HSIA_ID) |
| |
| def mk_classifier(self, classifier_info): |
| |
| classifier = openolt_pb2.Classifier() |
| |
| if 'eth_type' in classifier_info: |
| classifier.eth_type = classifier_info['eth_type'] |
| if 'ip_proto' in classifier_info: |
| classifier.ip_proto = classifier_info['ip_proto'] |
| if 'vlan_vid' in classifier_info: |
| classifier.o_vid = classifier_info['vlan_vid'] |
| if 'metadata' in classifier_info: |
| classifier.i_vid = classifier_info['metadata'] |
| if 'vlan_pcp' in classifier_info: |
| classifier.o_pbits = classifier_info['vlan_pcp'] |
| if 'udp_src' in classifier_info: |
| classifier.src_port = classifier_info['udp_src'] |
| if 'udp_dst' in classifier_info: |
| classifier.dst_port = classifier_info['udp_dst'] |
| if 'ipv4_dst' in classifier_info: |
| classifier.dst_ip = classifier_info['ipv4_dst'] |
| if 'ipv4_src' in classifier_info: |
| classifier.src_ip = classifier_info['ipv4_src'] |
| if 'pkt_tag_type' in classifier_info: |
| if classifier_info['pkt_tag_type'] == 'single_tag': |
| classifier.pkt_tag_type = 'single_tag' |
| elif classifier_info['pkt_tag_type'] == 'double_tag': |
| classifier.pkt_tag_type = 'double_tag' |
| elif classifier_info['pkt_tag_type'] == 'untagged': |
| classifier.pkt_tag_type = 'untagged' |
| else: |
| classifier.pkt_tag_type = 'none' |
| |
| return classifier |
| |
| def mk_action(self, action_info): |
| action = openolt_pb2.Action() |
| |
| if 'pop_vlan' in action_info: |
| action.o_vid = action_info['vlan_vid'] |
| action.cmd.remove_outer_tag = True |
| elif 'push_vlan' in action_info: |
| action.o_vid = action_info['vlan_vid'] |
| action.cmd.add_outer_tag = True |
| elif 'trap_to_host' in action_info: |
| action.cmd.trap_to_host = True |
| else: |
| self.log.info('Invalid-action-field') |
| return |
| return action |
| |
| def add_hsia_flow(self, onu_id, intf_id, uplink_classifier, uplink_action, |
| downlink_classifier, downlink_action, hsia_id): |
| |
| gemport_id = self.mk_gemport_id(onu_id) |
| alloc_id = self.mk_alloc_id(onu_id) |
| flow_id = self.mk_flow_id(onu_id, intf_id, hsia_id) |
| |
| self.log.info('add_hsia_flow', |
| onu_id=onu_id, |
| classifier=uplink_classifier, |
| action=uplink_action, |
| gemport_id=gemport_id, |
| flow_id=flow_id, |
| sched_info=alloc_id) |
| |
| flow = openolt_pb2.Flow( |
| onu_id=onu_id, |
| flow_id=flow_id, |
| flow_type="upstream", |
| gemport_id=gemport_id, |
| classifier=self.mk_classifier(uplink_classifier), |
| action=self.mk_action(uplink_action)) |
| self.stub.FlowAdd(flow) |
| time.sleep(0.1) # FIXME |
| |
| self.log.info('Adding-ARP-downstream-flow', |
| classifier=downlink_classifier, |
| action=downlink_action, |
| gemport_id=gemport_id, |
| flow_id=flow_id) |
| |
| flow = openolt_pb2.Flow( |
| onu_id=onu_id, |
| flow_id=flow_id, |
| flow_type="downstream", |
| access_intf_id=intf_id, |
| gemport_id=gemport_id, |
| classifier=self.mk_classifier(downlink_classifier), |
| action=self.mk_action(downlink_action)) |
| self.stub.FlowAdd(flow) |
| time.sleep(0.1) # FIXME |
| |
| def mk_flow_id(self, onu_id, intf_id, id): |
| # Tp-Do Need to generate unique flow ID using |
| # OnuID, IntfId, id |
| # BAL accepts flow_id till 16384. So we are |
| # using only onu_id and id to generate flow ID. |
| return ((onu_id << 5) | id) |