blob: d416db71d0e2ea5a41204285666afa222c664d20 [file] [log] [blame]
Zsolt Haraszti656ecc62016-12-28 15:08:23 -08001#
2# Copyright 2016 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"""
18Fully simulated OLT/ONU adapter.
19"""
20
21import structlog
22from twisted.internet import reactor
23from twisted.internet.defer import DeferredQueue, inlineCallbacks
24from zope.interface import implementer
25
26from voltha.adapters.interface import IAdapterInterface
27from voltha.core.logical_device_agent import mac_str_to_tuple
28from voltha.protos import third_party
29from voltha.protos.adapter_pb2 import Adapter
30from voltha.protos.adapter_pb2 import AdapterConfig
31from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
32 AdminState
33from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port
34from voltha.protos.health_pb2 import HealthStatus
35from voltha.protos.logical_device_pb2 import LogicalPort
36from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPF_1GB_FD
37from voltha.protos.openflow_13_pb2 import ofp_port
38from voltha.protos.ponsim_pb2 import FlowTable
39
40_ = third_party
41log = structlog.get_logger()
42
43
44@implementer(IAdapterInterface)
45class PonSimOnuAdapter(object):
46
47 name = 'ponsim_onu'
48
49 supported_device_types = [
50 DeviceType(
51 id=name,
52 adapter=name,
53 accepts_bulk_flow_update=True
54 )
55 ]
56
57 def __init__(self, adapter_agent, config):
58 self.adapter_agent = adapter_agent
59 self.config = config
60 self.descriptor = Adapter(
61 id=self.name,
62 vendor='Voltha project',
63 version='0.4',
64 config=AdapterConfig(log_level=LogLevel.INFO)
65 )
66 self.devices_handlers = dict() # device_id -> PonSimOltHandler()
67
68 def start(self):
69 log.debug('starting')
70 log.info('started')
71
72 def stop(self):
73 log.debug('stopping')
74 log.info('stopped')
75
76 def adapter_descriptor(self):
77 return self.descriptor
78
79 def device_types(self):
80 return DeviceTypes(items=self.supported_device_types)
81
82 def health(self):
83 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
84
85 def change_master_state(self, master):
86 raise NotImplementedError()
87
88 def adopt_device(self, device):
89 self.devices_handlers[device.id] = PonSimOnuHandler(self, device.id)
90 reactor.callLater(0, self.devices_handlers[device.id].activate, device)
91 return device
92
93 def abandon_device(self, device):
94 raise NotImplementedError()
95
96 def deactivate_device(self, device):
97 raise NotImplementedError()
98
99 def update_flows_bulk(self, device, flows, groups):
100 log.info('bulk-flow-update', device_id=device.id,
101 flows=flows, groups=groups)
102 assert len(groups.items) == 0
103 handler = self.devices_handlers[device.id]
104 return handler.update_flow_table(flows.items)
105
106 def update_flows_incrementally(self, device, flow_changes, group_changes):
107 raise NotImplementedError()
108
109 def send_proxied_message(self, proxy_address, msg):
110 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
111
112 def receive_proxied_message(self, proxy_address, msg):
113 log.info('receive-proxied-message', proxy_address=proxy_address,
114 device_id=proxy_address.device_id, msg=msg)
115 handler = self.devices_handlers[proxy_address.device_id]
116 handler.receive_message(msg)
117
118 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
119 log.info('packet-out', logical_device_id=logical_device_id,
120 egress_port_no=egress_port_no, msg_len=len(msg))
121
122
123class PonSimOnuHandler(object):
124
125 def __init__(self, adapter, device_id):
126 self.adapter = adapter
127 self.adapter_agent = adapter.adapter_agent
128 self.device_id = device_id
129 self.log = structlog.get_logger(device_id=device_id)
130 self.incoming_messages = DeferredQueue()
131 self.proxy_address = None
132
133 def receive_message(self, msg):
134 self.incoming_messages.put(msg)
135
136 def activate(self, device):
137 self.log.info('activating')
138
139 # first we verify that we got parent reference and proxy info
140 assert device.parent_id
141 assert device.proxy_address.device_id
142 assert device.proxy_address.channel_id
143
144 # register for proxied messages right away
145 self.proxy_address = device.proxy_address
146 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
147
148 # populate device info
149 device.root = True
150 device.vendor = 'ponsim'
151 device.model ='n/a'
152 device.connect_status = ConnectStatus.REACHABLE
153 self.adapter_agent.update_device(device)
154
155 # register physical ports
156 uni_port = Port(
157 port_no=2,
158 label='UNI facing Ethernet port',
159 type=Port.ETHERNET_UNI,
160 admin_state=AdminState.ENABLED,
161 oper_status=OperStatus.ACTIVE
162 )
163 self.adapter_agent.add_port(device.id, uni_port)
164 self.adapter_agent.add_port(device.id, Port(
165 port_no=1,
166 label='PON port',
167 type=Port.PON_ONU,
168 admin_state=AdminState.ENABLED,
169 oper_status=OperStatus.ACTIVE,
170 peers=[
171 Port.PeerPort(
172 device_id=device.parent_id,
173 port_no=device.parent_port_no
174 )
175 ]
176 ))
177
178 # add uni port to logical device
179 parent_device = self.adapter_agent.get_device(device.parent_id)
180 logical_device_id = parent_device.parent_id
181 assert logical_device_id
182 port_no = device.proxy_address.channel_id
183 cap = OFPPF_1GB_FD | OFPPF_FIBER
184 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
185 id='uni-{}'.format(port_no),
186 ofp_port=ofp_port(
187 port_no=port_no,
188 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
189 name='uni-{}'.format(port_no),
190 config=0,
191 state=OFPPS_LIVE,
192 curr=cap,
193 advertised=cap,
194 peer=cap,
195 curr_speed=OFPPF_1GB_FD,
196 max_speed=OFPPF_1GB_FD
197 ),
198 device_id=device.id,
199 device_port_no=uni_port.port_no
200 ))
201
202 device = self.adapter_agent.get_device(device.id)
203 device.oper_status = OperStatus.ACTIVE
204 self.adapter_agent.update_device(device)
205
206 @inlineCallbacks
207 def update_flow_table(self, flows):
208
209 # we need to proxy through the OLT to get to the ONU
210
211 # reset response queue
212 while self.incoming_messages.pending:
213 yield self.incoming_messages.get()
214
215 msg = FlowTable(
216 port=self.proxy_address.channel_id,
217 flows=flows
218 )
219 self.adapter_agent.send_proxied_message(self.proxy_address, msg)
220
221 yield self.incoming_messages.get()