blob: b8294996dfe6b4d54450621de5973228c3b1d2ca [file] [log] [blame]
Chip Boling8e042f62019-02-12 16:14:34 -06001# Copyright 2017-present Adtran, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import structlog
16from pyvoltha.protos.common_pb2 import OperStatus, AdminState
17from pyvoltha.protos.device_pb2 import Port
18from pyvoltha.protos.openflow_13_pb2 import OFPPF_10GB_FD
19from pyvoltha.protos.logical_device_pb2 import LogicalPort
20from pyvoltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER
21from pyvoltha.protos.openflow_13_pb2 import ofp_port
Chip Bolingd2d7a4d2019-03-14 14:34:56 -050022# import adtran_olt.resources.adtranolt_platform as platform
23from twisted.internet.defer import inlineCallbacks
Chip Boling8e042f62019-02-12 16:14:34 -060024
25
26class UniPort(object):
27 """Wraps southbound-port(s) support for ONU"""
28 def __init__(self, handler, name, port_no, ofp_port_no):
29 self.log = structlog.get_logger(device_id=handler.device_id, port_no=port_no)
30 self._enabled = False
31 self._handler = handler
32 self._name = name
33 self.uni_id = platform.uni_id_from_uni_port(port_no)
34 self._port = None
35 self._port_number = port_no
36 self._ofp_port_no = ofp_port_no # Set at by creator (vENET create)
37 self._logical_port_number = None # Set at time of logical port creation
38 self._entity_id = None # TODO: Use port number from UNI-G entity ID
39 self._mac_bridge_port_num = 0
40
41 self._admin_state = AdminState.ENABLED
42 self._oper_status = OperStatus.ACTIVE
43 # TODO Add state, stats, alarm reference, ...
44 pass
45
46 def __str__(self):
47 return "UniPort: {}:{}".format(self.name, self.port_number)
48
49 @staticmethod
50 def create(handler, name, port_no, ofp_port_no):
51 port = UniPort(handler, name, port_no, ofp_port_no)
52 return port
53
54 def _start(self):
55 self._cancel_deferred()
56
57 self._admin_state = AdminState.ENABLED
58 self._oper_status = OperStatus.ACTIVE
59 self._update_adapter_agent()
60 # TODO: start h/w sync
61 # TODO: Enable the actual physical port?
62 pass
63
64 def _stop(self):
65 self._cancel_deferred()
66
67 self._admin_state = AdminState.DISABLED
68 self._oper_status = OperStatus.UNKNOWN
69 self._update_adapter_agent()
70 # TODO: Disable/power-down the actual physical port?
71 pass
72
73 def delete(self):
74 self.enabled = False
75 self._handler = None
76
77 def _cancel_deferred(self):
78 pass
79
80 @property
81 def name(self):
82 return self._name
83
84 @property
85 def enabled(self):
86 return self._enabled
87
88 @enabled.setter
89 def enabled(self, value):
90 if self._enabled != value:
91 self._enabled = value
92
93 if value:
94 self._start()
95 else:
96 self._stop()
97
98 @property
99 def port_number(self):
100 """
101 Physical device port number
102 :return: (int) port number
103 """
104 return self._port_number
105
106 @property
107 def mac_bridge_port_num(self):
108 """
109 Port number used when creating MacBridgePortConfigurationDataFrame port number
110 :return: (int) port number
111 """
112 self.log.debug('function-entry')
113 return self._mac_bridge_port_num
114
115 @mac_bridge_port_num.setter
116 def mac_bridge_port_num(self, value):
117 self.log.debug('function-entry')
118 self._mac_bridge_port_num = value
119
120 @property
121 def entity_id(self):
122 """
123 OMCI UNI_G entity ID for port
124 """
125 return self._entity_id
126
127 @entity_id.setter
128 def entity_id(self, value):
129 assert self._entity_id is None, 'Cannot reset the Entity ID'
130 self._entity_id = value
131
132 @property
133 def logical_port_number(self):
134 """
135 Logical device port number (used as OpenFlow port for UNI)
136 :return: (int) port number
137 """
138 return self._logical_port_number
139
Chip Bolingd2d7a4d2019-03-14 14:34:56 -0500140 @inlineCallbacks
Chip Boling8e042f62019-02-12 16:14:34 -0600141 def _update_adapter_agent(self):
142 """
143 Update the port status and state in the core
144 """
145 self.log.debug('update-adapter-agent', admin_state=self._admin_state,
146 oper_status=self._oper_status)
147
148 if self._port is not None:
149 self._port.admin_state = self._admin_state
150 self._port.oper_status = self._oper_status
151
Chip Bolingd2d7a4d2019-03-14 14:34:56 -0500152 try:
153 yield self._handler.adapter_agent.port_state_update(self._handler.device_id,
154 self._port.type,
155 self._port.port_no,
156 self._port.oper_status)
157
158 except KeyError: # Expected exception during ONU disabling
159 pass
160 except Exception as e: # Expected exception during ONU disabling
161 self.log.exception('update-port', e=e)
Chip Boling8e042f62019-02-12 16:14:34 -0600162
163 def get_port(self):
164 """
165 Get the VOLTHA PORT object for this port
166 :return: VOLTHA Port object
167 """
168 if self._port is None:
169 self._port = Port(port_no=self.port_number,
170 label=self.port_id_name(),
171 type=Port.ETHERNET_UNI,
172 admin_state=self._admin_state,
173 oper_status=self._oper_status)
174 return self._port
175
176 def port_id_name(self):
177 return 'uni-{}'.format(self._port_number)
178
179 def add_logical_port(self, openflow_port_no, multi_uni_naming,
180 capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
181 speed=OFPPF_10GB_FD):
182
183 if self._logical_port_number is not None:
184 # delete old logical port if it exists
185 try:
186 port = self._handler.adapter_agent.get_logical_port(self._handler.logical_device_id,
187 self.port_id_name())
188 self._handler.adapter_agent.delete_logical_port(self._handler.logical_device_id, port)
189
190 except Exception as e:
191 # assume this exception was because logical port does not already exist
192 pass
193
194 self._logical_port_number = None
195
196 # Use vENET provisioned values if none supplied
197 port_no = openflow_port_no or self._ofp_port_no
198
199 if self._logical_port_number is None and port_no is not None:
200 self._logical_port_number = port_no
201 device = self._handler.adapter_agent.get_device(self._handler.device_id)
202
203 def mac_str_to_tuple(mac):
204 """
205 Convert 'xx:xx:xx:xx:xx:xx' MAC address string to a tuple of integers.
206 Example: mac_str_to_tuple('00:01:02:03:04:05') == (0, 1, 2, 3, 4, 5)
207 """
208 return tuple(int(d, 16) for d in mac.split(':'))
209
210 openflow_port = ofp_port(
211 port_no=port_no,
212 hw_addr=mac_str_to_tuple('08:00:%02x:%02x:%02x:%02x' %
213 ((device.parent_port_no >> 8 & 0xff),
214 device.parent_port_no & 0xff,
215 (port_no >> 8) & 0xff,
216 port_no & 0xff)),
217 name=device.serial_number + ['', '-' + str(self._mac_bridge_port_num)][multi_uni_naming],
218 config=0,
219 state=OFPPS_LIVE,
220 curr=capabilities,
221 advertised=capabilities,
222 peer=capabilities,
223 curr_speed=speed,
224 max_speed=speed
225 )
226 self._handler.adapter_agent.add_logical_port(self._handler.logical_device_id,
227 LogicalPort(
228 id=self.port_id_name(),
229 ofp_port=openflow_port,
230 device_id=device.id,
231 device_port_no=self._port_number))