| # |
| # 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 |
| from enum import Enum |
| from voltha.protos.common_pb2 import OperStatus, AdminState |
| from voltha.protos.device_pb2 import Port |
| from voltha.protos.openflow_13_pb2 import OFPPF_10GB_FD |
| from voltha.core.logical_device_agent import mac_str_to_tuple |
| from voltha.protos.logical_device_pb2 import LogicalPort |
| from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN |
| from voltha.protos.openflow_13_pb2 import ofp_port |
| |
| class UniType(Enum): |
| """ |
| UNI Types Defined in G.988 |
| """ |
| PPTP = 'PhysicalPathTerminationPointEthernet' |
| VEIP = 'VirtualEthernetInterfacePoint' |
| # TODO: Add others as they become supported |
| |
| |
| class UniPort(object): |
| """Wraps southbound-port(s) support for ONU""" |
| |
| def __init__(self, handler, name, uni_id, port_no, ofp_port_no, |
| type=UniType.PPTP): |
| self.log = structlog.get_logger(device_id=handler.device_id, |
| port_no=port_no) |
| self._enabled = False |
| self._handler = handler |
| self._name = name |
| self._port = None |
| self._port_number = port_no |
| self._ofp_port_no = ofp_port_no |
| self._logical_port_number = None |
| self._entity_id = None |
| self._mac_bridge_port_num = 0 |
| self._type = type |
| self._uni_id = uni_id |
| |
| self._admin_state = AdminState.ENABLED |
| self._oper_status = OperStatus.ACTIVE |
| |
| def __str__(self): |
| return "UniPort - name: {}, port_number: {}, entity_id: {}, mac_bridge_port_num: {}, type: {}, ofp_port: {}"\ |
| .format(self.name, self.port_number, self.entity_id, self._mac_bridge_port_num, self.type, self._ofp_port_no) |
| |
| def __repr__(self): |
| return str(self) |
| |
| @staticmethod |
| def create(handler, name, uni_id, port_no, ofp_port_no, type): |
| port = UniPort(handler, name, uni_id, port_no, ofp_port_no, type) |
| return port |
| |
| def _start(self): |
| self._cancel_deferred() |
| self._admin_state = AdminState.ENABLED |
| self._oper_status = OperStatus.ACTIVE |
| self._update_adapter_agent() |
| |
| def _stop(self): |
| self._cancel_deferred() |
| self._admin_state = AdminState.DISABLED |
| self._oper_status = OperStatus.UNKNOWN |
| self._update_adapter_agent() |
| |
| def delete(self): |
| self.enabled = False |
| self._handler = None |
| |
| def _cancel_deferred(self): |
| pass |
| |
| @property |
| def name(self): |
| return self._name |
| |
| @property |
| def enabled(self): |
| return self._enabled |
| |
| @enabled.setter |
| def enabled(self, value): |
| if self._enabled != value: |
| self._enabled = value |
| |
| if value: |
| self._start() |
| else: |
| self._stop() |
| |
| @property |
| def uni_id(self): |
| """ |
| Physical prt index on ONU 0 - N |
| :return: (int) uni id |
| """ |
| return self._uni_id |
| |
| |
| @property |
| def mac_bridge_port_num(self): |
| """ |
| Port number used when creating MacBridgePortConfigurationDataFrame port number |
| :return: (int) port number |
| """ |
| return self._mac_bridge_port_num |
| |
| @mac_bridge_port_num.setter |
| def mac_bridge_port_num(self, value): |
| self._mac_bridge_port_num = value |
| |
| @property |
| def port_number(self): |
| """ |
| Physical device port number |
| :return: (int) port number |
| """ |
| return self._port_number |
| |
| @property |
| def entity_id(self): |
| """ |
| OMCI UNI_G entity ID for port |
| """ |
| return self._entity_id |
| |
| @entity_id.setter |
| def entity_id(self, value): |
| assert self._entity_id is None, 'Cannot reset the Entity ID' |
| self._entity_id = value |
| |
| @property |
| def logical_port_number(self): |
| """ |
| Logical device port number (used as OpenFlow port for UNI) |
| :return: (int) port number |
| """ |
| return self._logical_port_number |
| |
| @property |
| def type(self): |
| """ |
| UNI Type used in OMCI messaging |
| :return: (UniType) One of the enumerated types |
| """ |
| return self._type |
| |
| def _update_adapter_agent(self): |
| """ |
| Update the port status and state in the core |
| """ |
| self.log.debug('update-adapter-agent', admin_state=self._admin_state, |
| oper_status=self._oper_status) |
| |
| if self._port is not None: |
| self._port.admin_state = self._admin_state |
| self._port.oper_status = self._oper_status |
| |
| try: |
| # adapter_agent add_port also does an update of existing port |
| self._handler.adapter_agent.add_port(self._handler.device_id, |
| self.get_port()) |
| |
| except Exception as e: |
| self.log.exception('update-port', e=e) |
| |
| def get_port(self): |
| """ |
| Get the VOLTHA PORT object for this port |
| :return: VOLTHA Port object |
| """ |
| self._port = Port(port_no=self.port_number, |
| label=self.port_id_name(), |
| type=Port.ETHERNET_UNI, |
| admin_state=self._admin_state, |
| oper_status=self._oper_status) |
| return self._port |
| |
| def port_id_name(self): |
| return 'uni-{}'.format(self._port_number) |
| |
| def add_logical_port(self, openflow_port_no, multi_uni_naming, |
| capabilities=OFPPF_10GB_FD | OFPPF_FIBER, |
| speed=OFPPF_10GB_FD): |
| |
| self.log.debug('function-entry') |
| |
| if self._logical_port_number is not None: |
| # delete old logical port if it exists |
| try: |
| port = self._handler.adapter_agent.get_logical_port(self._handler.logical_device_id, |
| self.port_id_name()) |
| self._handler.adapter_agent.delete_logical_port(self._handler.logical_device_id, port) |
| |
| except Exception as e: |
| # assume this exception was because logical port does not already exist |
| pass |
| |
| self._logical_port_number = None |
| |
| port_no = openflow_port_no or self._ofp_port_no |
| |
| if self._logical_port_number is None and port_no is not None: |
| self._logical_port_number = port_no |
| |
| device = self._handler.adapter_agent.get_device(self._handler.device_id) |
| |
| # leave the ports down until omci mib download has finished. otherwise flows push before time |
| openflow_port = ofp_port( |
| port_no=port_no, |
| hw_addr=mac_str_to_tuple('08:%02x:%02x:%02x:%02x:%02x' % |
| ((device.parent_port_no >> 8 & 0xff), |
| device.parent_port_no & 0xff, |
| (port_no >> 16) & 0xff, |
| (port_no >> 8) & 0xff, |
| port_no & 0xff)), |
| name=device.serial_number + ['', '-' + str(self._mac_bridge_port_num)][multi_uni_naming], |
| config=0, |
| state=OFPPS_LINK_DOWN, |
| curr=capabilities, |
| advertised=capabilities, |
| peer=capabilities, |
| curr_speed=speed, |
| max_speed=speed |
| ) |
| self._handler.adapter_agent.add_logical_port(self._handler.logical_device_id, |
| LogicalPort( |
| id=self.port_id_name(), |
| ofp_port=openflow_port, |
| device_id=device.id, |
| device_port_no=self._port_number)) |
| |
| self.log.debug('logical-port', id=self.port_id_name(), device_port_no=self._port_number, openflow_port=openflow_port) |