blob: 914c47bffc3b9164651e8ec135b3adcc324b55f7 [file] [log] [blame]
Zsolt Harasztied091602016-12-08 13:36:38 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Harasztied091602016-12-08 13:36:38 -08003#
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 Haraszti4ef0a9a2016-12-20 01:35:48 -080020import json
Paul Graybb82fe62017-03-30 04:22:39 -070021import time
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080022from uuid import uuid4
Paul Graybb82fe62017-03-30 04:22:39 -070023import struct
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080024
Zsolt Harasztic5f740b2017-01-18 09:53:17 -080025import arrow
Zsolt Haraszti80175202016-12-24 00:17:51 -080026import structlog
27from scapy.fields import StrField
Zsolt Haraszti348d1932016-12-10 01:10:07 -080028from scapy.layers.l2 import Ether, Dot1Q
Zsolt Haraszti80175202016-12-24 00:17:51 -080029from scapy.packet import Packet, bind_layers
Zsolt Haraszti89a27302016-12-08 16:53:06 -080030from twisted.internet import reactor
Zsolt Haraszti80175202016-12-24 00:17:51 -080031from twisted.internet.defer import DeferredQueue, inlineCallbacks
Zsolt Harasztic5f740b2017-01-18 09:53:17 -080032from twisted.internet.task import LoopingCall
Zsolt Harasztied091602016-12-08 13:36:38 -080033from zope.interface import implementer
34
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080035from common.frameio.frameio import BpfProgramFilter, hexify
Zsolt Harasztied091602016-12-08 13:36:38 -080036from voltha.adapters.interface import IAdapterInterface
Nathan Knuth31c36962016-12-27 10:04:49 -080037from voltha.extensions.eoam.EOAM import EOAMPayload, DPoEOpcode_SetRequest
38from voltha.extensions.eoam.EOAM_TLV import DOLTObject, \
Nathan Knuthd8285e62017-01-11 14:18:43 -060039 NetworkToNetworkPortObject, OLTUnicastLogicalLink, \
40 PortIngressRuleClauseMatchLength01, AddStaticMacAddress, \
41 PortIngressRuleClauseMatchLength02, PortIngressRuleResultForward, \
42 PortIngressRuleResultSet, PortIngressRuleResultInsert, \
43 PortIngressRuleResultCopy, PortIngressRuleResultReplace, \
44 PortIngressRuleResultDelete, PortIngressRuleResultOLTQueue, \
Nathan Knuth05f859e2017-02-23 13:22:26 -080045 PortIngressRuleResultOLTBroadcastQueue, \
Nathan Knuthb396f472017-03-28 17:18:51 -070046 PortIngressRuleTerminator, AddPortIngressRule, CablelabsOUI, \
47 ItuOUI, PonPortObject
Nathan Knuth31c36962016-12-27 10:04:49 -080048from voltha.extensions.eoam.EOAM_TLV import PortIngressRuleHeader
Nathan Knuthd390ceb2017-01-07 15:38:58 -080049from voltha.extensions.eoam.EOAM_TLV import ClauseSubtypeEnum
50from voltha.extensions.eoam.EOAM_TLV import RuleOperatorEnum
Paul Graybb82fe62017-03-30 04:22:39 -070051from voltha.extensions.eoam.EOAM_TLV import DPoEVariableResponseCodes
52from voltha.extensions.eoam.EOAM_TLV import TibitOUI
Nathan Knuth09c23102017-02-10 04:43:30 -080053from voltha.extensions.eoam.EOAM import EOAMPayload, CablelabsOUI
Zsolt Haraszti80175202016-12-24 00:17:51 -080054from voltha.core.flow_decomposer import *
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -080055from voltha.core.logical_device_agent import mac_str_to_tuple
Zsolt Harasztied091602016-12-08 13:36:38 -080056from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
Zsolt Haraszti80175202016-12-24 00:17:51 -080057from voltha.protos.common_pb2 import LogLevel, ConnectStatus
58from voltha.protos.common_pb2 import OperStatus, AdminState
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080059from voltha.protos.device_pb2 import Device, Port
Zsolt Harasztied091602016-12-08 13:36:38 -080060from voltha.protos.device_pb2 import DeviceType, DeviceTypes
Zsolt Harasztic5f740b2017-01-18 09:53:17 -080061from voltha.protos.events_pb2 import KpiEvent, MetricValuePairs
62from voltha.protos.events_pb2 import KpiEventType
Zsolt Harasztied091602016-12-08 13:36:38 -080063from voltha.protos.health_pb2 import HealthStatus
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -080064from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
65from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_10GB_FD, \
Nathan Knuthd8285e62017-01-11 14:18:43 -060066 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
67 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
Zsolt Haraszti80175202016-12-24 00:17:51 -080068from voltha.registry import registry
Zsolt Haraszti348d1932016-12-10 01:10:07 -080069log = structlog.get_logger()
Zsolt Harasztied091602016-12-08 13:36:38 -080070
Nathan Knuth388eff32017-01-18 18:31:22 -060071TIBIT_ONU_LINK_INDEX = 2
72
Nathan Knuth6e57f332016-12-22 15:49:20 -080073# Match on the MGMT VLAN, Priority 7
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080074TIBIT_MGMT_VLAN = 4090
75TIBIT_MGMT_PRIORITY = 7
76frame_match_case1 = 'ether[14:2] = 0x{:01x}{:03x}'.format(
77 TIBIT_MGMT_PRIORITY << 1, TIBIT_MGMT_VLAN)
78
79TIBIT_PACKET_IN_VLAN = 4000
80frame_match_case2 = '(ether[14:2] & 0xfff) = 0x{:03x}'.format(
81 TIBIT_PACKET_IN_VLAN)
82
Nathan Knuthfe2b2e02017-01-06 07:29:02 -080083TIBIT_PACKET_OUT_VLAN = 4000
84
Paul Graybb82fe62017-03-30 04:22:39 -070085TIBIT_MSG_WAIT_TIME = 3
86
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080087is_tibit_frame = BpfProgramFilter('{} or {}'.format(
88 frame_match_case1, frame_match_case2))
89
Paul Graybb82fe62017-03-30 04:22:39 -070090### Received OAM Message Types
91RxedOamMsgTypeEnum = {
92 "Unknown": 0x00,
93 # Info PDU - not currently used
94 "Info": 0x01,
95 # Event Notification - Tibit or DPoE Event
96 "Event Notification": 0x02,
97 "DPoE Get Response": 0x03,
98 "DPoE Set Response": 0x04,
99 # Specifically - a File Transfer ACK
100 "DPoE File Transfer": 0x05,
101 # Contains an embedded OMCI message
102 "OMCI Message": 0x06,
103 }
Nathan Knuth05f859e2017-02-23 13:22:26 -0800104
105# TODO: This information should be conveyed to the adapter
106# from a higher level.
107MULTICAST_VLAN = 140
108
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800109
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800110# Extract OLT MAC address: This is a good
111# example of getting the OLT mac address
112
113#for mac, device in self.device_ids.iteritems():
114# if device == dev_id:
115# olt_mac_address = mac
116# log.info('packet-out', olt_mac_address=olt_mac_address)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800117
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800118# To be removed in favor of OAM
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800119class TBJSON(Packet):
120 """ TBJSON 'packet' layer. """
121 name = "TBJSON"
122 fields_desc = [StrField("data", default="")]
123
Paul Graya107c4d2017-03-16 13:57:58 -0400124bind_layers(Ether, TBJSON, type=0xA8C8)
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800125
Nathan Knuthb396f472017-03-28 17:18:51 -0700126TIBIT_COMMUNICATIONS_OUI=u'000CE2'
127SUMITOMO_ELECTRIC_INDUSTRIES_OUI=u'0025DC'
128
129ADTRAN_SHORTENED_VSSN=u'4144' # 'AD'
130TIBIT_SHORTENED_VSSN=u'5442' # 'TB'
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800131
Zsolt Harasztied091602016-12-08 13:36:38 -0800132@implementer(IAdapterInterface)
133class TibitOltAdapter(object):
134
135 name = 'tibit_olt'
136
137 supported_device_types = [
138 DeviceType(
139 id='tibit_olt',
140 adapter=name,
141 accepts_bulk_flow_update=True
142 )
143 ]
144
145 def __init__(self, adapter_agent, config):
146 self.adapter_agent = adapter_agent
147 self.config = config
148 self.descriptor = Adapter(
149 id=self.name,
150 vendor='Tibit Communications Inc.',
151 version='0.1',
152 config=AdapterConfig(log_level=LogLevel.INFO)
153 )
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -0800154 self.interface = registry('main').get_args().interface
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800155 self.io_port = None
Nathan Knuth6e57f332016-12-22 15:49:20 -0800156 self.incoming_queues = {} # OLT mac_address -> DeferredQueue()
157 self.device_ids = {} # OLT mac_address -> device_id
Nathan Knuth96531582017-02-13 05:27:37 -0800158 self.vlan_to_device_ids = {} # c-vid -> (device_id, logical_device_id, mac_address)
Zsolt Harasztied091602016-12-08 13:36:38 -0800159
160 def start(self):
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -0800161 log.debug('starting', interface=self.interface)
162 log.info('started', interface=self.interface)
Zsolt Harasztied091602016-12-08 13:36:38 -0800163
164 def stop(self):
165 log.debug('stopping')
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800166 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800167 registry('frameio').close_port(self.io_port)
Zsolt Harasztied091602016-12-08 13:36:38 -0800168 log.info('stopped')
169
170 def adapter_descriptor(self):
171 return self.descriptor
172
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500173 def update_pm_config(self, device, pm_configs):
174 raise NotImplementedError()
175
Zsolt Harasztied091602016-12-08 13:36:38 -0800176 def device_types(self):
177 return DeviceTypes(items=self.supported_device_types)
178
179 def health(self):
180 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
181
182 def change_master_state(self, master):
183 raise NotImplementedError()
184
185 def adopt_device(self, device):
186 log.info('adopt-device', device=device)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800187 self._activate_io_port()
188 reactor.callLater(0, self._launch_device_activation, device)
Zsolt Harasztied091602016-12-08 13:36:38 -0800189
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800190 def _activate_io_port(self):
191 if self.io_port is None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800192 self.io_port = registry('frameio').open_port(
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800193 self.interface, self._rcv_io, is_tibit_frame)
194
195 @inlineCallbacks
196 def _launch_device_activation(self, device):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800197 try:
198 log.debug('launch_dev_activation')
199 # prepare receive queue
200 self.incoming_queues[device.mac_address] = DeferredQueue(size=100)
201
Nathan Knuth6e57f332016-12-22 15:49:20 -0800202 # add mac_address to device_ids table
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800203 olt_mac = device.mac_address
Nathan Knuth6e57f332016-12-22 15:49:20 -0800204 self.device_ids[olt_mac] = device.id
205
206 # send out ping to OLT device
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800207 ping_frame = self._make_ping_frame(mac_address=olt_mac)
208 self.io_port.send(ping_frame)
209
210 # wait till we receive a response
Nathan Knuth6e57f332016-12-22 15:49:20 -0800211 ## TODO add timeout mechanism so we can signal if we cannot reach
212 ##device
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800213 while True:
214 response = yield self.incoming_queues[olt_mac].get()
215 # verify response and if not the expected response
216 if 1: # TODO check if it is really what we expect, and wait if not
217 break
218
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800219 except Exception as e:
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800220 log.exception('launch device failed', e=e)
221
222 # if we got response, we can fill out the device info, mark the device
223 # reachable
Zsolt Haraszti80175202016-12-24 00:17:51 -0800224 jdev = json.loads(response.payload.payload.body.load)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800225 device.root = True
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800226 device.vendor = 'Tibit Communications, Inc.'
Nathan Knuth6e57f332016-12-22 15:49:20 -0800227 device.model = jdev.get('results', {}).get('device', 'DEVICE_UNKNOWN')
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800228 device.hardware_version = jdev['results']['datecode']
229 device.firmware_version = jdev['results']['firmware']
230 device.software_version = jdev['results']['modelversion']
231 device.serial_number = jdev['results']['manufacturer']
Nathan Knuth6e57f332016-12-22 15:49:20 -0800232
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800233 device.connect_status = ConnectStatus.REACHABLE
234 self.adapter_agent.update_device(device)
235
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800236 # then shortly after we create some ports for the device
237 log.info('create-port')
238 nni_port = Port(
239 port_no=2,
240 label='NNI facing Ethernet port',
241 type=Port.ETHERNET_NNI,
242 admin_state=AdminState.ENABLED,
243 oper_status=OperStatus.ACTIVE
244 )
245 self.adapter_agent.add_port(device.id, nni_port)
246 self.adapter_agent.add_port(device.id, Port(
247 port_no=1,
248 label='PON port',
249 type=Port.PON_OLT,
250 admin_state=AdminState.ENABLED,
251 oper_status=OperStatus.ACTIVE
252 ))
253
254 log.info('create-logical-device')
255 # then shortly after we create the logical device with one port
256 # that will correspond to the NNI port
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800257 ld = LogicalDevice(
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800258 desc=ofp_desc(
259 mfr_desc=device.vendor,
260 hw_desc=jdev['results']['device'],
261 sw_desc=jdev['results']['firmware'],
262 serial_num=uuid4().hex,
263 dp_desc='n/a'
264 ),
265 switch_features=ofp_switch_features(
266 n_buffers=256, # TODO fake for now
267 n_tables=2, # TODO ditto
268 capabilities=( # TODO and ditto
269 OFPC_FLOW_STATS
270 | OFPC_TABLE_STATS
271 | OFPC_PORT_STATS
272 | OFPC_GROUP_STATS
273 )
274 ),
275 root_device_id=device.id
276 )
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800277 ld_initialized = self.adapter_agent.create_logical_device(ld)
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800278 cap = OFPPF_10GB_FD | OFPPF_FIBER
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800279 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800280 id='nni',
281 ofp_port=ofp_port(
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800282 port_no=0,
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800283 hw_addr=mac_str_to_tuple(device.mac_address),
284 name='nni',
285 config=0,
286 state=OFPPS_LIVE,
287 curr=cap,
288 advertised=cap,
289 peer=cap,
290 curr_speed=OFPPF_10GB_FD,
291 max_speed=OFPPF_10GB_FD
292 ),
293 device_id=device.id,
294 device_port_no=nni_port.port_no,
295 root_port=True
296 ))
297
298 # and finally update to active
299 device = self.adapter_agent.get_device(device.id)
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800300 device.parent_id = ld_initialized.id
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800301 device.oper_status = OperStatus.ACTIVE
302 self.adapter_agent.update_device(device)
303
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800304 # Just transitioned to ACTIVE, wait a tenth of second
305 # before checking for ONUs
306 reactor.callLater(0.1, self._detect_onus, device)
307
308 @inlineCallbacks
309 def _detect_onus(self, device):
310 # send out get 'links' to the OLT device
311 olt_mac = device.mac_address
312 links_frame = self._make_links_frame(mac_address=olt_mac)
313 self.io_port.send(links_frame)
314 while True:
315 response = yield self.incoming_queues[olt_mac].get()
316 # verify response and if not the expected response
317 if 1: # TODO check if it is really what we expect, and wait if not
318 break
319
Zsolt Haraszti80175202016-12-24 00:17:51 -0800320 jdev = json.loads(response.payload.payload.body.load)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600321 onu_mac = ''
Nathan Knuthb396f472017-03-28 17:18:51 -0700322 child_device_name = ''
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800323 for macid in jdev['results']:
324 if macid['macid'] is None:
325 log.info('MAC ID is NONE %s' % str(macid['macid']))
Nathan Knuthd8285e62017-01-11 14:18:43 -0600326 elif macid['macid'][:6].upper() == SUMITOMO_ELECTRIC_INDUSTRIES_OUI:
Nathan Knuthb396f472017-03-28 17:18:51 -0700327 onu_mac_string = macid['macid']
Nathan Knuthd8285e62017-01-11 14:18:43 -0600328 log.info('SUMITOMO mac address %s' % str(macid['macid']))
Nathan Knuthb396f472017-03-28 17:18:51 -0700329 child_device_name = 'dpoe_onu'
330
331 elif macid['macid'][:4].upper() == ADTRAN_SHORTENED_VSSN:
332 onu_mac_string = macid['macid']
333 log.info('ADTRAN mac address %s' % str(macid['macid']))
334 child_device_name = 'adtran_onu'
Nathan Knuthd8285e62017-01-11 14:18:43 -0600335
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800336 else:
Nathan Knuth09c23102017-02-10 04:43:30 -0800337 onu_mac_string = '000c' + macid.get('macid', 'e2000000')[4:]
Nathan Knuthb396f472017-03-28 17:18:51 -0700338 log.info('TIBIT mac address %s' % onu_mac)
339 child_device_name = 'tibit_onu'
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800340
Nathan Knuthb396f472017-03-28 17:18:51 -0700341 # Convert from string to colon separated form
342 onu_mac = ':'.join(s.encode('hex') for s in onu_mac_string.decode('hex'))
343 log.info('activate-olt-for-onu-%s' % onu_mac)
344 mac_octet_4 = int(macid['macid'][-4:-2], 16)
345 vlan_id = self._olt_side_onu_activation(mac_octet_4)
346 self.adapter_agent.child_device_detected(
347 parent_device_id=device.id,
348 parent_port_no=1,
349 child_device_type=child_device_name,
350 mac_address = onu_mac,
351 proxy_address=Device.ProxyAddress(
352 device_id=device.id,
353 channel_id=vlan_id
354 ),
355 vlan=vlan_id
356 )
Nathan Knuth09c23102017-02-10 04:43:30 -0800357
Nathan Knuthb396f472017-03-28 17:18:51 -0700358 ## Automatically setup default downstream control frames flow (in this case VLAN 4000)
359 ## on the OLT for the new ONU/ONT device
360 Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()}
361 Operator = {v: k for k, v in RuleOperatorEnum.iteritems()}
362 packet_out_rule = (
363 Ether(dst=device.mac_address) /
364 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
365 EOAMPayload(
Paul Graybb82fe62017-03-30 04:22:39 -0700366 body=TibitOUI() / DPoEOpcode_SetRequest() /
Nathan Knuthb396f472017-03-28 17:18:51 -0700367 NetworkToNetworkPortObject()/
368 PortIngressRuleHeader(precedence=13)/
369 PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
370 operator=Operator['=='],
371 match=TIBIT_PACKET_OUT_VLAN)/
372 PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=1,
373 operator=Operator['=='], match=vlan_id)/
374 PortIngressRuleResultOLTQueue(unicastvssn="TBIT", unicastlink=int(onu_mac_string[4:], 16))/
375 PortIngressRuleResultForward()/
376 PortIngressRuleResultDelete(fieldcode=Clause['C-VLAN Tag'])/
377 PortIngressRuleTerminator()/
378 AddPortIngressRule()))
379
380 self.io_port.send(str(packet_out_rule))
Paul Graybb82fe62017-03-30 04:22:39 -0700381
382 # Get and process the Set Response
383 ack = False
384 start_time = time.time()
385
386 # Loop until we have a set response or timeout
387 while not ack:
388 frame = yield self.incoming_queues[olt_mac].get()
389 #TODO - Need to add propoer timeout functionality
390 #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
391 # break # don't wait forever
392
393 respType = self._voltha_get_oam_msg_type(frame)
394 log.info('Received OAM Message 0x %s' % str(respType))
395
396 #Check that the message received is a Set Response
397 if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
398 ack = True
399 else:
400 # Handle unexpected events/OMCI messages
401 self._voltha_check_resp(frame)
402
403 # Verify Set Response
404 if ack:
405 (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
406 if (rc == True):
407 log.info('Set Response had no errors')
408 else:
409 raise Exception('Set Respose had errors')
410 log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
Nathan Knuth09c23102017-02-10 04:43:30 -0800411
Nathan Knuth388eff32017-01-18 18:31:22 -0600412 # also record the vlan_id -> (device_id, logical_device_id, linkid) for
413 # later use. The linkid is the macid returned.
414 self.vlan_to_device_ids[vlan_id] = (device.id, device.parent_id, macid.get('macid', 0))
415
Nathan Knuth09c23102017-02-10 04:43:30 -0800416 ### KPI Metrics - Work in progress feature - Disabling for now
Nathan Knuthab966e52017-01-30 07:48:13 -0800417 ### Give the ONUs a chance to arrive before starting metric collection
418 ### reactor.callLater(5.0, self.start_kpi_collection, device.id)
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800419
Nathan Knuth6e57f332016-12-22 15:49:20 -0800420 def _olt_side_onu_activation(self, serial):
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800421 """
422 This is where if this was a real OLT, the OLT-side activation for
423 the new ONU should be performed. By the time we return, the OLT shall
424 be able to provide tunneled (proxy) communication to the given ONU,
425 using the returned information.
426 """
Nathan Knuth6e57f332016-12-22 15:49:20 -0800427 vlan_id = serial + 200
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800428 return vlan_id
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800429
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800430 def _rcv_io(self, port, frame):
431
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800432 log.info('frame-received', frame=hexify(frame))
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800433
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800434 # make into frame to extract source mac
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800435 response = Ether(frame)
436
Nathan Knuth6e57f332016-12-22 15:49:20 -0800437 if response.haslayer(Dot1Q):
Nathan Knuth6e57f332016-12-22 15:49:20 -0800438
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800439 # All OAM responses from the OLT should have a TIBIT_MGMT_VLAN.
440 # Responses from the ONUs should have a TIBIT_MGMT_VLAN followed by a ONU CTAG
441 # All packet-in frames will have the TIBIT_PACKET_IN_VLAN.
442 if response.getlayer(Dot1Q).type == 0x8100:
443
444 if response.getlayer(Dot1Q).vlan == TIBIT_PACKET_IN_VLAN:
445
446 inner_tag_and_rest = response.payload.payload
447
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800448 if isinstance(inner_tag_and_rest, Dot1Q):
449
450 cvid = inner_tag_and_rest.vlan
451
452 frame = Ether(src=response.src,
453 dst=response.dst,
454 type=inner_tag_and_rest.type) /\
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800455 inner_tag_and_rest.payload
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800456
Nathan Knuth6818b3b2017-02-10 03:28:03 -0800457 _, logical_device_id, _ = self.vlan_to_device_ids.get(cvid)
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800458 if logical_device_id is None:
459 log.error('invalid-cvid', cvid=cvid)
460 else:
461 self.adapter_agent.send_packet_in(
462 logical_device_id=logical_device_id,
463 logical_port_no=cvid, # C-VID encodes port no
464 packet=str(frame))
465
466 else:
467 log.error('packet-in-single-tagged',
468 frame=hexify(response))
469
470 else:
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800471 ## Mgmt responses received from the ONU
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800472 ## Since the type of the first layer is 0x8100,
473 ## then the frame must have an inner tag layer
474 olt_mac = response.src
475 device_id = self.device_ids[olt_mac]
476 channel_id = response[Dot1Q:2].vlan
477 log.info('received_channel_id', channel_id=channel_id,
478 device_id=device_id)
479
480 proxy_address=Device.ProxyAddress(
481 device_id=device_id,
482 channel_id=channel_id
483 )
484 # pop dot1q header(s)
485 msg = response.payload.payload
486 self.adapter_agent.receive_proxied_message(proxy_address, msg)
487
Nathan Knuth6e57f332016-12-22 15:49:20 -0800488 else:
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800489 ## Mgmt responses received from the OLT
Nathan Knuth6e57f332016-12-22 15:49:20 -0800490 ## enqueue incoming parsed frame to right device
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800491 log.info('received-dot1q-not-8100')
Nathan Knuth6e57f332016-12-22 15:49:20 -0800492 self.incoming_queues[response.src].put(response)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800493
494 def _make_ping_frame(self, mac_address):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800495 # Create a json packet
496 json_operation_str = '{\"operation\":\"version\"}'
Nathan Knuth6e57f332016-12-22 15:49:20 -0800497 frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800498 return str(frame)
499
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800500 def _make_links_frame(self, mac_address):
501 # Create a json packet
502 json_operation_str = '{\"operation\":\"links\"}'
Nathan Knuth6e57f332016-12-22 15:49:20 -0800503 frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800504 return str(frame)
505
Nathan Knuth388eff32017-01-18 18:31:22 -0600506 def _make_stats_frame(self, mac_address, itype, link):
507 # Create a json packet
508 json_operation_str = ('{\"operation\":\"stats\",\"parameters\":{\"itype\":\"%s\",\"iinst\",\"0\",\"macid\":\"%s\"}}' % (itype, link))
509 frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str)
510 return str(frame)
511
Zsolt Harasztied091602016-12-08 13:36:38 -0800512 def abandon_device(self, device):
513 raise NotImplementedError(0
514 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500515 def disable_device(self, device):
516 raise NotImplementedError()
517
518 def reenable_device(self, device):
519 raise NotImplementedError()
520
521 def reboot_device(self, device):
522 raise NotImplementedError()
523
524 def delete_device(self, device):
525 raise NotImplementedError()
526
527 def get_device_details(self, device):
Zsolt Harasztied091602016-12-08 13:36:38 -0800528 raise NotImplementedError()
529
Paul Graybb82fe62017-03-30 04:22:39 -0700530 @inlineCallbacks
Zsolt Harasztied091602016-12-08 13:36:38 -0800531 def update_flows_bulk(self, device, flows, groups):
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800532 log.info('########################################')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800533 log.info('bulk-flow-update', device_id=device.id,
534 flows=flows, groups=groups)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800535 assert len(groups.items) == 0, "Cannot yet deal with groups"
536
Nathan Knuthf840dfb2017-01-12 18:15:14 -0800537 # extract ONU VID
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800538 # vid_from_device_id = {v[0]: k for k,v in self.vlan_to_device_ids.iteritems()}
539 # ONU_VID = vid_from_device_id[device.id]
Nathan Knuth05f859e2017-02-23 13:22:26 -0800540 _inner_vid = None
Paul Graybb82fe62017-03-30 04:22:39 -0700541 olt_mac = device.mac_address
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600542
Nathan Knuthd390ceb2017-01-07 15:38:58 -0800543 Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()}
544 Operator = {v: k for k, v in RuleOperatorEnum.iteritems()}
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800545
Zsolt Haraszti80175202016-12-24 00:17:51 -0800546 for flow in flows.items:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800547
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800548 try:
549 in_port = get_in_port(flow)
550 assert in_port is not None
Zsolt Haraszti80175202016-12-24 00:17:51 -0800551
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800552 precedence = 255 - min(flow.priority / 256, 255)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800553
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800554 if in_port == 2:
555 log.info('#### Downstream Rule ####')
556 dn_req = NetworkToNetworkPortObject()
557 dn_req /= PortIngressRuleHeader(precedence=precedence)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800558
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800559 for field in get_ofb_fields(flow):
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800560
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800561 if field.type == ETH_TYPE:
562 _type = field.eth_type
563 log.info('#### field.type == ETH_TYPE ####')
564 dn_req /= PortIngressRuleClauseMatchLength02(
565 fieldcode=Clause['L2 Type/Len'],
566 operator=Operator['=='],
567 match=_type)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800568
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800569 elif field.type == IP_PROTO:
570 _proto = field.ip_proto
571 log.info('#### field.type == IP_PROTO ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800572
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800573 elif field.type == IN_PORT:
574 _port = field.port
575 log.info('#### field.type == IN_PORT ####', port=_port)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800576
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800577 elif field.type == VLAN_VID:
578 _vlan_vid = field.vlan_vid & 0xfff
579 log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid)
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800580 _outer_vid = _vlan_vid
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800581
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800582 elif field.type == VLAN_PCP:
583 _vlan_pcp = field.vlan_pcp
584 log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800585
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800586 elif field.type == UDP_DST:
587 _udp_dst = field.udp_dst
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800588 log.info('#### field.type == UDP_DST ####', udp_dst=_udp_dst)
Zsolt Haraszti6a5107c2017-01-09 23:42:41 -0800589
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800590 elif field.type == UDP_SRC:
591 _udp_src = field.udp_src
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800592 log.info('#### field.type == UDP_SRC ####', udp_src=_udp_src)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800593
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800594 elif field.type == IPV4_DST:
595 _ipv4_dst = field.ipv4_dst
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800596 log.info('#### field.type == IPV4_DST ####', ipv4_dst=_ipv4_dst)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800597
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800598 elif field.type == METADATA:
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800599 _metadata = field.table_metadata
600 log.info('#### field.type == METADATA ####', metadata=_metadata)
601 _inner_vid = _metadata
Zsolt Haraszti80175202016-12-24 00:17:51 -0800602
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800603 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800604 raise NotImplementedError('field.type={}'.format(
605 field.type))
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800606
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800607 for action in get_actions(flow):
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800608
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800609 if action.type == OUTPUT:
610 log.info('#### action.type == OUTPUT ####')
611 dn_req /= PortIngressRuleResultForward()
Nathan Knuth05f859e2017-02-23 13:22:26 -0800612 if _outer_vid == MULTICAST_VLAN:
613 dn_req /= PortIngressRuleResultOLTBroadcastQueue()
Nathan Knuthb396f472017-03-28 17:18:51 -0700614 elif _inner_vid is not None:
Nathan Knuth05f859e2017-02-23 13:22:26 -0800615 serial = _inner_vid - 200
616 link = (0xe222 << 16) | (serial << 8)
617 dn_req /= PortIngressRuleResultOLTQueue(unicastvssn="TBIT",
618 unicastlink=link)
Nathan Knuthb396f472017-03-28 17:18:51 -0700619 elif _inner_vid is None:
620 log.info('#### action.type == OUTPUT INNER VID is NONE ####')
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800621
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800622 elif action.type == POP_VLAN:
623 log.info('#### action.type == POP_VLAN ####')
Nathan Knuth05f859e2017-02-23 13:22:26 -0800624 if _outer_vid == MULTICAST_VLAN:
625 dn_req /= PortIngressRuleResultDelete(fieldcode=Clause['C-VLAN Tag'])
626 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
627 operator=Operator['=='], match=_outer_vid)
628 else:
629 dn_req /= PortIngressRuleResultDelete(fieldcode=Clause['S-VLAN Tag'])
630 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
631 operator=Operator['=='], match=_outer_vid)
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800632 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=1,
633 operator=Operator['=='], match=_inner_vid)
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800634
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800635 elif action.type == PUSH_VLAN:
636 log.info('#### action.type == PUSH_VLAN ####')
637 if action.push.ethertype != 0x8100:
638 log.error('unhandled-tpid',
639 ethertype=action.push.ethertype)
640 dn_req /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'])
Nathan Knuthd8285e62017-01-11 14:18:43 -0600641
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800642 elif action.type == SET_FIELD:
643 log.info('#### action.type == SET_FIELD ####')
644 assert (action.set_field.field.oxm_class ==
645 ofp.OFPXMC_OPENFLOW_BASIC)
646 field = action.set_field.field.ofb_field
647 if field.type == VLAN_VID:
648 dn_req /= PortIngressRuleResultSet(
649 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
650 else:
651 log.error('unsupported-action-set-field-type',
652 field_type=field.type)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600653 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800654 log.error('UNSUPPORTED-ACTION-TYPE',
655 action_type=action.type)
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800656
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800657 dn_req /= PortIngressRuleTerminator()
658 dn_req /= AddPortIngressRule()
659
660 msg = (
661 Ether(dst=device.mac_address) /
662 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
663 EOAMPayload(
Paul Graybb82fe62017-03-30 04:22:39 -0700664 body=TibitOUI() / DPoEOpcode_SetRequest() / dn_req)
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800665 )
666
667 self.io_port.send(str(msg))
668
Paul Graybb82fe62017-03-30 04:22:39 -0700669 # Get and process the Set Response
670 ack = False
671 start_time = time.time()
672
673 # Loop until we have a set response or timeout
674 while not ack:
675 frame = yield self.incoming_queues[olt_mac].get()
676 #TODO - Need to add propoer timeout functionality
677 #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
678 # break # don't wait forever
679
680 respType = self._voltha_get_oam_msg_type(frame)
681 log.info('Received OAM Message 0x %s' % str(respType))
682
683 #Check that the message received is a Set Response
684 if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
685 ack = True
686 else:
687 # Handle unexpected events/OMCI messages
688 self._voltha_check_resp(frame)
689
690 # Verify Set Response
691 if ack:
692 (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
693 if (rc == True):
694 log.info('Set Response had no errors')
695 else:
696 raise Exception('Set Respose had errors')
697 log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
698
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800699 elif in_port == 1:
700 # Upstream rule
701 log.info('#### Upstream Rule ####')
702
703 field_match_vlan_upstream_with_link = False
704 up_req_link = PortIngressRuleHeader(precedence=precedence)
705
706 up_req_pon = PonPortObject()
707 up_req_pon /= PortIngressRuleHeader(precedence=precedence)
708
709 for field in get_ofb_fields(flow):
710
711 if field.type == ETH_TYPE:
712 _type = field.eth_type
713 log.info('#### field.type == ETH_TYPE ####', in_port=in_port,
714 match=_type)
715 up_req_pon /= PortIngressRuleClauseMatchLength02(
716 fieldcode=Clause['L2 Type/Len'],
717 operator=Operator['=='],
718 match=_type)
719
720 up_req_link /= PortIngressRuleClauseMatchLength02(
721 fieldcode=Clause['L2 Type/Len'],
722 operator=Operator['=='],
723 match=_type)
724
725 elif field.type == IP_PROTO:
726 _proto = field.ip_proto
727 log.info('#### field.type == IP_PROTO ####', in_port=in_port,
728 ip_proto=_proto)
729
730 up_req_pon /= PortIngressRuleClauseMatchLength01(
731 fieldcode=Clause['IPv4/IPv6 Protocol Type'],
732 operator=Operator['=='], match=_proto)
733
734 up_req_link /= PortIngressRuleClauseMatchLength01(
735 fieldcode=Clause['IPv4/IPv6 Protocol Type'],
736 operator=Operator['=='], match=_proto)
737
738 elif field.type == IN_PORT:
739 _port = field.port
740 log.info('#### field.type == IN_PORT ####')
741
742 elif field.type == VLAN_VID:
743 _vlan_vid = field.vlan_vid & 0xfff
744 log.info('#### field.type == VLAN_VID ####')
745 up_req_pon /= PortIngressRuleClauseMatchLength02(
746 fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
747 operator=Operator['=='], match=_vlan_vid)
748
749 serial = _vlan_vid - 200
750 link = (0xe222 << 16) | (serial << 8)
751 up_req_link /= OLTUnicastLogicalLink(unicastvssn='TBIT', unicastlink=link)
752
753 up_req_link /= PortIngressRuleClauseMatchLength02(
754 fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
755 operator=Operator['=='], match=_vlan_vid)
756 field_match_vlan_upstream_with_link = True
757
758
759 elif field.type == VLAN_PCP:
760 _vlan_pcp = field.vlan_pcp
761 log.info('#### field.type == VLAN_PCP ####')
762
763 elif field.type == UDP_DST:
764 _udp_dst = field.udp_dst
765 log.info('#### field.type == UDP_DST ####')
766 up_req_pon /= (PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP source port'],
767 operator=Operator['=='], match=0x0044)/
768 PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP destination port'],
769 operator=Operator['=='], match=0x0043))
770
771 elif field.type == UDP_SRC:
772 _udp_src = field.udp_src
773 log.info('#### field.type == UDP_SRC ####')
774
775 else:
776 raise NotImplementedError('field.type={}'.format(
777 field.type))
778
779 for action in get_actions(flow):
780
781 if action.type == OUTPUT:
782 log.info('#### action.type == OUTPUT ####')
783 up_req_pon /= PortIngressRuleResultForward()
784 up_req_link /= PortIngressRuleResultForward()
785
786 elif action.type == POP_VLAN:
787 log.info('#### action.type == POP_VLAN ####')
788
789 elif action.type == PUSH_VLAN:
790 log.info('#### action.type == PUSH_VLAN ####')
791 if action.push.ethertype != 0x8100:
792 log.error('unhandled-ether-type',
793 ethertype=action.push.ethertype)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600794 if field_match_vlan_upstream_with_link == True:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800795 up_req_link /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'],
796 fieldinstance=1)
797 else:
798 up_req_pon /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'],
799 fieldinstance=0)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600800
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800801 elif action.type == SET_FIELD:
802 log.info('#### action.type == SET_FIELD ####')
803 assert (action.set_field.field.oxm_class ==
804 ofp.OFPXMC_OPENFLOW_BASIC)
805 field = action.set_field.field.ofb_field
806 if field.type == VLAN_VID:
807 if field_match_vlan_upstream_with_link == True:
808 up_req_link /=(PortIngressRuleResultCopy(fieldcode=Clause['C-VLAN Tag'])/
809 PortIngressRuleResultReplace(fieldcode=Clause['C-VLAN Tag']))
810
811 up_req_pon /= PortIngressRuleResultSet(
812 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
813 up_req_link /= PortIngressRuleResultSet(
814 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
815 else:
816 log.error('unsupported-action-set-field-type',
817 field_type=field.type)
818
Zsolt Haraszti80175202016-12-24 00:17:51 -0800819 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800820 log.error('UNSUPPORTED-ACTION-TYPE',
821 action_type=action.type)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800822
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800823 if (field_match_vlan_upstream_with_link == True):
824 up_req = up_req_link
Zsolt Haraszti80175202016-12-24 00:17:51 -0800825 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800826 up_req = up_req_pon
Zsolt Haraszti80175202016-12-24 00:17:51 -0800827
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800828 up_req /= PortIngressRuleTerminator()
829 up_req /= AddPortIngressRule()
830
831 msg = (
832 Ether(dst=device.mac_address) /
833 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
834 EOAMPayload(
Paul Graybb82fe62017-03-30 04:22:39 -0700835 body=TibitOUI() / DPoEOpcode_SetRequest() / up_req)
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800836 )
837
838 self.io_port.send(str(msg))
839
Paul Graybb82fe62017-03-30 04:22:39 -0700840 # Get and process the Set Response
841 ack = False
842 start_time = time.time()
843
844 # Loop until we have a set response or timeout
845 while not ack:
846 frame = yield self.incoming_queues[olt_mac].get()
847 #TODO - Need to add propoer timeout functionality
848 #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
849 # break # don't wait forever
850
851 respType = self._voltha_get_oam_msg_type(frame)
852 log.info('Received OAM Message 0x %s' % str(respType))
853
854 #Check that the message received is a Set Response
855 if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
856 ack = True
857 else:
858 # Handle unexpected events/OMCI messages
859 self._voltha_check_resp(frame)
860
861 # Verify Set Response
862 if ack:
863 (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
864 if (rc == True):
865 log.info('Set Response had no errors')
866 else:
867 raise Exception('Set Respose had errors')
868 log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
869
Nathan Knuthd8285e62017-01-11 14:18:43 -0600870 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800871 raise Exception('Port should be 1 or 2 by our convention')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600872
Nathan Knuth6b7b6ff2017-02-12 03:30:48 -0800873 except Exception, e:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800874 log.exception('failed-to-install-flow', e=e, flow=flow)
Zsolt Harasztied091602016-12-08 13:36:38 -0800875
876 def update_flows_incrementally(self, device, flow_changes, group_changes):
877 raise NotImplementedError()
878
879 def send_proxied_message(self, proxy_address, msg):
Nathan Knuth6e57f332016-12-22 15:49:20 -0800880 log.info('send-proxied-message', proxy_address=proxy_address)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800881 device = self.adapter_agent.get_device(proxy_address.device_id)
Nathan Knuthb396f472017-03-28 17:18:51 -0700882
883 mac_address = self.vlan_to_device_ids[proxy_address.channel_id][2].upper()
884
885 if mac_address.startswith(TIBIT_SHORTENED_VSSN):
886 # Send straight OAM
887 frame = Ether(dst=device.mac_address) / \
888 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) / \
889 Dot1Q(vlan=proxy_address.channel_id, prio=TIBIT_MGMT_PRIORITY) / \
890 msg
891 else:
892 # Use the standard to send OMCI over OAM
893 encapsulated_omci = EOAMPayload(body=ItuOUI()/msg)
894
895 frame = Ether(dst=device.mac_address) / \
896 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) / \
897 Dot1Q(vlan=proxy_address.channel_id, prio=TIBIT_MGMT_PRIORITY) / \
898 encapsulated_omci
Nathan Knuth6e57f332016-12-22 15:49:20 -0800899
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800900 self.io_port.send(str(frame))
Zsolt Harasztied091602016-12-08 13:36:38 -0800901
902 def receive_proxied_message(self, proxy_address, msg):
903 raise NotImplementedError()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800904
905 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
906 log.info('packet-out', logical_device_id=logical_device_id,
907 egress_port_no=egress_port_no, msg_len=len(msg))
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800908
Nathan Knuth96531582017-02-13 05:27:37 -0800909 _, logical_dev_id, _ = self.vlan_to_device_ids[egress_port_no]
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800910 if logical_dev_id != logical_device_id:
911 raise Exception('Internal table mismatch')
912
913 tmp = Ether(msg)
914
915 frame = Ether(dst=tmp.dst, src=tmp.src) / \
916 Dot1Q(vlan=TIBIT_PACKET_OUT_VLAN) / \
917 Dot1Q(vlan=egress_port_no) / \
918 tmp.payload
919
920 self.io_port.send(str(frame))
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800921
Peter Shafik9107f2e2017-05-02 15:54:39 -0400922 def receive_inter_adapter_message(self, msg):
923 raise NotImplementedError()
924
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800925 def start_kpi_collection(self, device_id):
Nathan Knuthc99552d2017-01-19 11:23:32 -0600926 """ Periodic KPI metric collection from the device """
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800927 import random
928
Nathan Knuthc99552d2017-01-19 11:23:32 -0600929 # This is setup (for now) to be called from the adapter. Push
930 # architectures should be explored in the near future.
931 @inlineCallbacks
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800932 def _collect(device_id, prefix):
933
Nathan Knuth388eff32017-01-18 18:31:22 -0600934 pon_port_metrics = {}
935 links = []
936 olt_mac = next((mac for mac, device in self.device_ids.iteritems() if device == device_id), None)
Nathan Knuth96531582017-02-13 05:27:37 -0800937 links = [v[TIBIT_ONU_LINK_INDEX] for _,v,_ in self.vlan_to_device_ids.iteritems()]
Nathan Knuth388eff32017-01-18 18:31:22 -0600938
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800939 try:
Nathan Knuth388eff32017-01-18 18:31:22 -0600940 # Step 1: gather metrics from device
941 log.info('link stats frame', links=links)
942 for link in links:
943 stats_frame = self._make_stats_frame(mac_address=olt_mac, itype='olt', link=link)
944 self.io_port.send(stats_frame)
945
946 ## Add timeout mechanism so we can signal if we cannot reach
947 ## device
948 while True:
949 response = yield self.incoming_queues[olt_mac].get()
950 jdict = json.loads(response.payload.payload.body.load)
951 pon_port_metrics[link] = {k: int(v,16) for k,v in jdict['results'].iteritems()}
952 # verify response and if not the expected response
953 if 1: # TODO check if it is really what we expect, and wait if not
954 break
955
956 log.info('nni stats frame')
957 olt_nni_link = ''.join(l for l in olt_mac.split(':'))
958 stats_frame = self._make_stats_frame(mac_address=olt_mac, itype='eth', link=olt_nni_link)
959 self.io_port.send(stats_frame)
960
961 ## Add timeout mechanism so we can signal if we cannot reach
962 ## device
963 while True:
964 response = yield self.incoming_queues[olt_mac].get()
965 jdict = json.loads(response.payload.payload.body.load)
966 nni_port_metrics = {k: int(v,16) for k,v in jdict['results'].iteritems()}
967 # verify response and if not the expected response
968 if 1: # TODO check if it is really what we expect, and wait if not
969 break
970
Nathan Knuthc99552d2017-01-19 11:23:32 -0600971 olt_metrics = dict(
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800972 cpu_util=20 + 5 * random.random(),
973 buffer_util=10 + 10 * random.random()
974 )
975
976 # Step 2: prepare the KpiEvent for submission
977 # we can time-stamp them here (or could use time derived from OLT
978 ts = arrow.utcnow().timestamp
Nathan Knuthc99552d2017-01-19 11:23:32 -0600979 prefixes = {
980 # CPU Metrics (example)
981 prefix: MetricValuePairs(metrics=olt_metrics),
982 # OLT NNI port
983 prefix + '.nni': MetricValuePairs(metrics=nni_port_metrics)
984 }
985
986 for link in links:
987 # PON link ports
988 prefixes[prefix + '.pon.{}'.format(link)] = MetricValuePairs(metrics=pon_port_metrics[link])
989
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800990 kpi_event = KpiEvent(
991 type=KpiEventType.slice,
992 ts=ts,
Nathan Knuthc99552d2017-01-19 11:23:32 -0600993 prefixes=prefixes
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800994 )
995
996 # Step 3: submit
997 self.adapter_agent.submit_kpis(kpi_event)
998
999 except Exception as e:
1000 log.exception('failed-to-submit-kpis', e=e)
1001
1002 prefix = 'voltha.{}.{}'.format(self.name, device_id)
1003 lc = LoopingCall(_collect, device_id, prefix)
1004 lc.start(interval=15) # TODO make this configurable
Paul Graybb82fe62017-03-30 04:22:39 -07001005
1006 def _voltha_get_oam_msg_type(self, frame):
1007 respType = RxedOamMsgTypeEnum["Unknown"]
1008 recv_frame = frame
1009 payload = recv_frame.payload
1010 if hasattr(payload, 'body'):
1011 loadstr = payload.body.load
1012 bytesRead = 0;
1013 if (payload.opcode == 0xFE):
1014
1015 # Extract the OUI
1016 (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
1017 oui = (oui_hi << 16) | oui_lo
1018 log.debug('oui: 0x %06x' % oui)
1019 bytesRead += 3
1020
1021 # If this is the ITU OUI, then there is an embedded OMCI message
1022 if (oui == 0x0019A7):
1023 respType = RxedOamMsgTypeEnum["OMCI Message"]
1024
1025 # Treat Cablelabs OUI and Tibit OUI as the same
1026 elif ((oui == 0x001000) or (oui == 0x2AEA15)):
1027
1028 (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
1029# log.info('DPoE Opcode: {} ({:0>2X})'.format(DPoEOpcodeEnum[dpoeOpcode], dpoeOpcode))
1030 bytesRead += 1
1031
1032 # Get Response
1033 if (dpoeOpcode == 0x02):
1034 respType = RxedOamMsgTypeEnum["DPoE Get Response"]
1035
1036 # Set Response
1037 elif (dpoeOpcode == 0x04):
1038 respType = RxedOamMsgTypeEnum["DPoE Set Response"]
1039
1040 # File Transfer ACK
1041 elif (dpoeOpcode == 0x09):
1042 respType = RxedOamMsgTypeEnum["DPoE File Transfer"]
1043
1044 else:
1045 log.info('Unsupported OAM OUI 0x{:0>6X}'.format(oui))
1046
1047 # Handle OAM Event Notification
1048 elif (payload.opcode == 0x01):
1049 respType = RxedOamMsgTypeEnum["Event Notification"]
1050 else:
1051 log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
1052 else:
1053 log.debug('received frame has no payload')
1054
1055 return respType
1056
1057
1058 def _voltha_check_set_resp(self, frame):
1059 rc = False
1060 branch = 0
1061 leaf = 0
1062 status = 0
1063
1064 recv_frame = frame
1065 payload = recv_frame.payload
1066 if hasattr(payload, 'body'):
1067 loadstr = payload.body.load
1068 bytesRead = 0;
1069 #if self.report_obj is not None:
1070 # self.report_obj.log_result(data="OAM Opcode", actual=hex(payload.opcode), expected=hex(0xFE), criteria="==")
1071 if (payload.opcode == 0xFE):
1072
1073 # Extract the OUI
1074 (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
1075 oui = (oui_hi << 16) | oui_lo
1076 log.info('oui: 0x %06x' % oui)
1077 bytesRead += 3
1078
1079 # Treat Cablelabs OUI and Tibit OUI as the same
1080 if ((oui == 0x001000) or (oui == 0x2AEA15)):
1081 (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
1082 bytesRead += 1
1083
1084 startOfTlvs = bytesRead
1085 # Set Response
1086 if (dpoeOpcode == 0x04):
1087 test =1
1088 (rc,branch,leaf,status) = self._voltha_check_set_resp_attrs(loadstr, startOfTlvs)
1089 if (rc == True):
1090 log.info('Set Response had no errors')
1091 else:
1092 log.debug('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
1093 else:
1094 log.info('Unsupported DPoE Opcode: {} ({:0>2X})'.format(DPoEOpcodeEnum[dpoeOpcode], dpoeOpcode))
1095 else:
1096 log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
1097 else:
1098 log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
1099 else:
1100 log.debug('received frame has no payload')
1101
1102 return rc,branch,leaf,status
1103
1104
1105 def _voltha_check_resp(self, frame):
1106 recv_frame = frame
1107 payload = recv_frame.payload
1108 if hasattr(payload, 'body'):
1109 loadstr = payload.body.load
1110 bytesRead = 0;
1111 if (payload.opcode == 0xFE):
1112
1113 # Extract the OUI
1114 (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
1115 oui = (oui_hi << 16) | oui_lo
1116 log.info('oui: 0x %06x' % oui)
1117 bytesRead += 3
1118
1119 # If this is the ITU OUI, then there is an embedded OMCI message
1120 if (oui == 0x0019A7):
1121 self._voltha_handle_omci(loadstr,bytesRead)
1122
1123 # Treat Cablelabs OUI and Tibit OUI as the same
1124 elif ((oui == 0x001000) or (oui == 0x2AEA15)):
1125 log.debug('Recieved Response OUI 0x{:0>6X}'. format(oui))
1126 else:
1127 log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
1128
1129 # Handle OAM Event Notification
1130 elif (payload.opcode == 0x01):
1131 self._voltha_handle_oam_event(loadstr, bytesRead)
1132 else:
1133 log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
1134
1135 else:
1136 log.debug('received frame has no payload')
1137
1138
1139 def _voltha_handle_oam_event(self, loadstr, startOfEvent):
1140 bytesRead = startOfEvent
1141 (seq_num, tlv_type, ev_len, oui_hi, oui_lo) = struct.unpack_from('>HBBBH', loadstr, bytesRead)
1142 oui = (oui_hi << 16) | oui_lo
1143
1144 log.info('seq_num: 0x%04x' % seq_num)
1145 log.info('tlv_type: 0x%' % tlv_type)
1146 log.info('ev_len: 0x%x' % ev_len)
1147 log.info('oui: 0x%06x"'% oui)
1148
1149 if (tlv_type != 0xFE):
1150 log.debug('unexpected tlv_type 0x%x (expected 0xFE)' % tlv_type)
1151 elif (oui == 0x001000):
1152 log.debug('DPoE Event')
1153 ## TODO - Handle DPoE Event/Alarm
1154 elif (oui == 0x2AEA15):
1155 log.debug('Tibit-specific Event')
1156
1157 # TODO - Handle addition/removal of links
1158
1159 bytesRead = 7
1160
1161 # TODO - Check OUI and parse Source and Reference Object Contexts
1162
1163
1164 def _voltha_handle_omci(self, loadstr, startOfEvent):
1165 bytesRead = startOfEvent
1166# (seq_num, tlv_type, ev_len, oui_hi, oui_lo) = struct.unpack_from('>BBBBBH', loadstr, bytesRead)
1167
1168 log.debug('OMCI Message')
1169
1170 # TODO - Handle OMCI message
1171
1172
1173
1174 def _voltha_handle_get_value(self, loadstr, startOfTlvs, queryBranch, queryLeaf):
1175 retVal = False;
1176 value = 0
1177 branch = 0
1178 leaf = 0
1179 bytesRead = startOfTlvs
1180 loadstrlen = len(loadstr)
1181
1182 while (bytesRead <= loadstrlen):
1183 (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)
1184# log.info('Branch/Leaf 0x{:0>2X}/0x{:0>4X}'.format(branch, leaf))
1185
1186 if (branch != 0):
1187 bytesRead += 3
1188 length = struct.unpack_from('>B', loadstr, bytesRead)[0]
1189# log.info('Length: 0x{:0>2X} ({})'.format(length,length))
1190 bytesRead += 1
1191
1192 if (length == 1):
1193 value = struct.unpack_from(">B", loadstr, bytesRead)[0]
1194 elif (length == 2):
1195 value = struct.unpack_from(">H", loadstr, bytesRead)[0]
1196 elif (length == 4):
1197 value = struct.unpack_from(">I", loadstr, bytesRead)[0]
1198 elif (length == 8):
1199 value = struct.unpack_from(">Q", loadstr, bytesRead)[0]
1200 else:
1201 if (length >= 0x80):
1202 log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
1203 # Set length to zero so bytesRead doesn't get mistakenly incremented below
1204 length = 0
1205 else:
1206 # Attributes with a length of zero are actually 128 bytes long
1207 if (length == 0):
1208 length = 128;
1209 valStr = ">{}s".format(length)
1210 value = struct.unpack_from(valStr, loadstr, bytesRead)[0]
1211
1212# log.info('Value: {}'.format(value))
1213
1214 if (length > 0):
1215 bytesRead += length
1216
1217 if (branch != 0xD6):
1218 if ( ((queryBranch == 0) and (queryLeaf == 0)) or
1219 ((queryBranch == branch) and (queryLeaf == leaf)) ):
1220 # Prevent zero-lengthed values from returning success
1221 if (length > 0):
1222 retVal = True;
1223 break
1224 else:
1225 break
1226
1227 if (retVal == False):
1228 value = 0
1229
1230 return retVal,bytesRead,value,branch,leaf
1231
1232 def _voltha_check_set_resp_attrs(self, loadstr, startOfTlvs):
1233 retVal = True;
1234 branch = 0
1235 leaf = 0
1236 length = 0
1237 bytesRead = startOfTlvs
1238 loadstrlen = len(loadstr)
1239
1240 while (bytesRead <= loadstrlen):
1241 (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)
1242# log.info('Branch/Leaf 0x{:0>2X}/0x{:0>4X}'.format(branch, leaf))
1243
1244 if (branch != 0):
1245 bytesRead += 3
1246 length = struct.unpack_from('>B', loadstr, bytesRead)[0]
1247# log.info('Length: 0x{:0>2X} ({})'.format(length,length))
1248 bytesRead += 1
1249
1250 if (length >= 0x80):
1251 log.debug('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
1252 if (length > 0x80):
1253 retVal = False;
1254 break;
1255 else:
1256 bytesRead += length
1257
1258 else:
1259 break
1260
1261 return retVal,branch,leaf,length
1262
1263
1264
1265 def _voltha_handle_fx_ack(self, loadstr, startOfXfer, block_number):
1266 retVal = False
1267 (fx_opcode, acked_block, response_code) = struct.unpack_from('>BHB', loadstr, startOfXfer)
1268
1269 log.debug('fx_opcode: 0x%x' % fx_opcode)
1270 log.debug('acked_block: 0x%x' % acked_block)
1271 log.debug('response_code: 0x%x' % response_code)
1272
1273
1274
1275 if (fx_opcode != 0x03):
1276 log.debug('unexpected fx_opcode 0x%x (expected 0x03)' % fx_opcode)
1277 elif (acked_block != block_number):
1278 log.debug('unexpected acked_block 0x%x (expected 0x%x)' % (acked_block, block_number))
1279 elif (response_code != 0):
1280 log.debug('unexpected response_code 0x%x (expected 0x00)' % response_code)
1281 else:
1282 retVal = True;
1283