blob: 13588f776391e5e6a3de69f3dc2718875d67e6af [file] [log] [blame]
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -05001#
2# Copyright 2018 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import structlog
17from enum import Enum
William Kurkian8235c1e2019-03-05 12:58:28 -050018from voltha_protos.common_pb2 import OperStatus, AdminState
19from voltha_protos.device_pb2 import Port
20from voltha_protos.openflow_13_pb2 import OFPPF_10GB_FD
Matt Jeanneret72f96fc2019-02-11 10:53:05 -050021from pyvoltha.common.utils.nethelpers import mac_str_to_tuple
William Kurkian8235c1e2019-03-05 12:58:28 -050022from voltha_protos.logical_device_pb2 import LogicalPort
23from voltha_protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN
24from voltha_protos.openflow_13_pb2 import ofp_port
Matt Jeanneretf1e9c5d2019-02-08 07:41:29 -050025
26class UniType(Enum):
27 """
28 UNI Types Defined in G.988
29 """
30 PPTP = 'PhysicalPathTerminationPointEthernet'
31 VEIP = 'VirtualEthernetInterfacePoint'
32 # TODO: Add others as they become supported
33
34
35class UniPort(object):
36 """Wraps southbound-port(s) support for ONU"""
37
38 def __init__(self, handler, name, uni_id, port_no, ofp_port_no,
39 type=UniType.PPTP):
40 self.log = structlog.get_logger(device_id=handler.device_id,
41 port_no=port_no)
42 self._enabled = False
43 self._handler = handler
44 self._name = name
45 self._port = None
46 self._port_number = port_no
47 self._ofp_port_no = ofp_port_no
48 self._logical_port_number = None
49 self._entity_id = None
50 self._mac_bridge_port_num = 0
51 self._type = type
52 self._uni_id = uni_id
53
54 self._admin_state = AdminState.ENABLED
55 self._oper_status = OperStatus.ACTIVE
56
57 def __str__(self):
58 return "UniPort - name: {}, port_number: {}, entity_id: {}, mac_bridge_port_num: {}, type: {}, ofp_port: {}"\
59 .format(self.name, self.port_number, self.entity_id, self._mac_bridge_port_num, self.type, self._ofp_port_no)
60
61 def __repr__(self):
62 return str(self)
63
64 @staticmethod
65 def create(handler, name, uni_id, port_no, ofp_port_no, type):
66 port = UniPort(handler, name, uni_id, port_no, ofp_port_no, type)
67 return port
68
69 def _start(self):
70 self._cancel_deferred()
71 self._admin_state = AdminState.ENABLED
72 self._oper_status = OperStatus.ACTIVE
73 self._update_adapter_agent()
74
75 def _stop(self):
76 self._cancel_deferred()
77 self._admin_state = AdminState.DISABLED
78 self._oper_status = OperStatus.UNKNOWN
79 self._update_adapter_agent()
80
81 def delete(self):
82 self.enabled = False
83 self._handler = None
84
85 def _cancel_deferred(self):
86 pass
87
88 @property
89 def name(self):
90 return self._name
91
92 @property
93 def enabled(self):
94 return self._enabled
95
96 @enabled.setter
97 def enabled(self, value):
98 if self._enabled != value:
99 self._enabled = value
100
101 if value:
102 self._start()
103 else:
104 self._stop()
105
106 @property
107 def uni_id(self):
108 """
109 Physical prt index on ONU 0 - N
110 :return: (int) uni id
111 """
112 return self._uni_id
113
114
115 @property
116 def mac_bridge_port_num(self):
117 """
118 Port number used when creating MacBridgePortConfigurationDataFrame port number
119 :return: (int) port number
120 """
121 return self._mac_bridge_port_num
122
123 @mac_bridge_port_num.setter
124 def mac_bridge_port_num(self, value):
125 self._mac_bridge_port_num = value
126
127 @property
128 def port_number(self):
129 """
130 Physical device port number
131 :return: (int) port number
132 """
133 return self._port_number
134
135 @property
136 def entity_id(self):
137 """
138 OMCI UNI_G entity ID for port
139 """
140 return self._entity_id
141
142 @entity_id.setter
143 def entity_id(self, value):
144 assert self._entity_id is None, 'Cannot reset the Entity ID'
145 self._entity_id = value
146
147 @property
148 def logical_port_number(self):
149 """
150 Logical device port number (used as OpenFlow port for UNI)
151 :return: (int) port number
152 """
153 return self._logical_port_number
154
155 @property
156 def type(self):
157 """
158 UNI Type used in OMCI messaging
159 :return: (UniType) One of the enumerated types
160 """
161 return self._type
162
163 def _update_adapter_agent(self):
164 """
165 Update the port status and state in the core
166 """
167 self.log.debug('update-adapter-agent', admin_state=self._admin_state,
168 oper_status=self._oper_status)
169
170 if self._port is not None:
171 self._port.admin_state = self._admin_state
172 self._port.oper_status = self._oper_status
173
174 try:
175 # adapter_agent add_port also does an update of existing port
176 self._handler.adapter_agent.add_port(self._handler.device_id,
177 self.get_port())
178
179 except Exception as e:
180 self.log.exception('update-port', e=e)
181
182 def get_port(self):
183 """
184 Get the VOLTHA PORT object for this port
185 :return: VOLTHA Port object
186 """
187 self._port = Port(port_no=self.port_number,
188 label=self.port_id_name(),
189 type=Port.ETHERNET_UNI,
190 admin_state=self._admin_state,
191 oper_status=self._oper_status)
192 return self._port
193
194 def port_id_name(self):
195 return 'uni-{}'.format(self._port_number)
196
197 def add_logical_port(self, openflow_port_no, multi_uni_naming,
198 capabilities=OFPPF_10GB_FD | OFPPF_FIBER,
199 speed=OFPPF_10GB_FD):
200
201 self.log.debug('function-entry')
202
203 if self._logical_port_number is not None:
204 # delete old logical port if it exists
205 try:
206 port = self._handler.adapter_agent.get_logical_port(self._handler.logical_device_id,
207 self.port_id_name())
208 self._handler.adapter_agent.delete_logical_port(self._handler.logical_device_id, port)
209
210 except Exception as e:
211 # assume this exception was because logical port does not already exist
212 pass
213
214 self._logical_port_number = None
215
216 port_no = openflow_port_no or self._ofp_port_no
217
218 if self._logical_port_number is None and port_no is not None:
219 self._logical_port_number = port_no
220
221 device = self._handler.adapter_agent.get_device(self._handler.device_id)
222
223 # leave the ports down until omci mib download has finished. otherwise flows push before time
224 openflow_port = ofp_port(
225 port_no=port_no,
226 hw_addr=mac_str_to_tuple('08:%02x:%02x:%02x:%02x:%02x' %
227 ((device.parent_port_no >> 8 & 0xff),
228 device.parent_port_no & 0xff,
229 (port_no >> 16) & 0xff,
230 (port_no >> 8) & 0xff,
231 port_no & 0xff)),
232 name=device.serial_number + ['', '-' + str(self._mac_bridge_port_num)][multi_uni_naming],
233 config=0,
234 state=OFPPS_LINK_DOWN,
235 curr=capabilities,
236 advertised=capabilities,
237 peer=capabilities,
238 curr_speed=speed,
239 max_speed=speed
240 )
241 self._handler.adapter_agent.add_logical_port(self._handler.logical_device_id,
242 LogicalPort(
243 id=self.port_id_name(),
244 ofp_port=openflow_port,
245 device_id=device.id,
246 device_port_no=self._port_number))
247
248 self.log.debug('logical-port', id=self.port_id_name(), device_port_no=self._port_number, openflow_port=openflow_port)