blob: dfac1d367cbe4433bf724b502e2a8e9d5d89d560 [file] [log] [blame]
khenaidoob9203542018-09-17 22:56:37 -04001#
2# Copyright 2017 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#
16
17"""
khenaidoo6fdf0ba2018-11-02 14:38:33 -040018Represents an ONU device
khenaidoob9203542018-09-17 22:56:37 -040019"""
20
khenaidoob9203542018-09-17 22:56:37 -040021import structlog
khenaidoo92e62c52018-10-03 14:02:54 -040022from twisted.internet.defer import DeferredQueue, inlineCallbacks, returnValue
khenaidoob9203542018-09-17 22:56:37 -040023
khenaidoo6fdf0ba2018-11-02 14:38:33 -040024from adapters.common.utils.asleep import asleep
khenaidoob9203542018-09-17 22:56:37 -040025from adapters.iadapter import OnuAdapter
26from adapters.protos import third_party
27from adapters.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
khenaidoo6fdf0ba2018-11-02 14:38:33 -040028from adapters.protos.core_adapter_pb2 import PortCapability, \
29 InterAdapterMessageType, InterAdapterResponseBody
khenaidoob9203542018-09-17 22:56:37 -040030from adapters.protos.device_pb2 import Port
31from adapters.protos.logical_device_pb2 import LogicalPort
32from adapters.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
33 OFPPF_1GB_FD
34from adapters.protos.openflow_13_pb2 import ofp_port
35from adapters.protos.ponsim_pb2 import FlowTable
khenaidoob9203542018-09-17 22:56:37 -040036
37_ = third_party
38log = structlog.get_logger()
39
40
41def mac_str_to_tuple(mac):
42 return tuple(int(d, 16) for d in mac.split(':'))
43
khenaidoo6fdf0ba2018-11-02 14:38:33 -040044
khenaidoob9203542018-09-17 22:56:37 -040045class PonSimOnuAdapter(OnuAdapter):
khenaidoo6fdf0ba2018-11-02 14:38:33 -040046 def __init__(self, core_proxy, adapter_proxy, config):
khenaidoob9203542018-09-17 22:56:37 -040047 # DeviceType of ONU should be same as VENDOR ID of ONU Serial Number as specified by standard
48 # requires for identifying correct adapter or ranged ONU
khenaidoo6fdf0ba2018-11-02 14:38:33 -040049 super(PonSimOnuAdapter, self).__init__(core_proxy=core_proxy,
50 adapter_proxy=adapter_proxy,
khenaidoob9203542018-09-17 22:56:37 -040051 config=config,
52 device_handler_class=PonSimOnuHandler,
53 name='ponsim_onu',
54 vendor='Voltha project',
55 version='0.4',
56 device_type='ponsim_onu',
57 vendor_id='PSMO',
58 accepts_bulk_flow_update=True,
59 accepts_add_remove_flow_updates=False)
60
61
62class PonSimOnuHandler(object):
63 def __init__(self, adapter, device_id):
64 self.adapter = adapter
khenaidoo6fdf0ba2018-11-02 14:38:33 -040065 self.core_proxy = adapter.core_proxy
66 self.adapter_proxy = adapter.adapter_proxy
khenaidoob9203542018-09-17 22:56:37 -040067 self.device_id = device_id
khenaidoo6fdf0ba2018-11-02 14:38:33 -040068 self.device_parent_id = None
khenaidoob9203542018-09-17 22:56:37 -040069 self.log = structlog.get_logger(device_id=device_id)
70 self.incoming_messages = DeferredQueue()
71 self.proxy_address = None
72 # reference of uni_port is required when re-enabling the device if
73 # it was disabled previously
74 self.uni_port = None
75 self.pon_port = None
76
77 def receive_message(self, msg):
78 self.incoming_messages.put(msg)
79
khenaidoob9203542018-09-17 22:56:37 -040080 @inlineCallbacks
81 def activate(self, device):
82 self.log.info('activating')
83
khenaidoo6fdf0ba2018-11-02 14:38:33 -040084 self.device_parent_id = device.parent_id
85 self.proxy_address = device.proxy_address
khenaidoob9203542018-09-17 22:56:37 -040086
87 # populate device info
88 device.root = False
89 device.vendor = 'ponsim'
90 device.model = 'n/a'
khenaidoo6fdf0ba2018-11-02 14:38:33 -040091 yield self.core_proxy.device_update(device)
khenaidoob9203542018-09-17 22:56:37 -040092
khenaidoo4d4802d2018-10-04 21:59:49 -040093 # register physical ports
khenaidoob9203542018-09-17 22:56:37 -040094 self.uni_port = Port(
95 port_no=2,
96 label='UNI facing Ethernet port',
97 type=Port.ETHERNET_UNI,
98 admin_state=AdminState.ENABLED,
99 oper_status=OperStatus.ACTIVE
100 )
101 self.pon_port = Port(
102 port_no=1,
103 label='PON port',
104 type=Port.PON_ONU,
105 admin_state=AdminState.ENABLED,
106 oper_status=OperStatus.ACTIVE,
107 peers=[
108 Port.PeerPort(
109 device_id=device.parent_id,
110 port_no=device.parent_port_no
111 )
112 ]
113 )
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400114 self.core_proxy.port_created(device.id, self.uni_port)
115 self.core_proxy.port_created(device.id, self.pon_port)
khenaidoob9203542018-09-17 22:56:37 -0400116
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400117 yield self.core_proxy.device_state_update(device.id,
118 connect_status=ConnectStatus.REACHABLE,
119 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400120
khenaidoo19d7b632018-10-30 10:49:50 -0400121 # TODO: Return only port specific info
khenaidoob9203542018-09-17 22:56:37 -0400122 def get_ofp_port_info(self, device, port_no):
123 # Since the adapter created the device port then it has the reference of the port to
khenaidoo19d7b632018-10-30 10:49:50 -0400124 # return the capability. TODO: Do a lookup on the UNI port number and return the
khenaidoob9203542018-09-17 22:56:37 -0400125 # appropriate attributes
126 self.log.info('get_ofp_port_info', port_no=port_no, device_id=device.id)
khenaidoob9203542018-09-17 22:56:37 -0400127 cap = OFPPF_1GB_FD | OFPPF_FIBER
128 return PortCapability(
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400129 port=LogicalPort(
khenaidoob9203542018-09-17 22:56:37 -0400130 ofp_port=ofp_port(
khenaidoob9203542018-09-17 22:56:37 -0400131 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
khenaidoob9203542018-09-17 22:56:37 -0400132 config=0,
133 state=OFPPS_LIVE,
134 curr=cap,
135 advertised=cap,
136 peer=cap,
137 curr_speed=OFPPF_1GB_FD,
138 max_speed=OFPPF_1GB_FD
khenaidoo19d7b632018-10-30 10:49:50 -0400139 ),
140 device_id=device.id,
141 device_port_no=port_no
khenaidoob9203542018-09-17 22:56:37 -0400142 )
143 )
144
khenaidoo92e62c52018-10-03 14:02:54 -0400145 @inlineCallbacks
146 def _get_uni_port(self):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400147 ports = yield self.core_proxy.get_ports(self.device_id,
148 Port.ETHERNET_UNI)
khenaidoo92e62c52018-10-03 14:02:54 -0400149 returnValue(ports)
150
151 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400152 def _get_pon_port(self):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400153 ports = yield self.core_proxy.get_ports(self.device_id, Port.PON_ONU)
khenaidoo92e62c52018-10-03 14:02:54 -0400154 returnValue(ports)
155
khenaidoob9203542018-09-17 22:56:37 -0400156 def reconcile(self, device):
157 self.log.info('reconciling-ONU-device-starts')
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400158 # TODO: complete code
khenaidoob9203542018-09-17 22:56:37 -0400159
160 @inlineCallbacks
161 def update_flow_table(self, flows):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400162 try:
163 self.log.info('update_flow_table', flows=flows)
164 # we need to proxy through the OLT to get to the ONU
khenaidoob9203542018-09-17 22:56:37 -0400165
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400166 # reset response queue
167 while self.incoming_messages.pending:
168 yield self.incoming_messages.get()
khenaidoob9203542018-09-17 22:56:37 -0400169
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400170 fb = FlowTable(
171 port=self.proxy_address.channel_id,
172 flows=flows
173 )
174 # Sends the request via proxy and wait for an ACK
175 yield self.adapter_proxy.send_inter_adapter_message(
176 msg=fb,
177 type=InterAdapterMessageType.FLOW_REQUEST,
178 from_adapter=self.adapter.name,
179 to_adapter=self.proxy_address.device_type,
180 to_device_id=self.device_id,
181 proxy_device_id=self.proxy_address.device_id
182 )
183 # Wait for the full response from the proxied adapter
184 res = yield self.incoming_messages.get()
185 self.log.info('response-received', result=res)
186 except Exception as e:
187 self.log.exception("update-flow-error", e=e)
khenaidoob9203542018-09-17 22:56:37 -0400188
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400189 def process_inter_adapter_message(self, msg):
190 self.log.info('process-inter-adapter-message', msg=msg)
191 if msg.header.type == InterAdapterMessageType.FLOW_RESPONSE:
192 body = InterAdapterResponseBody()
193 msg.body.Unpack(body)
194 self.log.info('received-response', status=body.success)
195 self.receive_message(msg)
khenaidoob9203542018-09-17 22:56:37 -0400196
197 def remove_from_flow_table(self, flows):
198 self.log.debug('remove-from-flow-table', flows=flows)
199 # TODO: Update PONSIM code to accept incremental flow changes.
200 # Once completed, the accepts_add_remove_flow_updates for this
201 # device type can be set to True
202
203 def add_to_flow_table(self, flows):
204 self.log.debug('add-to-flow-table', flows=flows)
205 # TODO: Update PONSIM code to accept incremental flow changes
206 # Once completed, the accepts_add_remove_flow_updates for this
207 # device type can be set to True
208
209 @inlineCallbacks
210 def reboot(self):
211 self.log.info('rebooting', device_id=self.device_id)
212
khenaidoo4d4802d2018-10-04 21:59:49 -0400213 # Update the connect status to UNREACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400214 yield self.core_proxy.device_state_update(self.device_id,
215 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400216
217 # Sleep 10 secs, simulating a reboot
218 # TODO: send alert and clear alert after the reboot
219 yield asleep(10)
220
khenaidoo4d4802d2018-10-04 21:59:49 -0400221 # Change the connection status back to REACHABLE. With a
222 # real ONU the connection state must be the actual state
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400223 yield self.core_proxy.device_state_update(self.device_id,
224 connect_status=ConnectStatus.REACHABLE)
khenaidoo4d4802d2018-10-04 21:59:49 -0400225
khenaidoob9203542018-09-17 22:56:37 -0400226 self.log.info('rebooted', device_id=self.device_id)
227
228 def self_test_device(self, device):
229 """
230 This is called to Self a device based on a NBI call.
231 :param device: A Voltha.Device object.
232 :return: Will return result of self test
233 """
234 log.info('self-test-device', device=device.id)
235 raise NotImplementedError()
236
khenaidoo92e62c52018-10-03 14:02:54 -0400237 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400238 def disable(self):
239 self.log.info('disabling', device_id=self.device_id)
240
khenaidoob9203542018-09-17 22:56:37 -0400241 # Update the device operational status to UNKNOWN
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400242 yield self.core_proxy.device_state_update(self.device_id,
243 oper_status=OperStatus.UNKNOWN,
244 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400245
246 # TODO:
247 # 1) Remove all flows from the device
248 # 2) Remove the device from ponsim
khenaidoo92e62c52018-10-03 14:02:54 -0400249 self.log.info('disabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400250
khenaidoo92e62c52018-10-03 14:02:54 -0400251 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400252 def reenable(self):
253 self.log.info('re-enabling', device_id=self.device_id)
254 try:
khenaidoob9203542018-09-17 22:56:37 -0400255
khenaidoo92e62c52018-10-03 14:02:54 -0400256 # Refresh the port reference - we only use one port for now
257 ports = yield self._get_uni_port()
258 self.log.info('re-enabling-uni-ports', ports=ports)
259 if ports.items:
260 self.uni_port = ports.items[0]
khenaidoob9203542018-09-17 22:56:37 -0400261
khenaidoo92e62c52018-10-03 14:02:54 -0400262 ports = yield self._get_pon_port()
263 self.log.info('re-enabling-pon-ports', ports=ports)
264 if ports.items:
265 self.pon_port = ports.items[0]
266
267 # Update the state of the UNI port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400268 yield self.core_proxy.port_state_update(self.device_id,
269 port_type=Port.ETHERNET_UNI,
270 port_no=self.uni_port.port_no,
271 oper_status=OperStatus.ACTIVE)
khenaidoo92e62c52018-10-03 14:02:54 -0400272
273 # Update the state of the PON port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400274 yield self.core_proxy.port_state_update(self.device_id,
275 port_type=Port.PON_ONU,
276 port_no=self.pon_port.port_no,
277 oper_status=OperStatus.ACTIVE)
khenaidoo92e62c52018-10-03 14:02:54 -0400278
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400279 yield self.core_proxy.device_state_update(self.device_id,
280 oper_status=OperStatus.ACTIVE,
281 connect_status=ConnectStatus.REACHABLE)
khenaidoo92e62c52018-10-03 14:02:54 -0400282
283 self.log.info('re-enabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400284 except Exception, e:
285 self.log.exception('error-reenabling', e=e)
286
287 def delete(self):
288 self.log.info('deleting', device_id=self.device_id)
289
khenaidoob9203542018-09-17 22:56:37 -0400290 # TODO:
291 # 1) Remove all flows from the device
292 # 2) Remove the device from ponsim
293
294 self.log.info('deleted', device_id=self.device_id)