blob: 4740f93ab97505d149aa55c1263e7cb2b28fb634 [file] [log] [blame]
Chip Boling27275992017-09-22 15:17:04 -05001# Copyright 2017-present Adtran, Inc.
Chip Boling3e3b1a92017-05-16 11:51:18 -05002#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
Chip Boling252c7772017-08-16 10:13:17 -05007# http://www.apache.org/licenses/LICENSE-2.0
Chip Boling3e3b1a92017-05-16 11:51:18 -05008#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Chip Boling3e3b1a92017-05-16 11:51:18 -050014"""
15Adtran generic VOLTHA device handler
16"""
Chip Boling7294b252017-06-15 16:16:55 -050017import argparse
Chip Boling3e3b1a92017-05-16 11:51:18 -050018import datetime
Chip Boling7294b252017-06-15 16:16:55 -050019import shlex
20import time
Chip Boling3e3b1a92017-05-16 11:51:18 -050021
22import arrow
Chip Boling3e3b1a92017-05-16 11:51:18 -050023import structlog
24from twisted.internet import reactor, defer
Chip Boling7294b252017-06-15 16:16:55 -050025from twisted.internet.defer import inlineCallbacks, returnValue
Chip Boling27275992017-09-22 15:17:04 -050026from twisted.python.failure import Failure
Chip Boling3e3b1a92017-05-16 11:51:18 -050027
Chip Boling7294b252017-06-15 16:16:55 -050028from voltha.adapters.adtran_olt.net.adtran_netconf import AdtranNetconfClient
Chip Boling3e3b1a92017-05-16 11:51:18 -050029from voltha.adapters.adtran_olt.net.adtran_rest import AdtranRestClient
30from voltha.protos import third_party
31from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
Chip Boling3e3b1a92017-05-16 11:51:18 -050032from voltha.protos.logical_device_pb2 import LogicalDevice
33from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_switch_features, OFPC_PORT_STATS, \
34 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
Chip Bolingbffef5e2018-08-07 14:53:12 -050035from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
Chip Bolingce5bfc02018-08-09 13:57:49 -050036from voltha.extensions.kpi.olt.olt_pm_metrics import OltPmMetrics
Chip Boling7294b252017-06-15 16:16:55 -050037from common.utils.asleep import asleep
38
Chip Boling3e3b1a92017-05-16 11:51:18 -050039_ = third_party
40
Chip Bolingab8863d2018-03-22 14:50:31 -050041DEFAULT_MULTICAST_VLAN = 4000
Chip Boling88354fb2018-09-21 12:17:19 -050042BROADCOM_UNTAGGED_VLAN = 4091 # SEBA - For BBWF demo (BroadCom Default VLAN)
Chip Boling211c39a2018-10-15 13:03:17 -050043DEFAULT_UTILITY_VLAN = 4094
Chip Boling88354fb2018-09-21 12:17:19 -050044DEFAULT_UNTAGGED_VLAN = BROADCOM_UNTAGGED_VLAN # if RG does not send priority tagged frames
Chip Boling5561d552017-07-07 15:11:26 -050045
Chip Bolingff3087f2017-10-12 09:26:29 -050046_DEFAULT_RESTCONF_USERNAME = ""
47_DEFAULT_RESTCONF_PASSWORD = ""
Chip Boling252c7772017-08-16 10:13:17 -050048_DEFAULT_RESTCONF_PORT = 8081
49
Chip Bolingff3087f2017-10-12 09:26:29 -050050_DEFAULT_NETCONF_USERNAME = ""
51_DEFAULT_NETCONF_PASSWORD = ""
Chip Boling252c7772017-08-16 10:13:17 -050052_DEFAULT_NETCONF_PORT = 830
53
Chip Boling88354fb2018-09-21 12:17:19 -050054_STARTUP_RETRY_TIMEOUT = 5 # 5 seconds delay after activate failed before we
Chip Boling78503602018-09-26 15:05:20 -050055_DEFAULT_XPON_SUPPORTED = False # LOOK for the keywords 'xpon_support', SEBA
Chip Boling88354fb2018-09-21 12:17:19 -050056 # for areas to clean up once xPON is deprecated
Chip Bolingce5bfc02018-08-09 13:57:49 -050057
Chip Boling3e3b1a92017-05-16 11:51:18 -050058
59class AdtranDeviceHandler(object):
60 """
61 A device that supports the ADTRAN RESTCONF protocol for communications
62 with a VOLTHA/VANILLA managed device.
Chip Boling3e3b1a92017-05-16 11:51:18 -050063 Port numbering guidelines for Adtran OLT devices. Derived classes may augment
64 the numbering scheme below as needed.
65
66 - Reserve port 0 for the CPU capture port. All ports to/from this port should
67 be related to messages destined to/from the OpenFlow controller.
68
69 - Begin numbering northbound ports (network facing) at port 1 contiguously.
70 Consider the northbound ports to typically be the highest speed uplinks.
71 If these ports are removable or provided by one or more slots in a chassis
72 subsystem, still reserve the appropriate amount of port numbers whether they
73 are populated or not.
74
75 - Number southbound ports (customer facing) ports next starting at the next
76 available port number. If chassis based, follow the same rules as northbound
77 ports and reserve enough port numbers.
78
79 - Number any out-of-band management ports (if any) last. It will be up to the
80 Device Adapter developer whether to expose these to openflow or not. If you do
81 not expose them, but do have the ports, still reserve the appropriate number of
82 port numbers just in case.
83 """
84 # HTTP shortcuts
85 HELLO_URI = '/restconf/adtran-hello:hello'
86
Chip Boling7294b252017-06-15 16:16:55 -050087 # RPC XML shortcuts
88 RESTART_RPC = '<system-restart xmlns="urn:ietf:params:xml:ns:yang:ietf-system"/>'
89
Chip Bolingfd1fd372017-12-20 13:34:12 -060090 def __init__(self, **kwargs):
Chip Bolingab8863d2018-03-22 14:50:31 -050091 from net.pio_zmq import DEFAULT_PIO_TCP_PORT
92 from net.pon_zmq import DEFAULT_PON_AGENT_TCP_PORT
Chip Boling252c7772017-08-16 10:13:17 -050093
Chip Bolingfd1fd372017-12-20 13:34:12 -060094 super(AdtranDeviceHandler, self).__init__()
95
96 adapter = kwargs['adapter']
97 device_id = kwargs['device-id']
98 timeout = kwargs.get('timeout', 20)
99
Chip Boling3e3b1a92017-05-16 11:51:18 -0500100 self.adapter = adapter
101 self.adapter_agent = adapter.adapter_agent
102 self.device_id = device_id
103 self.log = structlog.get_logger(device_id=device_id)
Chip Bolingbb15b512018-06-01 11:39:58 -0500104 self.startup = None # Startup/reboot deferred
Chip Boling3e3b1a92017-05-16 11:51:18 -0500105 self.channel = None # Proxy messaging channel with 'send' method
Chip Boling3e3b1a92017-05-16 11:51:18 -0500106 self.logical_device_id = None
Chip Boling5561d552017-07-07 15:11:26 -0500107 self.pm_metrics = None
108 self.alarms = None
Chip Boling27275992017-09-22 15:17:04 -0500109 self.multicast_vlans = [DEFAULT_MULTICAST_VLAN]
Chip Bolingab8863d2018-03-22 14:50:31 -0500110 self.untagged_vlan = DEFAULT_UNTAGGED_VLAN
111 self.utility_vlan = DEFAULT_UTILITY_VLAN
Chip Bolingbffef5e2018-08-07 14:53:12 -0500112 self.mac_address = '00:13:95:00:00:00'
Chip Bolingab8863d2018-03-22 14:50:31 -0500113 self._rest_support = None
Chip Bolingce5bfc02018-08-09 13:57:49 -0500114 self._initial_enable_complete = False
Chip Boling3e3b1a92017-05-16 11:51:18 -0500115
116 # Northbound and Southbound ports
117 self.northbound_ports = {} # port number -> Port
118 self.southbound_ports = {} # port number -> Port (For PON, use pon-id as key)
Chip Boling7294b252017-06-15 16:16:55 -0500119 # self.management_ports = {} # port number -> Port TODO: Not currently supported
Chip Boling3e3b1a92017-05-16 11:51:18 -0500120
121 self.num_northbound_ports = None
122 self.num_southbound_ports = None
Chip Boling7294b252017-06-15 16:16:55 -0500123 # self.num_management_ports = None
124
125 self.ip_address = None
126 self.timeout = timeout
127 self.restart_failure_timeout = 5 * 60 # 5 Minute timeout
Chip Boling3e3b1a92017-05-16 11:51:18 -0500128
129 # REST Client
Chip Boling252c7772017-08-16 10:13:17 -0500130 self.rest_port = _DEFAULT_RESTCONF_PORT
131 self.rest_username = _DEFAULT_RESTCONF_USERNAME
132 self.rest_password = _DEFAULT_RESTCONF_PASSWORD
Chip Boling5561d552017-07-07 15:11:26 -0500133 self._rest_client = None
Chip Boling3e3b1a92017-05-16 11:51:18 -0500134
Chip Boling7294b252017-06-15 16:16:55 -0500135 # NETCONF Client
Chip Boling252c7772017-08-16 10:13:17 -0500136 self.netconf_port = _DEFAULT_NETCONF_PORT
137 self.netconf_username = _DEFAULT_NETCONF_USERNAME
138 self.netconf_password = _DEFAULT_NETCONF_PASSWORD
Chip Boling5561d552017-07-07 15:11:26 -0500139 self._netconf_client = None
Chip Boling7294b252017-06-15 16:16:55 -0500140
Chip Boling88354fb2018-09-21 12:17:19 -0500141 # TODO: Decrement xPON once Technology Profiles completed
142 self.xpon_support = _DEFAULT_XPON_SUPPORTED
Chip Boling61a12792017-10-02 13:23:27 -0500143 self.max_nni_ports = 1 # TODO: This is a VOLTHA imposed limit in 'flow_decomposer.py
Chip Boling27275992017-09-22 15:17:04 -0500144 # and logical_device_agent.py
Chip Boling252c7772017-08-16 10:13:17 -0500145 # OMCI ZMQ Channel
Chip Boling9adbc952018-01-18 12:47:04 -0600146 self.pon_agent_port = DEFAULT_PON_AGENT_TCP_PORT
147 self.pio_port = DEFAULT_PIO_TCP_PORT
Chip Boling252c7772017-08-16 10:13:17 -0500148
Chip Boling3e3b1a92017-05-16 11:51:18 -0500149 # Heartbeat support
150 self.heartbeat_count = 0
151 self.heartbeat_miss = 0
Chip Boling61a12792017-10-02 13:23:27 -0500152 self.heartbeat_interval = 2 # TODO: Decrease before release or any scale testing
Chip Boling3e3b1a92017-05-16 11:51:18 -0500153 self.heartbeat_failed_limit = 3
154 self.heartbeat_timeout = 5
155 self.heartbeat = None
156 self.heartbeat_last_reason = ''
157
Chip Bolingab8863d2018-03-22 14:50:31 -0500158 # Virtualized OLT Support
Chip Boling7294b252017-06-15 16:16:55 -0500159 self.is_virtual_olt = False
160
161 # Installed flows
Chip Boling69fba862017-08-18 15:11:32 -0500162 self._evcs = {} # Flow ID/name -> FlowEntry
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500163
164 def _delete_logical_device(self):
165 ldi, self.logical_device_id = self.logical_device_id, None
166
167 if ldi is None:
168 return
169
170 self.log.debug('delete-logical-device', ldi=ldi)
171
172 logical_device = self.adapter_agent.get_logical_device(ldi)
173 self.adapter_agent.delete_logical_device(logical_device)
174
175 device = self.adapter_agent.get_device(self.device_id)
176 device.parent_id = ''
177
178 # Update the logical device mapping
179 if ldi in self.adapter.logical_device_id_to_root_device_id:
180 del self.adapter.logical_device_id_to_root_device_id[ldi]
181
Chip Boling3e3b1a92017-05-16 11:51:18 -0500182 def __del__(self):
183 # Kill any startup or heartbeat defers
184
185 d, self.startup = self.startup, None
Chip Boling5561d552017-07-07 15:11:26 -0500186 h, self.heartbeat = self.heartbeat, None
Chip Boling5561d552017-07-07 15:11:26 -0500187
Chip Boling48646962017-08-20 09:41:18 -0500188 if d is not None and not d.called:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500189 d.cancel()
190
Chip Boling48646962017-08-20 09:41:18 -0500191 if h is not None and not h.called:
Chip Boling3e3b1a92017-05-16 11:51:18 -0500192 h.cancel()
193
Chip Boling7294b252017-06-15 16:16:55 -0500194 # Remove the logical device
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500195 self._delete_logical_device()
Chip Boling7294b252017-06-15 16:16:55 -0500196
Chip Boling3e3b1a92017-05-16 11:51:18 -0500197 self.northbound_ports.clear()
198 self.southbound_ports.clear()
199
200 def __str__(self):
Chip Boling7294b252017-06-15 16:16:55 -0500201 return "AdtranDeviceHandler: {}".format(self.ip_address)
202
Chip Boling5561d552017-07-07 15:11:26 -0500203 @property
204 def netconf_client(self):
205 return self._netconf_client
206
207 @property
208 def rest_client(self):
209 return self._rest_client
210
Chip Boling69fba862017-08-18 15:11:32 -0500211 @property
212 def evcs(self):
213 return list(self._evcs.values())
214
215 def add_evc(self, evc):
Chip Boling27275992017-09-22 15:17:04 -0500216 if self._evcs is not None and evc.name not in self._evcs:
Chip Boling69fba862017-08-18 15:11:32 -0500217 self._evcs[evc.name] = evc
218
219 def remove_evc(self, evc):
220 if self._evcs is not None and evc.name in self._evcs:
221 del self._evcs[evc.name]
222
Chip Boling7294b252017-06-15 16:16:55 -0500223 def parse_provisioning_options(self, device):
Chip Bolingab8863d2018-03-22 14:50:31 -0500224 from net.pon_zmq import DEFAULT_PON_AGENT_TCP_PORT
225 from net.pio_zmq import DEFAULT_PIO_TCP_PORT
Chip Boling252c7772017-08-16 10:13:17 -0500226
Chip Boling308c3312018-08-09 13:08:24 -0500227 if device.ipv4_address:
228 self.ip_address = device.ipv4_address
Chip Boling7294b252017-06-15 16:16:55 -0500229
Chip Boling308c3312018-08-09 13:08:24 -0500230 elif device.host_and_port:
231 host_and_port = device.host_and_port.split(":")
232 self.ip_address = host_and_port[0]
233 self.netconf_port = int(host_and_port[1])
234 self.adapter_agent.update_device(device)
235
236 else:
237 self.activate_failed(device, 'No IP_address field provided')
Chip Boling7294b252017-06-15 16:16:55 -0500238
239 #############################################################
240 # Now optional parameters
241
242 def check_tcp_port(value):
243 ivalue = int(value)
244 if ivalue <= 0 or ivalue > 65535:
245 raise argparse.ArgumentTypeError("%s is a not a valid port number" % value)
246 return ivalue
247
Chip Boling27275992017-09-22 15:17:04 -0500248 def check_vid(value):
249 ivalue = int(value)
250 if ivalue <= 1 or ivalue > 4094:
251 raise argparse.ArgumentTypeError("Valid VLANs are 2..4094")
252 return ivalue
253
Chip Boling7294b252017-06-15 16:16:55 -0500254 parser = argparse.ArgumentParser(description='Adtran Device Adapter')
Chip Boling252c7772017-08-16 10:13:17 -0500255 parser.add_argument('--nc_username', '-u', action='store', default=_DEFAULT_NETCONF_USERNAME,
256 help='NETCONF username')
257 parser.add_argument('--nc_password', '-p', action='store', default=_DEFAULT_NETCONF_PASSWORD,
258 help='NETCONF Password')
259 parser.add_argument('--nc_port', '-t', action='store', default=_DEFAULT_NETCONF_PORT, type=check_tcp_port,
Chip Boling7294b252017-06-15 16:16:55 -0500260 help='NETCONF TCP Port')
Chip Boling252c7772017-08-16 10:13:17 -0500261 parser.add_argument('--rc_username', '-U', action='store', default=_DEFAULT_RESTCONF_USERNAME,
262 help='REST username')
263 parser.add_argument('--rc_password', '-P', action='store', default=_DEFAULT_RESTCONF_PASSWORD,
264 help='REST Password')
265 parser.add_argument('--rc_port', '-T', action='store', default=_DEFAULT_RESTCONF_PORT, type=check_tcp_port,
266 help='RESTCONF TCP Port')
Chip Boling9adbc952018-01-18 12:47:04 -0600267 parser.add_argument('--zmq_port', '-z', action='store', default=DEFAULT_PON_AGENT_TCP_PORT,
Chip Bolingab8863d2018-03-22 14:50:31 -0500268 type=check_tcp_port, help='PON Agent ZeroMQ Port')
269 parser.add_argument('--pio_port', '-Z', action='store', default=DEFAULT_PIO_TCP_PORT,
270 type=check_tcp_port, help='PIO Service ZeroMQ Port')
Chip Boling27275992017-09-22 15:17:04 -0500271 parser.add_argument('--multicast_vlan', '-M', action='store',
272 default='{}'.format(DEFAULT_MULTICAST_VLAN),
Chip Bolingab8863d2018-03-22 14:50:31 -0500273 help='Multicast VLAN'),
274 parser.add_argument('--untagged_vlan', '-v', action='store',
275 default='{}'.format(DEFAULT_UNTAGGED_VLAN),
276 help='VLAN for Untagged Frames from ONUs'),
277 parser.add_argument('--utility_vlan', '-B', action='store',
278 default='{}'.format(DEFAULT_UTILITY_VLAN),
Chip Bolingbb15b512018-06-01 11:39:58 -0500279 help='VLAN for Untagged Frames from ONUs')
Chip Boling88354fb2018-09-21 12:17:19 -0500280 parser.add_argument('--xpon_enable', '-X', action='store_true',
Chip Boling78503602018-09-26 15:05:20 -0500281 default=_DEFAULT_XPON_SUPPORTED,
Chip Boling88354fb2018-09-21 12:17:19 -0500282 help='enable xPON (BBF WT-385) provisioning support')
Chip Boling7294b252017-06-15 16:16:55 -0500283 try:
284 args = parser.parse_args(shlex.split(device.extra_args))
285
Chip Bolingbb15b512018-06-01 11:39:58 -0500286 # May have multiple multicast VLANs
287 self.multicast_vlans = [int(vid.strip()) for vid in args.multicast_vlan.split(',')]
Chip Boling27275992017-09-22 15:17:04 -0500288
Chip Boling7294b252017-06-15 16:16:55 -0500289 self.netconf_username = args.nc_username
290 self.netconf_password = args.nc_password
291 self.netconf_port = args.nc_port
292
293 self.rest_username = args.rc_username
294 self.rest_password = args.rc_password
295 self.rest_port = args.rc_port
296
Chip Boling9adbc952018-01-18 12:47:04 -0600297 self.pon_agent_port = args.zmq_port
Chip Bolingab8863d2018-03-22 14:50:31 -0500298 self.pio_port = args.pio_port
Chip Boling88354fb2018-09-21 12:17:19 -0500299 self.xpon_support = args.xpon_enable
300
301 if not self.xpon_support:
302 self.untagged_vlan = BROADCOM_UNTAGGED_VLAN
Chip Boling252c7772017-08-16 10:13:17 -0500303
Chip Bolingfd1fd372017-12-20 13:34:12 -0600304 if not self.rest_username:
305 self.rest_username = 'NDE0NDRkNDk0ZQ==\n'.\
306 decode('base64').decode('hex')
307 if not self.rest_password:
308 self.rest_password = 'NTA0MTUzNTM1NzRmNTI0NA==\n'.\
309 decode('base64').decode('hex')
310 if not self.netconf_username:
311 self.netconf_username = 'Njg3Mzc2NzI2ZjZmNzQ=\n'.\
312 decode('base64').decode('hex')
313 if not self.netconf_password:
314 self.netconf_password = 'NDI0ZjUzNDM0Zg==\n'.\
315 decode('base64').decode('hex')
316
Chip Boling7294b252017-06-15 16:16:55 -0500317 except argparse.ArgumentError as e:
318 self.activate_failed(device,
319 'Invalid arguments: {}'.format(e.message),
320 reachable=False)
321 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -0500322 self.log.exception('option_parsing_error: {}'.format(e.message))
323
Chip Boling3e3b1a92017-05-16 11:51:18 -0500324 @inlineCallbacks
Chip Bolingce5bfc02018-08-09 13:57:49 -0500325 def activate(self, done_deferred, reconciling):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500326 """
327 Activate the OLT device
328
Chip Bolingfd1fd372017-12-20 13:34:12 -0600329 :param done_deferred: (Deferred) Deferred to fire when done
Chip Boling7294b252017-06-15 16:16:55 -0500330 :param reconciling: If True, this adapter is taking over for a previous adapter
331 for an existing OLT
Chip Boling3e3b1a92017-05-16 11:51:18 -0500332 """
Chip Boling5561d552017-07-07 15:11:26 -0500333 self.log.info('AdtranDeviceHandler.activating', reconciling=reconciling)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500334
335 if self.logical_device_id is None:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500336 device = self.adapter_agent.get_device(self.device_id)
337
Chip Boling3e3b1a92017-05-16 11:51:18 -0500338 try:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600339 # Parse our command line options for this device
340 self.parse_provisioning_options(device)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500341
Chip Bolingfd1fd372017-12-20 13:34:12 -0600342 ############################################################################
Chip Boling8956b642018-04-11 13:47:01 -0500343 # Currently, only virtual OLT (pizzabox) is supported
344 # self.is_virtual_olt = Add test for MOCK Device if we want to support it
Chip Boling3e3b1a92017-05-16 11:51:18 -0500345
Chip Bolingfd1fd372017-12-20 13:34:12 -0600346 ############################################################################
347 # Start initial discovery of NETCONF support (if any)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600348 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500349 device.reason = 'establishing NETCONF connection'
350 self.adapter_agent.update_device(device)
351
Chip Bolingfd1fd372017-12-20 13:34:12 -0600352 self.startup = self.make_netconf_connection()
353 yield self.startup
Chip Boling3e3b1a92017-05-16 11:51:18 -0500354
Chip Bolingfd1fd372017-12-20 13:34:12 -0600355 except Exception as e:
Chip Boling8956b642018-04-11 13:47:01 -0500356 self.log.exception('netconf-connection', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500357 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Boling8956b642018-04-11 13:47:01 -0500358
359 ############################################################################
360 # Update access information on network device for full protocol support
361 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500362 device.reason = 'device networking validation'
363 self.adapter_agent.update_device(device)
Chip Boling8956b642018-04-11 13:47:01 -0500364 self.startup = self.ready_network_access()
365 yield self.startup
366
367 except Exception as e:
368 self.log.exception('network-setup', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500369 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Boling8956b642018-04-11 13:47:01 -0500370
371 ############################################################################
372 # Restconf setup
373 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500374 device.reason = 'establishing RESTConf connections'
375 self.adapter_agent.update_device(device)
Chip Boling8956b642018-04-11 13:47:01 -0500376 self.startup = self.make_restconf_connection()
377 yield self.startup
378
379 except Exception as e:
380 self.log.exception('restconf-setup', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500381 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500382
Chip Bolingfd1fd372017-12-20 13:34:12 -0600383 ############################################################################
384 # Get the device Information
385
386 if reconciling:
387 device.connect_status = ConnectStatus.REACHABLE
388 self.adapter_agent.update_device(device)
389 else:
390 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500391 device.reason = 'retrieving device information'
392 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600393 self.startup = self.get_device_info(device)
394 results = yield self.startup
395
396 device.model = results.get('model', 'unknown')
397 device.hardware_version = results.get('hardware_version', 'unknown')
398 device.firmware_version = results.get('firmware_version', 'unknown')
399 device.serial_number = results.get('serial_number', 'unknown')
Chip Bolingab8863d2018-03-22 14:50:31 -0500400 device.images.image.extend(results.get('software-images', []))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600401
Chip Bolingfd1fd372017-12-20 13:34:12 -0600402 device.root = True
403 device.vendor = results.get('vendor', 'Adtran, Inc.')
404 device.connect_status = ConnectStatus.REACHABLE
405 self.adapter_agent.update_device(device)
406
407 except Exception as e:
Chip Boling8956b642018-04-11 13:47:01 -0500408 self.log.exception('device-info', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500409 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600410
411 try:
412 # Enumerate and create Northbound NNI interfaces
413
Chip Bolingce5bfc02018-08-09 13:57:49 -0500414 device.reason = 'enumerating northbound interfaces'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600415 self.adapter_agent.update_device(device)
416 self.startup = self.enumerate_northbound_ports(device)
417 results = yield self.startup
418
419 self.startup = self.process_northbound_ports(device, results)
420 yield self.startup
421
Chip Bolingce5bfc02018-08-09 13:57:49 -0500422 device.reason = 'adding northbound interfaces to adapter'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600423 self.adapter_agent.update_device(device)
424
425 if not reconciling:
426 for port in self.northbound_ports.itervalues():
427 self.adapter_agent.add_port(device.id, port.get_port())
428
429 except Exception as e:
Chip Boling8956b642018-04-11 13:47:01 -0500430 self.log.exception('NNI-enumeration', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500431 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600432
433 try:
434 # Enumerate and create southbound interfaces
435
Chip Bolingce5bfc02018-08-09 13:57:49 -0500436 device.reason = 'enumerating southbound interfaces'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600437 self.adapter_agent.update_device(device)
438 self.startup = self.enumerate_southbound_ports(device)
439 results = yield self.startup
440
441 self.startup = self.process_southbound_ports(device, results)
442 yield self.startup
443
Chip Bolingce5bfc02018-08-09 13:57:49 -0500444 device.reason = 'adding southbound interfaces to adapter'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600445 self.adapter_agent.update_device(device)
446
447 if not reconciling:
448 for port in self.southbound_ports.itervalues():
449 self.adapter_agent.add_port(device.id, port.get_port())
450
451 except Exception as e:
452 self.log.exception('PON_enumeration', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500453 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600454
455 if reconciling:
456 if device.admin_state == AdminState.ENABLED:
457 if device.parent_id:
458 self.logical_device_id = device.parent_id
459 self.adapter_agent.reconcile_logical_device(device.parent_id)
460 else:
461 self.log.info('no-logical-device-set')
462
463 # Reconcile child devices
464 self.adapter_agent.reconcile_child_devices(device.id)
465 ld_initialized = self.adapter_agent.get_logical_device()
466 assert device.parent_id == ld_initialized.id, \
467 'parent ID not Logical device ID'
468
469 else:
470 # Complete activation by setting up logical device for this OLT and saving
471 # off the devices parent_id
472
473 ld_initialized = self.create_logical_device(device)
474
475 ############################################################################
476 # Setup PM configuration for this device
Chip Bolingce5bfc02018-08-09 13:57:49 -0500477 if self.pm_metrics is None:
478 try:
479 device.reason = 'setting up Performance Monitoring configuration'
480 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600481
Chip Bolingce5bfc02018-08-09 13:57:49 -0500482 kwargs = {
483 'nni-ports': self.northbound_ports.values(),
484 'pon-ports': self.southbound_ports.values()
485 }
486 self.pm_metrics = OltPmMetrics(self.adapter_agent, self.device_id,
Chip Bolingc0451382018-08-24 14:21:53 -0500487 ld_initialized.id, grouped=True,
488 freq_override=False, **kwargs)
Chip Bolingbffef5e2018-08-07 14:53:12 -0500489
Chip Bolingce5bfc02018-08-09 13:57:49 -0500490 pm_config = self.pm_metrics.make_proto()
491 self.log.debug("initial-pm-config", pm_config=pm_config)
492 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600493
Chip Bolingce5bfc02018-08-09 13:57:49 -0500494 except Exception as e:
495 self.log.exception('pm-setup', e=e)
496 self.activate_failed(device, e.message, reachable=False)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600497
498 ############################################################################
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500499 # Set the ports in a known good initial state
500 if not reconciling:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500501 device.reason = 'setting device to a known initial state'
502 self.adapter_agent.update_device(device)
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500503 try:
504 for port in self.northbound_ports.itervalues():
505 self.startup = yield port.reset()
506
507 for port in self.southbound_ports.itervalues():
508 self.startup = yield port.reset()
509
510 except Exception as e:
511 self.log.exception('port-reset', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500512 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500513
514 ############################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600515 # Create logical ports for all southbound and northbound interfaces
516 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500517 device.reason = 'creating logical ports'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600518 self.adapter_agent.update_device(device)
519 self.startup = self.create_logical_ports(device, ld_initialized, reconciling)
520 yield self.startup
521
522 except Exception as e:
523 self.log.exception('logical-port', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500524 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600525
526 ############################################################################
Chip Bolingbffef5e2018-08-07 14:53:12 -0500527 # Setup Alarm handler
528
Chip Bolingce5bfc02018-08-09 13:57:49 -0500529 device.reason = 'setting up adapter alarms'
Chip Bolingbffef5e2018-08-07 14:53:12 -0500530 self.adapter_agent.update_device(device)
531
532 self.alarms = AdapterAlarms(self.adapter_agent, device.id, ld_initialized.id)
533
534 ############################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600535 # Register for ONU detection
536 # self.adapter_agent.register_for_onu_detect_state(device.id)
537
538 # Complete device specific steps
539 try:
540 self.log.debug('device-activation-procedures')
Chip Bolingce5bfc02018-08-09 13:57:49 -0500541 device.reason = 'performing model specific activation procedures'
542 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600543 self.startup = self.complete_device_specific_activation(device, reconciling)
544 yield self.startup
545
546 except Exception as e:
547 self.log.exception('device-activation-procedures', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500548 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600549
550 # Schedule the heartbeat for the device
Chip Bolingfd1fd372017-12-20 13:34:12 -0600551 self.log.debug('starting-heartbeat')
552 self.start_heartbeat(delay=10)
553
554 device = self.adapter_agent.get_device(device.id)
555 device.parent_id = ld_initialized.id
556 device.oper_status = OperStatus.ACTIVE
557 device.reason = ''
558 self.adapter_agent.update_device(device)
559 self.logical_device_id = ld_initialized.id
560
Chip Bolingfd1fd372017-12-20 13:34:12 -0600561 # Start collecting stats from the device after a brief pause
Chip Bolingbffef5e2018-08-07 14:53:12 -0500562 reactor.callLater(10, self.pm_metrics.start_collector)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600563
564 # Signal completion
Chip Bolingce5bfc02018-08-09 13:57:49 -0500565 self._initial_enable_complete = True
Chip Bolingfd1fd372017-12-20 13:34:12 -0600566 self.log.info('activated')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500567
568 except Exception as e:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600569 self.log.exception('activate', e=e)
570 if done_deferred is not None:
571 done_deferred.errback(e)
Chip Boling8956b642018-04-11 13:47:01 -0500572
Chip Bolingfd1fd372017-12-20 13:34:12 -0600573 if done_deferred is not None:
574 done_deferred.callback('activated')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500575
Chip Bolingce5bfc02018-08-09 13:57:49 -0500576 returnValue('activated')
577
578 def restart_activate(self, done_deferred, reconciling):
579 """
580 Startup activation failed, pause a short period of time and retry
581
582 :param done_deferred: (deferred) Deferred to fire upon completion of activation
583 :param reconciling: (bool) If true, we are reconciling after moving to a new vCore
584 """
585 d, self.startup = self.startup, None
586 try:
587 if d is not None and not d.called:
588 d.cancel()
589 except:
590 pass
591 device = self.adapter_agent.get_device(self.device_id)
592 device.reason = 'Failed during {}, retrying'.format(device.reason)
593 self.adapter_agent.update_device(device)
594 self.startup = reactor.callLater(_STARTUP_RETRY_TIMEOUT, self.activate,
595 done_deferred, reconciling)
596 return 'retrying'
Chip Boling5561d552017-07-07 15:11:26 -0500597
Chip Boling8956b642018-04-11 13:47:01 -0500598 @inlineCallbacks
599 def ready_network_access(self):
600 # Override in device specific class if needed
601 returnValue('nop')
602
Chip Boling3e3b1a92017-05-16 11:51:18 -0500603 def activate_failed(self, device, reason, reachable=True):
604 """
605 Activation process (adopt_device) has failed.
606
607 :param device: A voltha.Device object, with possible device-type
608 specific extensions. Such extensions shall be described as part of
609 the device type specification returned by device_types().
610 :param reason: (string) failure reason
611 :param reachable: (boolean) Flag indicating if device may be reachable
612 via RESTConf or NETConf even after this failure.
613 """
614 device.oper_status = OperStatus.FAILED
615 if not reachable:
616 device.connect_status = ConnectStatus.UNREACHABLE
617
618 device.reason = reason
619 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600620 raise Exception('Failed to activate OLT: {}'.format(device.reason))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500621
Chip Boling5561d552017-07-07 15:11:26 -0500622 @inlineCallbacks
Chip Bolingff3087f2017-10-12 09:26:29 -0500623 def make_netconf_connection(self, connect_timeout=None,
624 close_existing_client=False):
625
626 if close_existing_client and self._netconf_client is not None:
627 try:
628 yield self._netconf_client.close()
629 except:
630 pass
631 self._netconf_client = None
Chip Boling7294b252017-06-15 16:16:55 -0500632
Chip Boling5561d552017-07-07 15:11:26 -0500633 client = self._netconf_client
634
635 if client is None:
636 if not self.is_virtual_olt:
637 client = AdtranNetconfClient(self.ip_address,
638 self.netconf_port,
639 self.netconf_username,
640 self.netconf_password,
641 self.timeout)
642 else:
643 from voltha.adapters.adtran_olt.net.mock_netconf_client import MockNetconfClient
644 client = MockNetconfClient(self.ip_address,
645 self.netconf_port,
646 self.netconf_username,
647 self.netconf_password,
648 self.timeout)
649 if client.connected:
650 self._netconf_client = client
651 returnValue(True)
Chip Boling7294b252017-06-15 16:16:55 -0500652
653 timeout = connect_timeout or self.timeout
Chip Boling7294b252017-06-15 16:16:55 -0500654
Chip Boling5561d552017-07-07 15:11:26 -0500655 try:
656 request = client.connect(timeout)
657 results = yield request
658 self._netconf_client = client
659 returnValue(results)
660
661 except Exception as e:
662 self.log.exception('Failed to create NETCONF Client', e=e)
663 self._netconf_client = None
664 raise
665
666 @inlineCallbacks
Chip Boling7294b252017-06-15 16:16:55 -0500667 def make_restconf_connection(self, get_timeout=None):
Chip Boling5561d552017-07-07 15:11:26 -0500668 client = self._rest_client
669
670 if client is None:
671 client = AdtranRestClient(self.ip_address,
672 self.rest_port,
673 self.rest_username,
674 self.rest_password,
675 self.timeout)
Chip Boling7294b252017-06-15 16:16:55 -0500676
677 timeout = get_timeout or self.timeout
Chip Boling5561d552017-07-07 15:11:26 -0500678
679 try:
680 request = client.request('GET', self.HELLO_URI, name='hello', timeout=timeout)
681 results = yield request
682 if isinstance(results, dict) and 'module-info' in results:
683 self._rest_client = client
684 returnValue(results)
685 else:
686 from twisted.internet.error import ConnectError
687 self._rest_client = None
688 raise ConnectError(string='Results received but unexpected data type or contents')
689 except Exception:
690 self._rest_client = None
691 raise
Chip Boling7294b252017-06-15 16:16:55 -0500692
693 def create_logical_device(self, device):
Chip Boling5561d552017-07-07 15:11:26 -0500694 version = device.images.image[0].version
695
Chip Boling7294b252017-06-15 16:16:55 -0500696 ld = LogicalDevice(
697 # NOTE: not setting id and datapath_id will let the adapter agent pick id
Chip Bolingb1ddfc42018-10-16 14:17:32 -0500698 desc=ofp_desc(mfr_desc='VOLTHA Project',
Chip Boling7294b252017-06-15 16:16:55 -0500699 hw_desc=device.hardware_version,
Chip Boling5561d552017-07-07 15:11:26 -0500700 sw_desc=version,
Chip Boling7294b252017-06-15 16:16:55 -0500701 serial_num=device.serial_number,
702 dp_desc='n/a'),
Chip Boling27275992017-09-22 15:17:04 -0500703 switch_features=ofp_switch_features(n_buffers=256,
704 n_tables=2,
Chip Boling7294b252017-06-15 16:16:55 -0500705 capabilities=(
Chip Boling5561d552017-07-07 15:11:26 -0500706 OFPC_FLOW_STATS |
707 OFPC_TABLE_STATS |
708 OFPC_GROUP_STATS |
Chip Boling7294b252017-06-15 16:16:55 -0500709 OFPC_PORT_STATS)),
710 root_device_id=device.id)
711
Chip Bolingfd1fd372017-12-20 13:34:12 -0600712 ld_initialized = self.adapter_agent.create_logical_device(ld,
Chip Bolingbffef5e2018-08-07 14:53:12 -0500713 dpid=self.mac_address)
Chip Boling7294b252017-06-15 16:16:55 -0500714 return ld_initialized
715
716 @inlineCallbacks
717 def create_logical_ports(self, device, ld_initialized, reconciling):
Chip Boling7294b252017-06-15 16:16:55 -0500718 if not reconciling:
Chip Boling27275992017-09-22 15:17:04 -0500719 # Add the ports to the logical device
720
Chip Boling7294b252017-06-15 16:16:55 -0500721 for port in self.northbound_ports.itervalues():
722 lp = port.get_logical_port()
723 if lp is not None:
724 self.adapter_agent.add_logical_port(ld_initialized.id, lp)
725
726 for port in self.southbound_ports.itervalues():
727 lp = port.get_logical_port()
728 if lp is not None:
729 self.adapter_agent.add_logical_port(ld_initialized.id, lp)
730
Chip Bolingab8863d2018-03-22 14:50:31 -0500731 # Clean up all EVCs, EVC maps and ACLs (exceptions are ok)
Chip Boling252c7772017-08-16 10:13:17 -0500732 try:
733 from flow.evc import EVC
734 self.startup = yield EVC.remove_all(self.netconf_client)
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500735 from flow.utility_evc import UtilityEVC
736 self.startup = yield UtilityEVC.remove_all(self.netconf_client)
737 from flow.untagged_evc import UntaggedEVC
738 self.startup = yield UntaggedEVC.remove_all(self.netconf_client)
Chip Boling252c7772017-08-16 10:13:17 -0500739
740 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500741 self.log.exception('evc-cleanup', e=e)
Chip Boling252c7772017-08-16 10:13:17 -0500742
743 try:
744 from flow.evc_map import EVCMap
745 self.startup = yield EVCMap.remove_all(self.netconf_client)
746
747 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500748 self.log.exception('evc-map-cleanup', e=e)
Chip Boling252c7772017-08-16 10:13:17 -0500749
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500750 from flow.acl import ACL
751 ACL.clear_all(device.id)
Chip Bolingab8863d2018-03-22 14:50:31 -0500752 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500753 self.startup = yield ACL.remove_all(self.netconf_client)
754
755 except Exception as e:
756 self.log.exception('acl-cleanup', e=e)
757
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500758 from flow.flow_entry import FlowEntry
759 FlowEntry.clear_all(device.id)
760
Chip Bolingbffef5e2018-08-07 14:53:12 -0500761 from download import Download
762 Download.clear_all(self.netconf_client)
763
Chip Boling27275992017-09-22 15:17:04 -0500764 # Start/stop the interfaces as needed. These are deferred calls
Chip Boling7294b252017-06-15 16:16:55 -0500765
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500766 dl = []
767 for port in self.northbound_ports.itervalues():
768 try:
Chip Boling27275992017-09-22 15:17:04 -0500769 dl.append(port.start())
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500770 except Exception as e:
771 self.log.exception('northbound-port-startup', e=e)
Chip Boling7294b252017-06-15 16:16:55 -0500772
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500773 for port in self.southbound_ports.itervalues():
774 try:
Chip Boling27275992017-09-22 15:17:04 -0500775 dl.append(port.start() if port.admin_state == AdminState.ENABLED else port.stop())
776
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500777 except Exception as e:
778 self.log.exception('southbound-port-startup', e=e)
Chip Boling27275992017-09-22 15:17:04 -0500779
Chip Bolingff3087f2017-10-12 09:26:29 -0500780 results = yield defer.gatherResults(dl, consumeErrors=True)
Chip Boling7294b252017-06-15 16:16:55 -0500781
Chip Boling5561d552017-07-07 15:11:26 -0500782 returnValue(results)
Chip Boling7294b252017-06-15 16:16:55 -0500783
784 @inlineCallbacks
785 def device_information(self, device):
786 """
787 Examine the various managment models and extract device information for
788 VOLTHA use
789
790 :param device: A voltha.Device object, with possible device-type
791 specific extensions.
792 :return: (Deferred or None).
793 """
794 yield defer.Deferred(lambda c: c.callback("Not Required"))
795
Chip Boling3e3b1a92017-05-16 11:51:18 -0500796 @inlineCallbacks
797 def enumerate_northbound_ports(self, device):
798 """
799 Enumerate all northbound ports of a device. You should override
Chip Boling3e3b1a92017-05-16 11:51:18 -0500800 a non-recoverable error, throw an appropriate exception.
801
802 :param device: A voltha.Device object, with possible device-type
803 specific extensions.
804 :return: (Deferred or None).
805 """
806 yield defer.Deferred(lambda c: c.callback("Not Required"))
807
808 @inlineCallbacks
809 def process_northbound_ports(self, device, results):
810 """
811 Process the results from the 'enumerate_northbound_ports' method.
812 You should override this method in your derived class as necessary and
813 create an NNI Port object (of your own choosing) that supports a 'get_port'
814 method. Once created, insert it into this base class's northbound_ports
815 collection.
816
817 Should you encounter a non-recoverable error, throw an appropriate exception.
818
819 :param device: A voltha.Device object, with possible device-type
820 specific extensions.
821 :param results: Results from the 'enumerate_northbound_ports' method that
822 you implemented. The type and contents are up to you to
823 :return:
824 """
825 yield defer.Deferred(lambda c: c.callback("Not Required"))
826
827 @inlineCallbacks
828 def enumerate_southbound_ports(self, device):
829 """
830 Enumerate all southbound ports of a device. You should override
831 this method in your derived class as necessary. Should you encounter
832 a non-recoverable error, throw an appropriate exception.
833
834 :param device: A voltha.Device object, with possible device-type
835 specific extensions.
836 :return: (Deferred or None).
837 """
838 yield defer.Deferred(lambda c: c.callback("Not Required"))
839
840 @inlineCallbacks
841 def process_southbound_ports(self, device, results):
842 """
843 Process the results from the 'enumerate_southbound_ports' method.
844 You should override this method in your derived class as necessary and
845 create an Port object (of your own choosing) that supports a 'get_port'
846 method. Once created, insert it into this base class's southbound_ports
847 collection.
848
849 Should you encounter a non-recoverable error, throw an appropriate exception.
850
851 :param device: A voltha.Device object, with possible device-type
852 specific extensions.
853 :param results: Results from the 'enumerate_southbound_ports' method that
854 you implemented. The type and contents are up to you to
855 :return:
856 """
857 yield defer.Deferred(lambda c: c.callback("Not Required"))
858
Chip Boling5561d552017-07-07 15:11:26 -0500859 # TODO: Move some of the items below from here and the EVC to a utility class
860
861 def is_nni_port(self, port):
862 return port in self.northbound_ports
863
864 def is_uni_port(self, port):
865 raise NotImplementedError('implement in derived class')
866
867 def is_pon_port(self, port):
868 raise NotImplementedError('implement in derived class')
869
870 def is_logical_port(self, port):
871 return not self.is_nni_port(port) and not self.is_uni_port(port) and not self.is_pon_port(port)
872
873 def get_port_name(self, port):
874 raise NotImplementedError('implement in derived class')
875
Chip Boling3e3b1a92017-05-16 11:51:18 -0500876 @inlineCallbacks
Chip Boling7294b252017-06-15 16:16:55 -0500877 def complete_device_specific_activation(self, _device, _reconciling):
Chip Bolingab8863d2018-03-22 14:50:31 -0500878 # NOTE: Override this in your derived class for any device startup completion
Chip Boling5561d552017-07-07 15:11:26 -0500879 return defer.succeed('NOP')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500880
Chip Boling7294b252017-06-15 16:16:55 -0500881 def disable(self):
882 """
883 This is called when a previously enabled device needs to be disabled based on a NBI call.
884 """
885 self.log.info('disabling', device_id=self.device_id)
886
Chip Boling69fba862017-08-18 15:11:32 -0500887 # Cancel any running enable/disable/... in progress
888 d, self.startup = self.startup, None
Chip Boling27275992017-09-22 15:17:04 -0500889 try:
890 if d is not None and not d.called:
891 d.cancel()
892 except:
893 pass
Chip Bolingce5bfc02018-08-09 13:57:49 -0500894
Chip Boling7294b252017-06-15 16:16:55 -0500895 # Get the latest device reference
896 device = self.adapter_agent.get_device(self.device_id)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500897 device.reason = 'Disabling'
898 self.adapter_agent.update_device(device)
Chip Boling7294b252017-06-15 16:16:55 -0500899
Chip Boling27275992017-09-22 15:17:04 -0500900 # Drop registration for ONU detection
901 # self.adapter_agent.unregister_for_onu_detect_state(self.device.id)
902
Chip Boling7294b252017-06-15 16:16:55 -0500903 # Suspend any active healthchecks / pings
904
905 h, self.heartbeat = self.heartbeat, None
Chip Boling27275992017-09-22 15:17:04 -0500906 try:
907 if h is not None and not h.called:
908 h.cancel()
909 except:
910 pass
Chip Boling7294b252017-06-15 16:16:55 -0500911 # Update the operational status to UNKNOWN
912
913 device.oper_status = OperStatus.UNKNOWN
914 device.connect_status = ConnectStatus.UNREACHABLE
915 self.adapter_agent.update_device(device)
916
Chip Boling7294b252017-06-15 16:16:55 -0500917 # Disable all child devices first
918 self.adapter_agent.update_child_devices_state(self.device_id,
919 admin_state=AdminState.DISABLED)
920
921 # Remove the peer references from this device
922 self.adapter_agent.delete_all_peer_references(self.device_id)
923
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500924 # Remove the logical device
925 self._delete_logical_device()
926
Chip Boling7294b252017-06-15 16:16:55 -0500927 # Set all ports to disabled
928 self.adapter_agent.disable_all_ports(self.device_id)
929
Chip Boling5561d552017-07-07 15:11:26 -0500930 dl = []
Chip Boling7294b252017-06-15 16:16:55 -0500931 for port in self.northbound_ports.itervalues():
Chip Boling5561d552017-07-07 15:11:26 -0500932 dl.append(port.stop())
Chip Boling7294b252017-06-15 16:16:55 -0500933
934 for port in self.southbound_ports.itervalues():
Chip Boling5561d552017-07-07 15:11:26 -0500935 dl.append(port.stop())
Chip Boling7294b252017-06-15 16:16:55 -0500936
Chip Boling69fba862017-08-18 15:11:32 -0500937 # NOTE: Flows removed before this method is called
938 # Wait for completion
939
Chip Bolingfd1fd372017-12-20 13:34:12 -0600940 self.startup = defer.gatherResults(dl, consumeErrors=True)
Chip Boling7294b252017-06-15 16:16:55 -0500941
Chip Boling69fba862017-08-18 15:11:32 -0500942 def _drop_netconf():
943 return self.netconf_client.close() if \
944 self.netconf_client is not None else defer.succeed('NOP')
Chip Boling7294b252017-06-15 16:16:55 -0500945
946 def _null_clients():
Chip Boling5561d552017-07-07 15:11:26 -0500947 self._netconf_client = None
948 self._rest_client = None
Chip Boling7294b252017-06-15 16:16:55 -0500949
Chip Boling69fba862017-08-18 15:11:32 -0500950 # Shutdown communications with OLT
Chip Boling69fba862017-08-18 15:11:32 -0500951 self.startup.addCallbacks(_drop_netconf, _null_clients)
952 self.startup.addCallbacks(_null_clients, _null_clients)
953
Chip Bolingce5bfc02018-08-09 13:57:49 -0500954 device.reason = ''
955 self.adapter_agent.update_device(device)
Chip Boling7294b252017-06-15 16:16:55 -0500956 self.log.info('disabled', device_id=device.id)
Chip Boling69fba862017-08-18 15:11:32 -0500957 return self.startup
Chip Boling7294b252017-06-15 16:16:55 -0500958
959 @inlineCallbacks
Chip Bolingfd1fd372017-12-20 13:34:12 -0600960 def reenable(self, done_deferred=None):
Chip Boling7294b252017-06-15 16:16:55 -0500961 """
962 This is called when a previously disabled device needs to be enabled based on a NBI call.
Chip Bolingfd1fd372017-12-20 13:34:12 -0600963 :param done_deferred: (Deferred) Deferred to fire when done
Chip Boling7294b252017-06-15 16:16:55 -0500964 """
965 self.log.info('re-enabling', device_id=self.device_id)
966
Chip Boling69fba862017-08-18 15:11:32 -0500967 # Cancel any running enable/disable/... in progress
968 d, self.startup = self.startup, None
Chip Boling27275992017-09-22 15:17:04 -0500969 try:
970 if d is not None and not d.called:
971 d.cancel()
972 except:
973 pass
Chip Bolingce5bfc02018-08-09 13:57:49 -0500974
975 if not self._initial_enable_complete:
976 # Never contacted the device on the initial startup, do 'activate' steps instead
977 self.startup = reactor.callLater(0, self.activate, done_deferred, False)
978 returnValue('activating')
979
Chip Boling7294b252017-06-15 16:16:55 -0500980 # Get the latest device reference
981 device = self.adapter_agent.get_device(self.device_id)
982
983 # Update the connect status to REACHABLE
984 device.connect_status = ConnectStatus.REACHABLE
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500985 device.oper_status = OperStatus.ACTIVATING
Chip Boling7294b252017-06-15 16:16:55 -0500986 self.adapter_agent.update_device(device)
987
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500988 # Reenable any previously configured southbound ports
989 for port in self.southbound_ports.itervalues():
990 self.log.debug('reenable-checking-pon-port', pon_id=port.pon_id)
Chip Boling7294b252017-06-15 16:16:55 -0500991
Chip Boling88354fb2018-09-21 12:17:19 -0500992 gpon_info = self.get_xpon_info(port.pon_id) # SEBA
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500993 if gpon_info is not None and \
994 gpon_info['channel-terminations'] is not None and \
995 len(gpon_info['channel-terminations']) > 0:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600996
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500997 cterms = gpon_info['channel-terminations']
998 if any(term.get('enabled') for term in cterms.itervalues()):
999 self.log.info('reenable', pon_id=port.pon_id)
1000 port.enabled = True
1001
1002 # Flows should not exist on re-enable. They are re-pushed
1003 if len(self._evcs):
1004 self.log.warn('evcs-found', evcs=self._evcs)
1005 self._evcs.clear()
Chip Bolingfd1fd372017-12-20 13:34:12 -06001006
Chip Boling7294b252017-06-15 16:16:55 -05001007 try:
1008 yield self.make_restconf_connection()
1009
1010 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -05001011 self.log.exception('adtran-hello-reconnect', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001012
Chip Boling5561d552017-07-07 15:11:26 -05001013 try:
1014 yield self.make_netconf_connection()
Chip Boling7294b252017-06-15 16:16:55 -05001015
Chip Boling5561d552017-07-07 15:11:26 -05001016 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -05001017 self.log.exception('NETCONF-re-connection', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001018
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001019 # Recreate the logical device
1020 # NOTE: This causes a flow update event
1021 ld_initialized = self.create_logical_device(device)
Chip Boling7294b252017-06-15 16:16:55 -05001022
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001023 # Create logical ports for all southbound and northbound interfaces
Chip Boling27275992017-09-22 15:17:04 -05001024 try:
1025 self.startup = self.create_logical_ports(device, ld_initialized, False)
1026 yield self.startup
1027
1028 except Exception as e:
1029 self.log.exception('logical-port-creation', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001030
1031 device = self.adapter_agent.get_device(device.id)
1032 device.parent_id = ld_initialized.id
1033 device.oper_status = OperStatus.ACTIVE
Chip Boling5561d552017-07-07 15:11:26 -05001034 device.reason = ''
Chip Boling7294b252017-06-15 16:16:55 -05001035 self.logical_device_id = ld_initialized.id
1036
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001037 # update device active status now
1038 self.adapter_agent.update_device(device)
1039
Chip Boling7294b252017-06-15 16:16:55 -05001040 # Reenable all child devices
1041 self.adapter_agent.update_child_devices_state(device.id,
1042 admin_state=AdminState.ENABLED)
Chip Boling7294b252017-06-15 16:16:55 -05001043
Chip Boling27275992017-09-22 15:17:04 -05001044 # Re-subscribe for ONU detection
1045 # self.adapter_agent.register_for_onu_detect_state(self.device.id)
1046
Chip Boling7294b252017-06-15 16:16:55 -05001047 # TODO:
1048 # 1) Restart health check / pings
Chip Boling5561d552017-07-07 15:11:26 -05001049
Chip Boling7294b252017-06-15 16:16:55 -05001050 self.log.info('re-enabled', device_id=device.id)
Chip Bolingfd1fd372017-12-20 13:34:12 -06001051
1052 if done_deferred is not None:
1053 done_deferred.callback('Done')
1054
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001055 returnValue('reenabled')
Chip Boling7294b252017-06-15 16:16:55 -05001056
1057 @inlineCallbacks
1058 def reboot(self):
1059 """
1060 This is called to reboot a device based on a NBI call. The admin state of the device
1061 will not change after the reboot.
1062 """
1063 self.log.debug('reboot')
1064
Chip Bolingce5bfc02018-08-09 13:57:49 -05001065 if not self._initial_enable_complete:
1066 # Never contacted the device on the initial startup, do 'activate' steps instead
1067 returnValue('failed')
1068
Chip Boling69fba862017-08-18 15:11:32 -05001069 # Cancel any running enable/disable/... in progress
1070 d, self.startup = self.startup, None
Chip Boling27275992017-09-22 15:17:04 -05001071 try:
1072 if d is not None and not d.called:
1073 d.cancel()
1074 except:
1075 pass
1076 # Issue reboot command
1077
1078 if not self.is_virtual_olt:
1079 try:
1080 yield self.netconf_client.rpc(AdtranDeviceHandler.RESTART_RPC)
1081
1082 except Exception as e:
1083 self.log.exception('NETCONF-shutdown', e=e)
1084 returnValue(defer.fail(Failure()))
1085
1086 # self.adapter_agent.unregister_for_onu_detect_state(self.device.id)
Chip Boling69fba862017-08-18 15:11:32 -05001087
Chip Boling7294b252017-06-15 16:16:55 -05001088 # Update the operational status to ACTIVATING and connect status to
1089 # UNREACHABLE
1090
1091 device = self.adapter_agent.get_device(self.device_id)
1092 previous_oper_status = device.oper_status
1093 previous_conn_status = device.connect_status
1094 device.oper_status = OperStatus.ACTIVATING
1095 device.connect_status = ConnectStatus.UNREACHABLE
1096 self.adapter_agent.update_device(device)
1097
1098 # Update the child devices connect state to UNREACHABLE
1099 self.adapter_agent.update_child_devices_state(self.device_id,
1100 connect_status=ConnectStatus.UNREACHABLE)
Chip Boling7294b252017-06-15 16:16:55 -05001101
Chip Boling27275992017-09-22 15:17:04 -05001102 # Shutdown communications with OLT. Typically it takes about 2 seconds
1103 # or so after the reply before the restart actually occurs
Chip Boling7294b252017-06-15 16:16:55 -05001104
Chip Boling27275992017-09-22 15:17:04 -05001105 try:
1106 response = yield self.netconf_client.close()
1107 self.log.debug('Restart response XML was: {}'.format('ok' if response.ok else 'bad'))
Chip Boling7294b252017-06-15 16:16:55 -05001108
Chip Boling27275992017-09-22 15:17:04 -05001109 except Exception as e:
1110 self.log.exception('NETCONF-client-shutdown', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001111
Chip Boling27275992017-09-22 15:17:04 -05001112 # Clear off clients
Chip Boling7294b252017-06-15 16:16:55 -05001113
Chip Boling5561d552017-07-07 15:11:26 -05001114 self._netconf_client = None
1115 self._rest_client = None
Chip Boling7294b252017-06-15 16:16:55 -05001116
1117 # Run remainder of reboot process as a new task. The OLT then may be up in a
1118 # few moments or may take 3 minutes or more depending on any self tests enabled
1119
Chip Boling5561d552017-07-07 15:11:26 -05001120 current_time = time.time()
Chip Boling7294b252017-06-15 16:16:55 -05001121 timeout = current_time + self.restart_failure_timeout
1122
Chip Boling27275992017-09-22 15:17:04 -05001123 self.startup = reactor.callLater(10, self._finish_reboot, timeout,
1124 previous_oper_status,
1125 previous_conn_status)
1126 returnValue(self.startup)
Chip Boling7294b252017-06-15 16:16:55 -05001127
1128 @inlineCallbacks
1129 def _finish_reboot(self, timeout, previous_oper_status, previous_conn_status):
1130 # Now wait until REST & NETCONF are re-established or we timeout
1131
Chip Boling252c7772017-08-16 10:13:17 -05001132 self.log.info('Resuming-activity',
Chip Boling5561d552017-07-07 15:11:26 -05001133 remaining=timeout - time.time(), timeout=timeout, current=time.time())
1134
1135 if self.rest_client is None:
Chip Boling7294b252017-06-15 16:16:55 -05001136 try:
Chip Boling27275992017-09-22 15:17:04 -05001137 yield self.make_restconf_connection(get_timeout=10)
Chip Boling5561d552017-07-07 15:11:26 -05001138
1139 except Exception:
1140 self.log.debug('No RESTCONF connection yet')
1141 self._rest_client = None
1142
1143 if self.netconf_client is None:
1144 try:
1145 yield self.make_netconf_connection(connect_timeout=10)
Chip Boling7294b252017-06-15 16:16:55 -05001146
1147 except Exception as e:
Chip Boling7294b252017-06-15 16:16:55 -05001148 try:
Chip Boling5561d552017-07-07 15:11:26 -05001149 if self.netconf_client is not None:
1150 yield self.netconf_client.close()
Chip Boling7294b252017-06-15 16:16:55 -05001151 except Exception as e:
1152 self.log.exception(e.message)
1153 finally:
Chip Boling5561d552017-07-07 15:11:26 -05001154 self._netconf_client = None
Chip Boling7294b252017-06-15 16:16:55 -05001155
1156 if (self.netconf_client is None and not self.is_virtual_olt) or self.rest_client is None:
Chip Boling5561d552017-07-07 15:11:26 -05001157 current_time = time.time()
Chip Boling7294b252017-06-15 16:16:55 -05001158 if current_time < timeout:
Chip Boling27275992017-09-22 15:17:04 -05001159 self.startup = reactor.callLater(5, self._finish_reboot, timeout,
1160 previous_oper_status,
1161 previous_conn_status)
1162 returnValue(self.startup)
Chip Boling7294b252017-06-15 16:16:55 -05001163
1164 if self.netconf_client is None and not self.is_virtual_olt:
Chip Boling252c7772017-08-16 10:13:17 -05001165 self.log.error('NETCONF-restore-failure')
Chip Boling7294b252017-06-15 16:16:55 -05001166 pass # TODO: What is best course of action if cannot get clients back?
1167
1168 if self.rest_client is None:
Chip Boling252c7772017-08-16 10:13:17 -05001169 self.log.error('RESTCONF-restore-failure')
Chip Boling7294b252017-06-15 16:16:55 -05001170 pass # TODO: What is best course of action if cannot get clients back?
1171
Chip Boling5561d552017-07-07 15:11:26 -05001172 # Pause additional 5 seconds to let allow OLT microservices to complete some more initialization
Chip Boling7294b252017-06-15 16:16:55 -05001173 yield asleep(5)
Chip Bolingab8863d2018-03-22 14:50:31 -05001174 # TODO: Update device info. The software images may have changed...
Chip Boling7294b252017-06-15 16:16:55 -05001175 # Get the latest device reference
1176
1177 device = self.adapter_agent.get_device(self.device_id)
1178 device.oper_status = previous_oper_status
1179 device.connect_status = previous_conn_status
1180 self.adapter_agent.update_device(device)
1181
1182 # Update the child devices connect state to REACHABLE
1183 self.adapter_agent.update_child_devices_state(self.device_id,
1184 connect_status=ConnectStatus.REACHABLE)
Chip Boling69fba862017-08-18 15:11:32 -05001185 # Restart ports to previous state
1186
1187 dl = []
1188
1189 for port in self.northbound_ports.itervalues():
1190 dl.append(port.restart())
1191
1192 for port in self.southbound_ports.itervalues():
1193 dl.append(port.restart())
1194
1195 try:
Chip Bolingfd1fd372017-12-20 13:34:12 -06001196 yield defer.gatherResults(dl, consumeErrors=True)
Chip Boling69fba862017-08-18 15:11:32 -05001197 except Exception as e:
1198 self.log.exception('port-restart', e=e)
1199
Chip Boling27275992017-09-22 15:17:04 -05001200 # Re-subscribe for ONU detection
1201 # self.adapter_agent.register_for_onu_detect_state(self.device.id)
1202
Chip Boling69fba862017-08-18 15:11:32 -05001203 # Request reflow of any EVC/EVC-MAPs
1204
1205 if len(self._evcs) > 0:
1206 dl = []
1207 for evc in self.evcs:
1208 dl.append(evc.reflow())
1209
1210 try:
1211 yield defer.gatherResults(dl)
1212 except Exception as e:
1213 self.log.exception('flow-restart', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001214
Chip Boling7294b252017-06-15 16:16:55 -05001215 self.log.info('rebooted', device_id=self.device_id)
Chip Boling5561d552017-07-07 15:11:26 -05001216 returnValue('Rebooted')
Chip Boling7294b252017-06-15 16:16:55 -05001217
1218 @inlineCallbacks
1219 def delete(self):
1220 """
1221 This is called to delete a device from the PON based on a NBI call.
1222 If the device is an OLT then the whole PON will be deleted.
1223 """
1224 self.log.info('deleting', device_id=self.device_id)
1225
1226 # Cancel any outstanding tasks
1227
1228 d, self.startup = self.startup, None
Chip Boling27275992017-09-22 15:17:04 -05001229 try:
1230 if d is not None and not d.called:
1231 d.cancel()
1232 except:
1233 pass
Chip Boling7294b252017-06-15 16:16:55 -05001234 h, self.heartbeat = self.heartbeat, None
Chip Boling27275992017-09-22 15:17:04 -05001235 try:
1236 if h is not None and not h.called:
1237 h.cancel()
1238 except:
1239 pass
Chip Bolingce5bfc02018-08-09 13:57:49 -05001240
1241 # Get the latest device reference
1242 device = self.adapter_agent.get_device(self.device_id)
1243 device.reason = 'Deleting'
1244 self.adapter_agent.update_device(device)
1245
Chip Boling27275992017-09-22 15:17:04 -05001246 # self.adapter_agent.unregister_for_onu_detect_state(self.device.id)
Chip Boling7294b252017-06-15 16:16:55 -05001247
Chip Boling5561d552017-07-07 15:11:26 -05001248 # Remove all flows from the device
1249 # TODO: Create a bulk remove-all by device-id
Chip Boling7294b252017-06-15 16:16:55 -05001250
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001251 evcs = self._evcs
Chip Boling69fba862017-08-18 15:11:32 -05001252 self._evcs.clear()
Chip Boling5561d552017-07-07 15:11:26 -05001253
Chip Boling69fba862017-08-18 15:11:32 -05001254 for evc in evcs:
1255 evc.delete() # TODO: implement bulk-flow procedures
Chip Boling7294b252017-06-15 16:16:55 -05001256
1257 # Remove all child devices
1258 self.adapter_agent.delete_all_child_devices(self.device_id)
1259
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001260 # Remove the logical device (should already be gone if disable came first)
1261 self._delete_logical_device()
Chip Boling7294b252017-06-15 16:16:55 -05001262
1263 # Remove the peer references from this device
1264 self.adapter_agent.delete_all_peer_references(self.device_id)
1265
1266 # Tell all ports to stop any background processing
1267
1268 for port in self.northbound_ports.itervalues():
1269 port.delete()
1270
1271 for port in self.southbound_ports.itervalues():
1272 port.delete()
1273
1274 self.northbound_ports.clear()
1275 self.southbound_ports.clear()
1276
1277 # Shutdown communications with OLT
1278
1279 if self.netconf_client is not None:
1280 try:
1281 yield self.netconf_client.close()
1282 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -05001283 self.log.exception('NETCONF-shutdown', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001284
Chip Boling5561d552017-07-07 15:11:26 -05001285 self._netconf_client = None
Chip Boling7294b252017-06-15 16:16:55 -05001286
Chip Boling5561d552017-07-07 15:11:26 -05001287 self._rest_client = None
Chip Boling7294b252017-06-15 16:16:55 -05001288
1289 self.log.info('deleted', device_id=self.device_id)
1290
Chip Boling5561d552017-07-07 15:11:26 -05001291 def packet_out(self, egress_port, msg):
Chip Bolingab8863d2018-03-22 14:50:31 -05001292 raise NotImplementedError('Overload in a derived class')
Chip Boling5561d552017-07-07 15:11:26 -05001293
1294 def update_pm_config(self, device, pm_config):
1295 # TODO: This has not been tested
1296 self.log.info('update_pm_config', pm_config=pm_config)
1297 self.pm_metrics.update(pm_config)
1298
Chip Boling3e3b1a92017-05-16 11:51:18 -05001299 @inlineCallbacks
1300 def get_device_info(self, device):
1301 """
1302 Perform an initial network operation to discover the device hardware
1303 and software version. Serial Number would be helpful as well.
1304
1305 Upon successfully retrieving the information, remember to call the
1306 'start_heartbeat' method to keep in contact with the device being managed
1307
1308 :param device: A voltha.Device object, with possible device-type
1309 specific extensions. Such extensions shall be described as part of
1310 the device type specification returned by device_types().
1311 """
Chip Boling7294b252017-06-15 16:16:55 -05001312 device = {}
Chip Boling7294b252017-06-15 16:16:55 -05001313 returnValue(device)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001314
1315 def start_heartbeat(self, delay=10):
Chip Boling27275992017-09-22 15:17:04 -05001316 assert delay > 1, 'Minimum heartbeat is 1 second'
Chip Boling252c7772017-08-16 10:13:17 -05001317 self.log.info('Starting-Device-Heartbeat ***')
Chip Boling3e3b1a92017-05-16 11:51:18 -05001318 self.heartbeat = reactor.callLater(delay, self.check_pulse)
Chip Boling5561d552017-07-07 15:11:26 -05001319 return self.heartbeat
Chip Boling3e3b1a92017-05-16 11:51:18 -05001320
1321 def check_pulse(self):
1322 if self.logical_device_id is not None:
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001323 try:
1324 self.heartbeat = self.rest_client.request('GET', self.HELLO_URI,
1325 name='hello', timeout=5)
1326 self.heartbeat.addCallbacks(self._heartbeat_success, self._heartbeat_fail)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001327
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001328 except Exception as e:
1329 self.heartbeat = reactor.callLater(5, self._heartbeat_fail, e)
1330
Chip Bolingfd1fd372017-12-20 13:34:12 -06001331 def on_heatbeat_alarm(self, active):
1332 if active and self.netconf_client is None or not self.netconf_client.connected:
1333 self.make_netconf_connection(close_existing_client=True)
1334
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001335 def heartbeat_check_status(self, _):
Chip Boling3e3b1a92017-05-16 11:51:18 -05001336 """
1337 Check the number of heartbeat failures against the limit and emit an alarm if needed
1338 """
1339 device = self.adapter_agent.get_device(self.device_id)
1340
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001341 try:
Chip Bolingbffef5e2018-08-07 14:53:12 -05001342 from voltha.extensions.alarms.heartbeat_alarm import HeartbeatAlarm
Chip Bolingfd1fd372017-12-20 13:34:12 -06001343
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001344 if self.heartbeat_miss >= self.heartbeat_failed_limit:
1345 if device.connect_status == ConnectStatus.REACHABLE:
1346 self.log.warning('heartbeat-failed', count=self.heartbeat_miss)
1347 device.connect_status = ConnectStatus.UNREACHABLE
1348 device.oper_status = OperStatus.FAILED
1349 device.reason = self.heartbeat_last_reason
1350 self.adapter_agent.update_device(device)
Chip Bolingbffef5e2018-08-07 14:53:12 -05001351 HeartbeatAlarm(self.alarms, 'olt', self.heartbeat_miss).raise_alarm()
Chip Bolingfd1fd372017-12-20 13:34:12 -06001352 self.on_heatbeat_alarm(True)
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001353 else:
1354 # Update device states
1355 if device.connect_status != ConnectStatus.REACHABLE:
1356 device.connect_status = ConnectStatus.REACHABLE
1357 device.oper_status = OperStatus.ACTIVE
1358 device.reason = ''
1359 self.adapter_agent.update_device(device)
Chip Bolingbffef5e2018-08-07 14:53:12 -05001360 HeartbeatAlarm(self.alarms, 'olt').clear_alarm()
Chip Bolingfd1fd372017-12-20 13:34:12 -06001361 self.on_heatbeat_alarm(False)
Chip Bolingff3087f2017-10-12 09:26:29 -05001362
1363 if self.netconf_client is None or not self.netconf_client.connected:
1364 self.make_netconf_connection(close_existing_client=True)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001365
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001366 except Exception as e:
1367 self.log.exception('heartbeat-check', e=e)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001368
1369 # Reschedule next heartbeat
1370 if self.logical_device_id is not None:
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001371 self.heartbeat_count += 1
Chip Boling3e3b1a92017-05-16 11:51:18 -05001372 self.heartbeat = reactor.callLater(self.heartbeat_interval, self.check_pulse)
1373
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001374 def _heartbeat_success(self, results):
1375 self.log.debug('heartbeat-success')
1376 self.heartbeat_miss = 0
1377 self.heartbeat_last_reason = ''
1378 self.heartbeat_check_status(results)
1379
1380 def _heartbeat_fail(self, failure):
Chip Boling3e3b1a92017-05-16 11:51:18 -05001381 self.heartbeat_miss += 1
1382 self.log.info('heartbeat-miss', failure=failure,
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001383 count=self.heartbeat_count,
1384 miss=self.heartbeat_miss)
1385 self.heartbeat_last_reason = 'RESTCONF connectivity error'
Chip Boling3e3b1a92017-05-16 11:51:18 -05001386 self.heartbeat_check_status(None)
1387
Chip Boling3e3b1a92017-05-16 11:51:18 -05001388 @staticmethod
1389 def parse_module_revision(revision):
1390 try:
1391 return datetime.datetime.strptime(revision, '%Y-%m-%d')
1392 except Exception:
1393 return None