blob: 9de22438d7af0576b6ce4e21f5258ba619080f48 [file] [log] [blame]
# Copyright 2017-present Adtran, Inc.
#
# 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 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
from voltha.protos.openflow_13_pb2 import ofp_port
class UniPort(object):
"""Wraps southbound-port(s) support for ONU"""
DEFAULT_UNTAGGED_VLAN = 4092
def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
untagged_vlan=None):
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 # Set at by creator (vENET create)
self._logical_port_number = None # Set at time of logical port creation
self._subscriber_vlan = subscriber_vlan
self._untagged_vlan = untagged_vlan
self._admin_state = AdminState.ENABLED
self._oper_status = OperStatus.ACTIVE
# TODO Add state, stats, alarm reference, ...
pass
def __str__(self):
return "UniPort: {}:{}".format(self.name, self.port_number)
@staticmethod
def create(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan):
port = UniPort(handler, name, port_no, ofp_port_no, subscriber_vlan, untagged_vlan)
return port
def _start(self):
self._cancel_deferred()
self._admin_state = AdminState.ENABLED
self._oper_status = OperStatus.ACTIVE
self._update_adapter_agent()
# TODO: start h/w sync
# TODO: Enable the actual physical port?
pass
def _stop(self):
self._cancel_deferred()
self._admin_state = AdminState.DISABLED
self._oper_status = OperStatus.UNKNOWN
self._update_adapter_agent()
# TODO: Disable/power-down the actual physical port?
pass
def delete(self):
self.enabled = False
self._handler = None
# TODO: anything else
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 port_number(self):
"""
Physical device port number
:return: (int) port number
"""
return self._port_number
@property
def subscriber_vlan(self):
"""
Subscriber vlan assigned to this UNI
:return: (int) subscriber vlan
"""
return self._subscriber_vlan
@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
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)
@staticmethod
def decode_venet(venet_info):
try:
# Allow spaces or dashes as separator, select last as the
# port number. UNI-1, UNI 1, and UNI 3-2-1 are the same
port_no = int(venet_info['name'].replace(' ', '-').split('-')[-1:][0])
subscriber_vlan = port_no
untagged_vlan = UniPort.DEFAULT_UNTAGGED_VLAN
try:
# Subscriber VLAN and Untagged vlan are comma separated
parts = venet_info['description'].split(',')
sub_part = next((part for part in parts if 'vlan' in part.lower()), None)
untagged_part = next((part for part in parts if 'untagged' in part.lower()), None)
try:
if sub_part is not None:
subscriber_vlan = int(sub_part.split(':')[-1:][0])
except Exception as e:
pass
try:
if untagged_part is not None:
untagged_vlan = int(untagged_part.split(':')[-1:][0])
except Exception as e:
pass
except Exception as e:
pass
return port_no, subscriber_vlan, untagged_vlan
except ValueError:
pass
except KeyError:
pass
def get_port(self):
"""
Get the VOLTHA PORT object for this port
:return: VOLTHA Port object
"""
if self._port is None:
self._port = Port(port_no=self.port_number,
label='Ethernet port',
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._logical_port_number)
def add_logical_port(self, openflow_port_no, subscriber_vlan=None,
capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
speed=OFPPF_10GB_FD):
if self._logical_port_number is not None:
# delete old logical port if it exists
try:
port = self.adapter_agent.get_logical_port(self._handler.logical_device_id,
self.port_id_name())
self.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
# Use vENET provisioned values if none supplied
port_no = openflow_port_no or self._ofp_port_no
vlan = subscriber_vlan or self._subscriber_vlan
if self._logical_port_number is None and port_no is not None:
self._logical_port_number = port_no
self._subscriber_vlan = vlan
device = self._handler.adapter_agent.get_device(self._handler.device_id)
if vlan is not None and device.vlan != vlan:
device.vlan = vlan
self._handler.adapter_agent.update_device(device)
openflow_port = ofp_port(
port_no=port_no,
hw_addr=mac_str_to_tuple('08:00:%02x:%02x:%02x:%02x' %
((device.parent_port_no >> 8 & 0xff),
device.parent_port_no & 0xff,
(port_no >> 8) & 0xff,
port_no & 0xff)),
name=self.port_id_name(),
config=0,
state=OFPPS_LIVE,
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))
# TODO: Should we use the UNI object 'name' as the id for OpenFlow?