blob: 4a0a323b8046ef7d5100ddaf3f191b5a51a49df6 [file] [log] [blame]
Zsolt Harasztied091602016-12-08 13:36:38 -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"""
18Tibit OLT device adapter
19"""
Zsolt Haraszti89a27302016-12-08 16:53:06 -080020import scapy
Zsolt Harasztied091602016-12-08 13:36:38 -080021import structlog
Zsolt Haraszti89a27302016-12-08 16:53:06 -080022from scapy.layers.inet import ICMP, IP
23from scapy.layers.l2 import Ether
24from twisted.internet.defer import DeferredQueue, inlineCallbacks
25from twisted.internet import reactor
26
Zsolt Harasztied091602016-12-08 13:36:38 -080027from zope.interface import implementer
28
Zsolt Haraszti89a27302016-12-08 16:53:06 -080029from common.frameio.frameio import BpfProgramFilter
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -080030from voltha.registry import registry
Zsolt Harasztied091602016-12-08 13:36:38 -080031from voltha.adapters.interface import IAdapterInterface
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -080032from voltha.core.logical_device_agent import mac_str_to_tuple
Zsolt Harasztied091602016-12-08 13:36:38 -080033from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
34from voltha.protos.device_pb2 import DeviceType, DeviceTypes
35from voltha.protos.health_pb2 import HealthStatus
Zsolt Haraszti89a27302016-12-08 16:53:06 -080036from voltha.protos.common_pb2 import LogLevel, ConnectStatus
37
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -080038from voltha.protos.common_pb2 import OperStatus, AdminState
39
40from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
41from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_10GB_FD, \
42 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
43 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
44
Zsolt Haraszti89a27302016-12-08 16:53:06 -080045from scapy.packet import Packet, bind_layers
46from scapy.fields import StrField
Zsolt Harasztied091602016-12-08 13:36:38 -080047
48log = structlog.get_logger()
49
50
Zsolt Haraszti89a27302016-12-08 16:53:06 -080051is_tibit_frame = BpfProgramFilter('ether[12:2] = 0x9001')
52
53# To be removed
54class TBJSON(Packet):
55 """ TBJSON 'packet' layer. """
56 name = "TBJSON"
57 fields_desc = [StrField("data", default="")]
58
Zsolt Harasztied091602016-12-08 13:36:38 -080059@implementer(IAdapterInterface)
60class TibitOltAdapter(object):
61
62 name = 'tibit_olt'
63
64 supported_device_types = [
65 DeviceType(
66 id='tibit_olt',
67 adapter=name,
68 accepts_bulk_flow_update=True
69 )
70 ]
71
72 def __init__(self, adapter_agent, config):
73 self.adapter_agent = adapter_agent
74 self.config = config
75 self.descriptor = Adapter(
76 id=self.name,
77 vendor='Tibit Communications Inc.',
78 version='0.1',
79 config=AdapterConfig(log_level=LogLevel.INFO)
80 )
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -080081 self.interface = registry('main').get_args().interface
Zsolt Haraszti89a27302016-12-08 16:53:06 -080082 self.io_port = None
83 self.incoming_queues = {} # mac_address -> DeferredQueue()
Zsolt Harasztied091602016-12-08 13:36:38 -080084
85 def start(self):
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -080086 log.debug('starting', interface=self.interface)
87 log.info('started', interface=self.interface)
Zsolt Harasztied091602016-12-08 13:36:38 -080088
89 def stop(self):
90 log.debug('stopping')
Zsolt Haraszti89a27302016-12-08 16:53:06 -080091 if self.io_port is not None:
92 registry('frameio').del_interface(self.interface)
Zsolt Harasztied091602016-12-08 13:36:38 -080093 log.info('stopped')
94
95 def adapter_descriptor(self):
96 return self.descriptor
97
98 def device_types(self):
99 return DeviceTypes(items=self.supported_device_types)
100
101 def health(self):
102 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
103
104 def change_master_state(self, master):
105 raise NotImplementedError()
106
107 def adopt_device(self, device):
108 log.info('adopt-device', device=device)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800109 self._activate_io_port()
110 reactor.callLater(0, self._launch_device_activation, device)
Zsolt Harasztied091602016-12-08 13:36:38 -0800111 return device
112
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800113 def _activate_io_port(self):
114 if self.io_port is None:
115 self.io_port = registry('frameio').add_interface(
116 self.interface, self._rcv_io, is_tibit_frame)
117
118 @inlineCallbacks
119 def _launch_device_activation(self, device):
120
121
122 try:
123 log.debug('launch_dev_activation')
124 # prepare receive queue
125 self.incoming_queues[device.mac_address] = DeferredQueue(size=100)
126
127 # send out ping to OLT device
128 olt_mac = device.mac_address
129 ping_frame = self._make_ping_frame(mac_address=olt_mac)
130 self.io_port.send(ping_frame)
131
132 # wait till we receive a response
133 # TODO add timeout mechanism so we can signal if we cannot reach device
134 while True:
135 response = yield self.incoming_queues[olt_mac].get()
136 # verify response and if not the expected response
137 if 1: # TODO check if it is really what we expect, and wait if not
138 break
139
140 except Exception, e:
141 log.exception('launch device failed', e=e)
142
143 # if we got response, we can fill out the device info, mark the device
144 # reachable
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800145
146 device.root = True
147 device.vendor = 'Tibit stuff'
148 device.model = 'n/a'
149 device.hardware_version = 'n/a'
150 device.firmware_version = 'n/a'
151 device.software_version = '1.0'
152 device.serial_number = 'add junk here'
153 device.connect_status = ConnectStatus.REACHABLE
154 self.adapter_agent.update_device(device)
155
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800156 # then shortly after we create some ports for the device
157 log.info('create-port')
158 nni_port = Port(
159 port_no=2,
160 label='NNI facing Ethernet port',
161 type=Port.ETHERNET_NNI,
162 admin_state=AdminState.ENABLED,
163 oper_status=OperStatus.ACTIVE
164 )
165 self.adapter_agent.add_port(device.id, nni_port)
166 self.adapter_agent.add_port(device.id, Port(
167 port_no=1,
168 label='PON port',
169 type=Port.PON_OLT,
170 admin_state=AdminState.ENABLED,
171 oper_status=OperStatus.ACTIVE
172 ))
173
174 log.info('create-logical-device')
175 # then shortly after we create the logical device with one port
176 # that will correspond to the NNI port
177 logical_device_id = uuid4().hex[:12]
178 ld = LogicalDevice(
179 id=logical_device_id,
180 datapath_id=int('0x' + logical_device_id[:8], 16), # from id
181 desc=ofp_desc(
182 mfr_desc=device.vendor,
183 hw_desc=jdev['results']['device'],
184 sw_desc=jdev['results']['firmware'],
185 serial_num=uuid4().hex,
186 dp_desc='n/a'
187 ),
188 switch_features=ofp_switch_features(
189 n_buffers=256, # TODO fake for now
190 n_tables=2, # TODO ditto
191 capabilities=( # TODO and ditto
192 OFPC_FLOW_STATS
193 | OFPC_TABLE_STATS
194 | OFPC_PORT_STATS
195 | OFPC_GROUP_STATS
196 )
197 ),
198 root_device_id=device.id
199 )
200 self.adapter_agent.create_logical_device(ld)
201 cap = OFPPF_10GB_FD | OFPPF_FIBER
202 self.adapter_agent.add_logical_port(ld.id, LogicalPort(
203 id='nni',
204 ofp_port=ofp_port(
205 port_no=129,
206 hw_addr=mac_str_to_tuple(device.mac_address),
207 name='nni',
208 config=0,
209 state=OFPPS_LIVE,
210 curr=cap,
211 advertised=cap,
212 peer=cap,
213 curr_speed=OFPPF_10GB_FD,
214 max_speed=OFPPF_10GB_FD
215 ),
216 device_id=device.id,
217 device_port_no=nni_port.port_no,
218 root_port=True
219 ))
220
221 # and finally update to active
222 device = self.adapter_agent.get_device(device.id)
223 device.parent_id = ld.id
224 device.oper_status = OperStatus.ACTIVE
225 self.adapter_agent.update_device(device)
226
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800227 def _rcv_io(self, port, frame):
228
229 log.info('frame-recieved')
230
231 # extract source mac
232 response = Ether(frame)
233
234 # enqueue incoming parsed frame to rigth device
235 self.incoming_queues[response.src].put(response)
236
237 def _make_ping_frame(self, mac_address):
238 # TODO Nathan to make this to be an actual OLT ping
239 # Create a json packet
240 json_operation_str = '{\"operation\":\"version\"}'
241 frame = Ether()/TBJSON(data='json %s' % json_operation_str)
242 frame.type = int("9001", 16)
243 frame.dst = '00:0c:e2:31:25:00'
244 bind_layers(Ether, TBJSON, type=0x9001)
245 frame.show()
246 return str(frame)
247
Zsolt Harasztied091602016-12-08 13:36:38 -0800248 def abandon_device(self, device):
249 raise NotImplementedError(0
250 )
251 def deactivate_device(self, device):
252 raise NotImplementedError()
253
254 def update_flows_bulk(self, device, flows, groups):
255 log.debug('bulk-flow-update', device_id=device.id,
256 flows=flows, groups=groups)
257
258 def update_flows_incrementally(self, device, flow_changes, group_changes):
259 raise NotImplementedError()
260
261 def send_proxied_message(self, proxy_address, msg):
262 log.debug('send-proxied-message',
263 proxy_address=proxy_address, msg=msg)
264
265 def receive_proxied_message(self, proxy_address, msg):
266 raise NotImplementedError()
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800267