blob: 4ddeeb52b897de09a0b18a89e25c6d71c080e364 [file] [log] [blame]
Zsolt Haraszticc153aa2016-12-14 02:28:59 -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"""
18Mock device adapter for testing.
19"""
20from uuid import uuid4
21
22import structlog
23from twisted.internet import reactor
24from twisted.internet.defer import inlineCallbacks, DeferredQueue
25from zope.interface import implementer
26
27from common.utils.asleep import asleep
28from voltha.adapters.interface import IAdapterInterface
29from voltha.core.logical_device_agent import mac_str_to_tuple
30from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
31from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port
32from voltha.protos.health_pb2 import HealthStatus
33from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
34 AdminState
35from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
36from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_1GB_FD, \
37 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
38 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
39
40log = structlog.get_logger()
41
42
43@implementer(IAdapterInterface)
44class BroadcomOnuAdapter(object):
45
46 name = 'broadcom_onu'
47
48 supported_device_types = [
49 DeviceType(
50 id='broadcom_onu',
51 adapter=name,
52 accepts_bulk_flow_update=True
53 )
54 ]
55
56 def __init__(self, adapter_agent, config):
57 self.adapter_agent = adapter_agent
58 self.config = config
59 self.descriptor = Adapter(
60 id=self.name,
61 vendor='Voltha project',
62 version='0.1',
63 config=AdapterConfig(log_level=LogLevel.INFO)
64 )
65 self.incoming_messages = DeferredQueue()
66
67 def start(self):
68 log.debug('starting')
69 log.info('started')
70
71 def stop(self):
72 log.debug('stopping')
73 log.info('stopped')
74
75 def adapter_descriptor(self):
76 return self.descriptor
77
78 def device_types(self):
79 return DeviceTypes(items=self.supported_device_types)
80
81 def health(self):
82 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
83
84 def change_master_state(self, master):
85 raise NotImplementedError()
86
87 def adopt_device(self, device):
88 # We kick of a simulated activation scenario
89 reactor.callLater(0.2, self._simulate_device_activation, device)
90 return device
91
92 def abandon_device(self, device):
93 raise NotImplementedError()
94
95 def deactivate_device(self, device):
96 raise NotImplementedError()
97
98 @inlineCallbacks
99 def _simulate_device_activation(self, device):
100
101 # first we verify that we got parent reference and proxy info
102 assert device.parent_id
103 assert device.proxy_address.device_id
104 assert device.proxy_address.channel_id
105
106 # we pretend that we were able to contact the device and obtain
107 # additional information about it
108 device.vendor = 'Broadcom'
109 device.model = 'to be filled'
110 device.hardware_version = 'to be filled'
111 device.firmware_version = 'to be filled'
112 device.software_version = 'to be filled'
113 device.serial_number = uuid4().hex
114 device.connect_status = ConnectStatus.REACHABLE
115 self.adapter_agent.update_device(device)
116
117 # then shortly after we create some ports for the device
118 yield asleep(0.05)
119 uni_port = Port(
120 port_no=0,
121 label='UNI facing Ethernet port',
122 type=Port.ETHERNET_UNI,
123 admin_state=AdminState.ENABLED,
124 oper_status=OperStatus.ACTIVE
125 )
126 self.adapter_agent.add_port(device.id, uni_port)
127 self.adapter_agent.add_port(device.id, Port(
128 port_no=1,
129 label='PON port',
130 type=Port.PON_ONU,
131 admin_state=AdminState.ENABLED,
132 oper_status=OperStatus.ACTIVE,
133 peers=[
134 Port.PeerPort(
135 device_id=device.parent_id,
136 port_no=device.parent_port_no
137 )
138 ]
139 ))
140
141 # TODO adding vports to the logical device shall be done by agent?
142 # then we create the logical device port that corresponds to the UNI
143 # port of the device
144 yield asleep(0.05)
145
146 # obtain logical device id
147 parent_device = self.adapter_agent.get_device(device.parent_id)
148 logical_device_id = parent_device.parent_id
149 assert logical_device_id
150
151 # we are going to use the proxy_address.channel_id as unique number
152 # and name for the virtual ports, as this is guaranteed to be unique
153 # in the context of the OLT port, so it is also unique in the context
154 # of the logical device
155 port_no = device.proxy_address.channel_id
156 cap = OFPPF_1GB_FD | OFPPF_FIBER
157 self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
158 id=str(port_no),
159 ofp_port=ofp_port(
160 port_no=port_no,
161 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
162 name='uni-{}'.format(port_no),
163 config=0,
164 state=OFPPS_LIVE,
165 curr=cap,
166 advertised=cap,
167 peer=cap,
168 curr_speed=OFPPF_1GB_FD,
169 max_speed=OFPPF_1GB_FD
170 ),
171 device_id=device.id,
172 device_port_no=uni_port.port_no
173 ))
174
175 # simulate a proxied message sending and receving a reply
176 reply = yield self._simulate_message_exchange(device)
177
178 # and finally update to "ACTIVE"
179 device = self.adapter_agent.get_device(device.id)
180 device.oper_status = OperStatus.ACTIVE
181 self.adapter_agent.update_device(device)
182
183 def update_flows_bulk(self, device, flows, groups):
184 log.debug('bulk-flow-update', device_id=device.id,
185 flows=flows, groups=groups)
186
187 def update_flows_incrementally(self, device, flow_changes, group_changes):
188 raise NotImplementedError()
189
190 def send_proxied_message(self, proxy_address, msg):
191 raise NotImplementedError()
192
193 def receive_proxied_message(self, proxy_address, msg):
194 # just place incoming message to a list
195 self.incoming_messages.put((proxy_address, msg))
196
197 @inlineCallbacks
198 def _simulate_message_exchange(self, device):
199
200 # register for receiving async messages
201 self.adapter_agent.register_for_proxied_messages(device.proxy_address)
202
203 # reset incoming message queue
204 while self.incoming_messages.pending:
205 _ = yield self.incoming_messages.get()
206
207 # construct message
208 msg = 'test message'
209
210 # send message
211 self.adapter_agent.send_proxied_message(device.proxy_address, msg)
212
213 # wait till we detect incoming message
214 yield self.incoming_messages.get()
215
216 # by returning we allow the device to be shown as active, which
217 # indirectly verified that message passing works