blob: 5a3598f9fd2a9ccc20a19bd2649d9d0a1c10db58 [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
Chip Boling3e3b1a92017-05-16 11:51:18 -050022import structlog
23from twisted.internet import reactor, defer
Chip Boling7294b252017-06-15 16:16:55 -050024from twisted.internet.defer import inlineCallbacks, returnValue
Chip Boling27275992017-09-22 15:17:04 -050025from twisted.python.failure import Failure
Chip Boling3e3b1a92017-05-16 11:51:18 -050026
Chip Boling7294b252017-06-15 16:16:55 -050027from voltha.adapters.adtran_olt.net.adtran_netconf import AdtranNetconfClient
Chip Boling3e3b1a92017-05-16 11:51:18 -050028from voltha.adapters.adtran_olt.net.adtran_rest import AdtranRestClient
29from voltha.protos import third_party
30from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
Chip Boling3e3b1a92017-05-16 11:51:18 -050031from voltha.protos.logical_device_pb2 import LogicalDevice
32from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_switch_features, OFPC_PORT_STATS, \
33 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
Chip Bolingbffef5e2018-08-07 14:53:12 -050034from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
Chip Bolingce5bfc02018-08-09 13:57:49 -050035from voltha.extensions.kpi.olt.olt_pm_metrics import OltPmMetrics
Chip Boling7294b252017-06-15 16:16:55 -050036from common.utils.asleep import asleep
Chip Bolingb4868192018-11-01 16:38:44 -050037from flow.flow_tables import DeviceFlows, DownstreamFlows
Adam Dyesse5e428a2019-02-01 08:10:24 -060038from net.pio_zmq import DEFAULT_PIO_TCP_PORT
39from net.pon_zmq import DEFAULT_PON_AGENT_TCP_PORT
Chip Boling7294b252017-06-15 16:16:55 -050040
Chip Boling3e3b1a92017-05-16 11:51:18 -050041_ = third_party
42
Chip Bolingab8863d2018-03-22 14:50:31 -050043DEFAULT_MULTICAST_VLAN = 4000
Chip Bolingb4868192018-11-01 16:38:44 -050044BROADCOM_UNTAGGED_VLAN = 4091
45DEFAULT_UTILITY_VLAN = BROADCOM_UNTAGGED_VLAN
Chip Boling5561d552017-07-07 15:11:26 -050046
Chip Bolingff3087f2017-10-12 09:26:29 -050047_DEFAULT_RESTCONF_USERNAME = ""
48_DEFAULT_RESTCONF_PASSWORD = ""
Chip Boling252c7772017-08-16 10:13:17 -050049_DEFAULT_RESTCONF_PORT = 8081
50
Chip Bolingff3087f2017-10-12 09:26:29 -050051_DEFAULT_NETCONF_USERNAME = ""
52_DEFAULT_NETCONF_PASSWORD = ""
Chip Boling252c7772017-08-16 10:13:17 -050053_DEFAULT_NETCONF_PORT = 830
54
Chip Boling88354fb2018-09-21 12:17:19 -050055_STARTUP_RETRY_TIMEOUT = 5 # 5 seconds delay after activate failed before we
Chip Bolingb4868192018-11-01 16:38:44 -050056_DEFAULT_RESOURCE_MGR_KEY = "adtran"
Chip Bolingce5bfc02018-08-09 13:57:49 -050057
Chip Boling3e3b1a92017-05-16 11:51:18 -050058
Adam Dyesse5e428a2019-02-01 08:10:24 -060059#############################################################
60# Raise any Parsing Errors rather than sys.exit
61def _parser_error(message):
62 raise argparse.ArgumentTypeError(message)
63
64
Chip Boling3e3b1a92017-05-16 11:51:18 -050065class AdtranDeviceHandler(object):
66 """
67 A device that supports the ADTRAN RESTCONF protocol for communications
68 with a VOLTHA/VANILLA managed device.
Chip Boling3e3b1a92017-05-16 11:51:18 -050069 Port numbering guidelines for Adtran OLT devices. Derived classes may augment
70 the numbering scheme below as needed.
71
72 - Reserve port 0 for the CPU capture port. All ports to/from this port should
73 be related to messages destined to/from the OpenFlow controller.
74
75 - Begin numbering northbound ports (network facing) at port 1 contiguously.
76 Consider the northbound ports to typically be the highest speed uplinks.
77 If these ports are removable or provided by one or more slots in a chassis
78 subsystem, still reserve the appropriate amount of port numbers whether they
79 are populated or not.
80
81 - Number southbound ports (customer facing) ports next starting at the next
82 available port number. If chassis based, follow the same rules as northbound
83 ports and reserve enough port numbers.
84
85 - Number any out-of-band management ports (if any) last. It will be up to the
86 Device Adapter developer whether to expose these to openflow or not. If you do
87 not expose them, but do have the ports, still reserve the appropriate number of
88 port numbers just in case.
89 """
90 # HTTP shortcuts
91 HELLO_URI = '/restconf/adtran-hello:hello'
92
Chip Boling7294b252017-06-15 16:16:55 -050093 # RPC XML shortcuts
94 RESTART_RPC = '<system-restart xmlns="urn:ietf:params:xml:ns:yang:ietf-system"/>'
95
Adam Dyesse5e428a2019-02-01 08:10:24 -060096 # CONFIG PARSING
97 PARSER = argparse.ArgumentParser(description='Adtran Device Adapter')
98 PARSER.add_argument('--nc_username', '-u', action='store', default=_DEFAULT_NETCONF_USERNAME,
99 help='NETCONF username')
100 PARSER.add_argument('--nc_password', '-p', action='store', default=_DEFAULT_NETCONF_PASSWORD,
101 help='NETCONF Password')
102 PARSER.add_argument('--nc_port', '-t', action='store', default=_DEFAULT_NETCONF_PORT,
103 type=int, choices=range(1, 65536), help='NETCONF TCP Port')
104 PARSER.add_argument('--rc_username', '-U', action='store', default=_DEFAULT_RESTCONF_USERNAME,
105 help='REST username')
106 PARSER.add_argument('--rc_password', '-P', action='store', default=_DEFAULT_RESTCONF_PASSWORD,
107 help='REST Password')
108 PARSER.add_argument('--rc_port', '-T', action='store', default=_DEFAULT_RESTCONF_PORT,
109 type=int, choices=range(1, 65536), help='RESTCONF TCP Port')
110 PARSER.add_argument('--zmq_port', '-z', action='store', default=DEFAULT_PON_AGENT_TCP_PORT,
111 type=int, choices=range(1, 65536), help='PON Agent ZeroMQ Port')
112 PARSER.add_argument('--pio_port', '-Z', action='store', default=DEFAULT_PIO_TCP_PORT,
113 type=int, choices=range(1, 65536), help='PIO Service ZeroMQ Port')
114 PARSER.add_argument('--multicast_vlan', '-M', action='store',
115 metavar='int', type=int, choices=range(1, 4095),
116 default=[DEFAULT_MULTICAST_VLAN],
117 nargs='+', help='Multicast VLANs are 1..4094'),
118 PARSER.add_argument('--utility_vlan', '-B', action='store',
119 metavar='int', type=int, choices=range(1, 4095),
120 default=DEFAULT_UTILITY_VLAN,
121 help='VLAN for Controller based upstream flows from ONUs')
122 PARSER.add_argument('--resource_mgr_key', '-o', action='store',
123 default=_DEFAULT_RESOURCE_MGR_KEY,
124 help='OLT Type to look up associated resource manager configuration')
125 PARSER.error = _parser_error
Chip Boling252c7772017-08-16 10:13:17 -0500126
Adam Dyesse5e428a2019-02-01 08:10:24 -0600127 # Timeout Waiting on Rest Connectivity before initiating next HEARTBEAT
128 HEARTBEAT_TIMEOUT = 5
129
130 NC_CLIENT = AdtranNetconfClient
131
132 def __init__(self, **kwargs):
Chip Bolingfd1fd372017-12-20 13:34:12 -0600133 super(AdtranDeviceHandler, self).__init__()
134
135 adapter = kwargs['adapter']
136 device_id = kwargs['device-id']
137 timeout = kwargs.get('timeout', 20)
138
Chip Boling3e3b1a92017-05-16 11:51:18 -0500139 self.adapter = adapter
140 self.adapter_agent = adapter.adapter_agent
141 self.device_id = device_id
142 self.log = structlog.get_logger(device_id=device_id)
Chip Bolingbb15b512018-06-01 11:39:58 -0500143 self.startup = None # Startup/reboot deferred
Chip Boling3e3b1a92017-05-16 11:51:18 -0500144 self.channel = None # Proxy messaging channel with 'send' method
Chip Boling3e3b1a92017-05-16 11:51:18 -0500145 self.logical_device_id = None
Chip Boling5561d552017-07-07 15:11:26 -0500146 self.pm_metrics = None
147 self.alarms = None
Chip Boling27275992017-09-22 15:17:04 -0500148 self.multicast_vlans = [DEFAULT_MULTICAST_VLAN]
Chip Bolingab8863d2018-03-22 14:50:31 -0500149 self.utility_vlan = DEFAULT_UTILITY_VLAN
Chip Bolingbffef5e2018-08-07 14:53:12 -0500150 self.mac_address = '00:13:95:00:00:00'
Chip Bolingab8863d2018-03-22 14:50:31 -0500151 self._rest_support = None
Chip Bolingce5bfc02018-08-09 13:57:49 -0500152 self._initial_enable_complete = False
Chip Bolingb4868192018-11-01 16:38:44 -0500153 self.resource_mgr = None
Chip Boling521735d2018-12-20 09:58:01 -0600154 self.tech_profiles = None # dict(): intf_id -> ResourceMgr.TechProfile
Chip Boling3e3b1a92017-05-16 11:51:18 -0500155
156 # Northbound and Southbound ports
157 self.northbound_ports = {} # port number -> Port
158 self.southbound_ports = {} # port number -> Port (For PON, use pon-id as key)
Chip Boling7294b252017-06-15 16:16:55 -0500159 # self.management_ports = {} # port number -> Port TODO: Not currently supported
Chip Boling3e3b1a92017-05-16 11:51:18 -0500160
161 self.num_northbound_ports = None
162 self.num_southbound_ports = None
Chip Boling7294b252017-06-15 16:16:55 -0500163 # self.num_management_ports = None
164
165 self.ip_address = None
Chip Bolingb4868192018-11-01 16:38:44 -0500166 self.host_and_port = None
Chip Boling7294b252017-06-15 16:16:55 -0500167 self.timeout = timeout
168 self.restart_failure_timeout = 5 * 60 # 5 Minute timeout
Chip Boling3e3b1a92017-05-16 11:51:18 -0500169
170 # REST Client
Chip Boling252c7772017-08-16 10:13:17 -0500171 self.rest_port = _DEFAULT_RESTCONF_PORT
172 self.rest_username = _DEFAULT_RESTCONF_USERNAME
173 self.rest_password = _DEFAULT_RESTCONF_PASSWORD
Chip Boling5561d552017-07-07 15:11:26 -0500174 self._rest_client = None
Chip Boling3e3b1a92017-05-16 11:51:18 -0500175
Chip Boling7294b252017-06-15 16:16:55 -0500176 # NETCONF Client
Chip Boling252c7772017-08-16 10:13:17 -0500177 self.netconf_port = _DEFAULT_NETCONF_PORT
178 self.netconf_username = _DEFAULT_NETCONF_USERNAME
179 self.netconf_password = _DEFAULT_NETCONF_PASSWORD
Chip Boling5561d552017-07-07 15:11:26 -0500180 self._netconf_client = None
Chip Boling7294b252017-06-15 16:16:55 -0500181
Chip Bolingb4868192018-11-01 16:38:44 -0500182 # Flow entries
183 self.upstream_flows = DeviceFlows()
184 self.downstream_flows = DownstreamFlows()
185
Chip Boling61a12792017-10-02 13:23:27 -0500186 self.max_nni_ports = 1 # TODO: This is a VOLTHA imposed limit in 'flow_decomposer.py
Chip Boling27275992017-09-22 15:17:04 -0500187 # and logical_device_agent.py
Chip Bolingb4868192018-11-01 16:38:44 -0500188
189 self.resource_manager_key = _DEFAULT_RESOURCE_MGR_KEY
Chip Boling252c7772017-08-16 10:13:17 -0500190 # OMCI ZMQ Channel
Chip Boling9adbc952018-01-18 12:47:04 -0600191 self.pon_agent_port = DEFAULT_PON_AGENT_TCP_PORT
192 self.pio_port = DEFAULT_PIO_TCP_PORT
Chip Boling252c7772017-08-16 10:13:17 -0500193
Chip Boling3e3b1a92017-05-16 11:51:18 -0500194 # Heartbeat support
195 self.heartbeat_count = 0
196 self.heartbeat_miss = 0
Chip Boling61a12792017-10-02 13:23:27 -0500197 self.heartbeat_interval = 2 # TODO: Decrease before release or any scale testing
Chip Boling3e3b1a92017-05-16 11:51:18 -0500198 self.heartbeat_failed_limit = 3
199 self.heartbeat_timeout = 5
200 self.heartbeat = None
201 self.heartbeat_last_reason = ''
202
Chip Boling7294b252017-06-15 16:16:55 -0500203 # Installed flows
Chip Boling69fba862017-08-18 15:11:32 -0500204 self._evcs = {} # Flow ID/name -> FlowEntry
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500205
206 def _delete_logical_device(self):
207 ldi, self.logical_device_id = self.logical_device_id, None
208
209 if ldi is None:
210 return
211
212 self.log.debug('delete-logical-device', ldi=ldi)
213
214 logical_device = self.adapter_agent.get_logical_device(ldi)
215 self.adapter_agent.delete_logical_device(logical_device)
216
217 device = self.adapter_agent.get_device(self.device_id)
218 device.parent_id = ''
219
220 # Update the logical device mapping
221 if ldi in self.adapter.logical_device_id_to_root_device_id:
222 del self.adapter.logical_device_id_to_root_device_id[ldi]
223
Chip Boling3e3b1a92017-05-16 11:51:18 -0500224 def __del__(self):
225 # Kill any startup or heartbeat defers
Adam Dyesse5e428a2019-02-01 08:10:24 -0600226 self._cancel_tasks()
227 self._suspend_heartbeat()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500228
Chip Boling7294b252017-06-15 16:16:55 -0500229 # Remove the logical device
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500230 self._delete_logical_device()
Chip Boling7294b252017-06-15 16:16:55 -0500231
Chip Boling3e3b1a92017-05-16 11:51:18 -0500232 self.northbound_ports.clear()
233 self.southbound_ports.clear()
234
235 def __str__(self):
Chip Boling7294b252017-06-15 16:16:55 -0500236 return "AdtranDeviceHandler: {}".format(self.ip_address)
237
Chip Boling5561d552017-07-07 15:11:26 -0500238 @property
239 def netconf_client(self):
240 return self._netconf_client
241
242 @property
243 def rest_client(self):
244 return self._rest_client
245
Chip Boling69fba862017-08-18 15:11:32 -0500246 @property
247 def evcs(self):
248 return list(self._evcs.values())
249
Adam Dyesse5e428a2019-02-01 08:10:24 -0600250 @property
251 def all_ports(self):
252 for port in self.northbound_ports.itervalues():
253 yield port
254 for port in self.southbound_ports.itervalues():
255 yield port
256
Chip Boling69fba862017-08-18 15:11:32 -0500257 def add_evc(self, evc):
Chip Boling27275992017-09-22 15:17:04 -0500258 if self._evcs is not None and evc.name not in self._evcs:
Chip Boling69fba862017-08-18 15:11:32 -0500259 self._evcs[evc.name] = evc
260
261 def remove_evc(self, evc):
262 if self._evcs is not None and evc.name in self._evcs:
263 del self._evcs[evc.name]
264
Chip Boling7294b252017-06-15 16:16:55 -0500265 def parse_provisioning_options(self, device):
Chip Boling308c3312018-08-09 13:08:24 -0500266 if device.ipv4_address:
267 self.ip_address = device.ipv4_address
Chip Bolingb4868192018-11-01 16:38:44 -0500268 self.host_and_port = '{}:{}'.format(self.ip_address,
269 self.netconf_port)
Chip Boling308c3312018-08-09 13:08:24 -0500270 elif device.host_and_port:
Chip Bolingb4868192018-11-01 16:38:44 -0500271 self.host_and_port = device.host_and_port.split(":")
272 self.ip_address = self.host_and_port[0]
273 self.netconf_port = int(self.host_and_port[1])
Chip Boling308c3312018-08-09 13:08:24 -0500274 self.adapter_agent.update_device(device)
275
276 else:
277 self.activate_failed(device, 'No IP_address field provided')
Chip Boling7294b252017-06-15 16:16:55 -0500278
Chip Boling7294b252017-06-15 16:16:55 -0500279 try:
Adam Dyesse5e428a2019-02-01 08:10:24 -0600280 args = self.PARSER.parse_args(shlex.split(device.extra_args))
Chip Boling7294b252017-06-15 16:16:55 -0500281
Chip Bolingbb15b512018-06-01 11:39:58 -0500282 # May have multiple multicast VLANs
Adam Dyesse5e428a2019-02-01 08:10:24 -0600283 self.multicast_vlans = args.multicast_vlan
284 self.utility_vlan = args.utility_vlan
Chip Boling27275992017-09-22 15:17:04 -0500285
Chip Boling7294b252017-06-15 16:16:55 -0500286 self.netconf_username = args.nc_username
287 self.netconf_password = args.nc_password
288 self.netconf_port = args.nc_port
289
290 self.rest_username = args.rc_username
291 self.rest_password = args.rc_password
292 self.rest_port = args.rc_port
293
Chip Boling9adbc952018-01-18 12:47:04 -0600294 self.pon_agent_port = args.zmq_port
Chip Bolingab8863d2018-03-22 14:50:31 -0500295 self.pio_port = args.pio_port
Chip Bolingb4868192018-11-01 16:38:44 -0500296 self.resource_manager_key = args.resource_mgr_key
Chip Boling252c7772017-08-16 10:13:17 -0500297
Chip Bolingfd1fd372017-12-20 13:34:12 -0600298 if not self.rest_username:
299 self.rest_username = 'NDE0NDRkNDk0ZQ==\n'.\
300 decode('base64').decode('hex')
301 if not self.rest_password:
302 self.rest_password = 'NTA0MTUzNTM1NzRmNTI0NA==\n'.\
303 decode('base64').decode('hex')
304 if not self.netconf_username:
305 self.netconf_username = 'Njg3Mzc2NzI2ZjZmNzQ=\n'.\
306 decode('base64').decode('hex')
307 if not self.netconf_password:
308 self.netconf_password = 'NDI0ZjUzNDM0Zg==\n'.\
309 decode('base64').decode('hex')
310
Adam Dyesse5e428a2019-02-01 08:10:24 -0600311 except argparse.ArgumentTypeError as e:
Chip Boling7294b252017-06-15 16:16:55 -0500312 self.activate_failed(device,
313 'Invalid arguments: {}'.format(e.message),
314 reachable=False)
315 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -0500316 self.log.exception('option_parsing_error: {}'.format(e.message))
317
Chip Boling3e3b1a92017-05-16 11:51:18 -0500318 @inlineCallbacks
Chip Bolingce5bfc02018-08-09 13:57:49 -0500319 def activate(self, done_deferred, reconciling):
Chip Boling3e3b1a92017-05-16 11:51:18 -0500320 """
321 Activate the OLT device
322
Chip Bolingfd1fd372017-12-20 13:34:12 -0600323 :param done_deferred: (Deferred) Deferred to fire when done
Chip Boling7294b252017-06-15 16:16:55 -0500324 :param reconciling: If True, this adapter is taking over for a previous adapter
325 for an existing OLT
Chip Boling3e3b1a92017-05-16 11:51:18 -0500326 """
Chip Boling5561d552017-07-07 15:11:26 -0500327 self.log.info('AdtranDeviceHandler.activating', reconciling=reconciling)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500328
329 if self.logical_device_id is None:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500330 device = self.adapter_agent.get_device(self.device_id)
331
Chip Boling3e3b1a92017-05-16 11:51:18 -0500332 try:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600333 # Parse our command line options for this device
334 self.parse_provisioning_options(device)
Chip Boling3e3b1a92017-05-16 11:51:18 -0500335
Chip Bolingfd1fd372017-12-20 13:34:12 -0600336 ############################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600337 # Start initial discovery of NETCONF support (if any)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600338 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500339 device.reason = 'establishing NETCONF connection'
340 self.adapter_agent.update_device(device)
341
Chip Bolingfd1fd372017-12-20 13:34:12 -0600342 self.startup = self.make_netconf_connection()
343 yield self.startup
Chip Boling3e3b1a92017-05-16 11:51:18 -0500344
Chip Bolingfd1fd372017-12-20 13:34:12 -0600345 except Exception as e:
Chip Boling8956b642018-04-11 13:47:01 -0500346 self.log.exception('netconf-connection', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500347 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Boling8956b642018-04-11 13:47:01 -0500348
349 ############################################################################
350 # Update access information on network device for full protocol support
351 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500352 device.reason = 'device networking validation'
353 self.adapter_agent.update_device(device)
Chip Boling8956b642018-04-11 13:47:01 -0500354 self.startup = self.ready_network_access()
355 yield self.startup
356
357 except Exception as e:
358 self.log.exception('network-setup', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500359 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Boling8956b642018-04-11 13:47:01 -0500360
361 ############################################################################
362 # Restconf setup
363 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500364 device.reason = 'establishing RESTConf connections'
365 self.adapter_agent.update_device(device)
Chip Boling8956b642018-04-11 13:47:01 -0500366 self.startup = self.make_restconf_connection()
367 yield self.startup
368
369 except Exception as e:
370 self.log.exception('restconf-setup', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500371 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500372
Chip Bolingfd1fd372017-12-20 13:34:12 -0600373 ############################################################################
374 # Get the device Information
Chip Bolingfd1fd372017-12-20 13:34:12 -0600375 if reconciling:
376 device.connect_status = ConnectStatus.REACHABLE
377 self.adapter_agent.update_device(device)
378 else:
379 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500380 device.reason = 'retrieving device information'
381 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600382 self.startup = self.get_device_info(device)
383 results = yield self.startup
384
385 device.model = results.get('model', 'unknown')
386 device.hardware_version = results.get('hardware_version', 'unknown')
387 device.firmware_version = results.get('firmware_version', 'unknown')
388 device.serial_number = results.get('serial_number', 'unknown')
Chip Bolingab8863d2018-03-22 14:50:31 -0500389 device.images.image.extend(results.get('software-images', []))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600390
Chip Bolingfd1fd372017-12-20 13:34:12 -0600391 device.root = True
Chip Boling4912e782018-11-09 16:33:40 -0600392 device.vendor = results.get('vendor', 'Adtran Inc.')
Chip Bolingfd1fd372017-12-20 13:34:12 -0600393 device.connect_status = ConnectStatus.REACHABLE
394 self.adapter_agent.update_device(device)
395
396 except Exception as e:
Chip Boling8956b642018-04-11 13:47:01 -0500397 self.log.exception('device-info', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500398 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600399
400 try:
401 # Enumerate and create Northbound NNI interfaces
Chip Bolingce5bfc02018-08-09 13:57:49 -0500402 device.reason = 'enumerating northbound interfaces'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600403 self.adapter_agent.update_device(device)
404 self.startup = self.enumerate_northbound_ports(device)
405 results = yield self.startup
406
407 self.startup = self.process_northbound_ports(device, results)
408 yield self.startup
409
Chip Bolingce5bfc02018-08-09 13:57:49 -0500410 device.reason = 'adding northbound interfaces to adapter'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600411 self.adapter_agent.update_device(device)
412
413 if not reconciling:
414 for port in self.northbound_ports.itervalues():
415 self.adapter_agent.add_port(device.id, port.get_port())
416
417 except Exception as e:
Chip Boling8956b642018-04-11 13:47:01 -0500418 self.log.exception('NNI-enumeration', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500419 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600420
421 try:
422 # Enumerate and create southbound interfaces
Chip Bolingce5bfc02018-08-09 13:57:49 -0500423 device.reason = 'enumerating southbound interfaces'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600424 self.adapter_agent.update_device(device)
425 self.startup = self.enumerate_southbound_ports(device)
426 results = yield self.startup
427
428 self.startup = self.process_southbound_ports(device, results)
429 yield self.startup
430
Chip Bolingce5bfc02018-08-09 13:57:49 -0500431 device.reason = 'adding southbound interfaces to adapter'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600432 self.adapter_agent.update_device(device)
433
434 if not reconciling:
435 for port in self.southbound_ports.itervalues():
436 self.adapter_agent.add_port(device.id, port.get_port())
437
438 except Exception as e:
439 self.log.exception('PON_enumeration', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500440 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600441
Chip Bolingb4868192018-11-01 16:38:44 -0500442 # Initialize resource manager
443 self.initialize_resource_manager()
444
Chip Bolingfd1fd372017-12-20 13:34:12 -0600445 if reconciling:
446 if device.admin_state == AdminState.ENABLED:
447 if device.parent_id:
448 self.logical_device_id = device.parent_id
449 self.adapter_agent.reconcile_logical_device(device.parent_id)
450 else:
451 self.log.info('no-logical-device-set')
452
453 # Reconcile child devices
454 self.adapter_agent.reconcile_child_devices(device.id)
455 ld_initialized = self.adapter_agent.get_logical_device()
456 assert device.parent_id == ld_initialized.id, \
457 'parent ID not Logical device ID'
458
459 else:
460 # Complete activation by setting up logical device for this OLT and saving
461 # off the devices parent_id
Chip Bolingfd1fd372017-12-20 13:34:12 -0600462 ld_initialized = self.create_logical_device(device)
463
464 ############################################################################
465 # Setup PM configuration for this device
Chip Bolingce5bfc02018-08-09 13:57:49 -0500466 if self.pm_metrics is None:
467 try:
468 device.reason = 'setting up Performance Monitoring configuration'
469 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600470
Chip Bolingce5bfc02018-08-09 13:57:49 -0500471 kwargs = {
472 'nni-ports': self.northbound_ports.values(),
473 'pon-ports': self.southbound_ports.values()
474 }
475 self.pm_metrics = OltPmMetrics(self.adapter_agent, self.device_id,
Chip Bolingc0451382018-08-24 14:21:53 -0500476 ld_initialized.id, grouped=True,
477 freq_override=False, **kwargs)
Chip Bolingbffef5e2018-08-07 14:53:12 -0500478
Chip Bolingce5bfc02018-08-09 13:57:49 -0500479 pm_config = self.pm_metrics.make_proto()
480 self.log.debug("initial-pm-config", pm_config=pm_config)
481 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600482
Chip Bolingce5bfc02018-08-09 13:57:49 -0500483 except Exception as e:
484 self.log.exception('pm-setup', e=e)
485 self.activate_failed(device, e.message, reachable=False)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600486
487 ############################################################################
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500488 # Set the ports in a known good initial state
489 if not reconciling:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500490 device.reason = 'setting device to a known initial state'
491 self.adapter_agent.update_device(device)
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500492 try:
Adam Dyesse5e428a2019-02-01 08:10:24 -0600493 for port in self.all_ports:
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500494 self.startup = yield port.reset()
495
496 except Exception as e:
497 self.log.exception('port-reset', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500498 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500499
500 ############################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600501 # Create logical ports for all southbound and northbound interfaces
502 try:
Chip Bolingce5bfc02018-08-09 13:57:49 -0500503 device.reason = 'creating logical ports'
Chip Bolingfd1fd372017-12-20 13:34:12 -0600504 self.adapter_agent.update_device(device)
505 self.startup = self.create_logical_ports(device, ld_initialized, reconciling)
506 yield self.startup
507
508 except Exception as e:
509 self.log.exception('logical-port', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500510 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600511
512 ############################################################################
Chip Bolingbffef5e2018-08-07 14:53:12 -0500513 # Setup Alarm handler
Chip Bolingce5bfc02018-08-09 13:57:49 -0500514 device.reason = 'setting up adapter alarms'
Chip Bolingbffef5e2018-08-07 14:53:12 -0500515 self.adapter_agent.update_device(device)
516
517 self.alarms = AdapterAlarms(self.adapter_agent, device.id, ld_initialized.id)
518
519 ############################################################################
Chip Bolingfd1fd372017-12-20 13:34:12 -0600520 # Register for ONU detection
521 # self.adapter_agent.register_for_onu_detect_state(device.id)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600522 # Complete device specific steps
523 try:
524 self.log.debug('device-activation-procedures')
Chip Bolingce5bfc02018-08-09 13:57:49 -0500525 device.reason = 'performing model specific activation procedures'
526 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600527 self.startup = self.complete_device_specific_activation(device, reconciling)
528 yield self.startup
529
530 except Exception as e:
531 self.log.exception('device-activation-procedures', e=e)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500532 returnValue(self.restart_activate(done_deferred, reconciling))
Chip Bolingfd1fd372017-12-20 13:34:12 -0600533
534 # Schedule the heartbeat for the device
Chip Bolingfd1fd372017-12-20 13:34:12 -0600535 self.log.debug('starting-heartbeat')
536 self.start_heartbeat(delay=10)
537
538 device = self.adapter_agent.get_device(device.id)
539 device.parent_id = ld_initialized.id
540 device.oper_status = OperStatus.ACTIVE
541 device.reason = ''
542 self.adapter_agent.update_device(device)
543 self.logical_device_id = ld_initialized.id
544
Chip Bolingfd1fd372017-12-20 13:34:12 -0600545 # Start collecting stats from the device after a brief pause
Chip Bolingbffef5e2018-08-07 14:53:12 -0500546 reactor.callLater(10, self.pm_metrics.start_collector)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600547
548 # Signal completion
Chip Bolingce5bfc02018-08-09 13:57:49 -0500549 self._initial_enable_complete = True
Chip Bolingfd1fd372017-12-20 13:34:12 -0600550 self.log.info('activated')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500551
552 except Exception as e:
Chip Bolingfd1fd372017-12-20 13:34:12 -0600553 self.log.exception('activate', e=e)
554 if done_deferred is not None:
555 done_deferred.errback(e)
Chip Boling8956b642018-04-11 13:47:01 -0500556
Chip Bolingfd1fd372017-12-20 13:34:12 -0600557 if done_deferred is not None:
558 done_deferred.callback('activated')
Chip Boling3e3b1a92017-05-16 11:51:18 -0500559
Chip Bolingce5bfc02018-08-09 13:57:49 -0500560 returnValue('activated')
561
562 def restart_activate(self, done_deferred, reconciling):
563 """
564 Startup activation failed, pause a short period of time and retry
565
566 :param done_deferred: (deferred) Deferred to fire upon completion of activation
567 :param reconciling: (bool) If true, we are reconciling after moving to a new vCore
568 """
Adam Dyesse5e428a2019-02-01 08:10:24 -0600569 self._cancel_tasks()
Chip Bolingce5bfc02018-08-09 13:57:49 -0500570 device = self.adapter_agent.get_device(self.device_id)
571 device.reason = 'Failed during {}, retrying'.format(device.reason)
572 self.adapter_agent.update_device(device)
573 self.startup = reactor.callLater(_STARTUP_RETRY_TIMEOUT, self.activate,
574 done_deferred, reconciling)
575 return 'retrying'
Chip Boling5561d552017-07-07 15:11:26 -0500576
Chip Boling8956b642018-04-11 13:47:01 -0500577 @inlineCallbacks
578 def ready_network_access(self):
579 # Override in device specific class if needed
Adam Dyesse5e428a2019-02-01 08:10:24 -0600580 yield defer.Deferred()
Chip Boling8956b642018-04-11 13:47:01 -0500581
Chip Boling3e3b1a92017-05-16 11:51:18 -0500582 def activate_failed(self, device, reason, reachable=True):
583 """
584 Activation process (adopt_device) has failed.
585
586 :param device: A voltha.Device object, with possible device-type
587 specific extensions. Such extensions shall be described as part of
588 the device type specification returned by device_types().
589 :param reason: (string) failure reason
590 :param reachable: (boolean) Flag indicating if device may be reachable
591 via RESTConf or NETConf even after this failure.
592 """
593 device.oper_status = OperStatus.FAILED
594 if not reachable:
595 device.connect_status = ConnectStatus.UNREACHABLE
596
597 device.reason = reason
598 self.adapter_agent.update_device(device)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600599 raise Exception('Failed to activate OLT: {}'.format(device.reason))
Chip Boling3e3b1a92017-05-16 11:51:18 -0500600
Chip Boling5561d552017-07-07 15:11:26 -0500601 @inlineCallbacks
Adam Dyesse5e428a2019-02-01 08:10:24 -0600602 def _close_netconf_connection(self):
603 resp = None
604 if self.netconf_client:
605 try:
606 resp = yield self.netconf_client.close()
607 except Exception as e:
608 self.log.exception('NETCONF-shutdown', e, device_id=self.device_id)
609 finally:
610 self._netconf_client = None
611 returnValue(resp)
612
613 @inlineCallbacks
Chip Bolingff3087f2017-10-12 09:26:29 -0500614 def make_netconf_connection(self, connect_timeout=None,
615 close_existing_client=False):
616
Adam Dyesse5e428a2019-02-01 08:10:24 -0600617 if close_existing_client:
618 yield self._close_netconf_connection()
Chip Boling7294b252017-06-15 16:16:55 -0500619
Adam Dyesse5e428a2019-02-01 08:10:24 -0600620 client = self.netconf_client
Chip Boling5561d552017-07-07 15:11:26 -0500621
622 if client is None:
Adam Dyesse5e428a2019-02-01 08:10:24 -0600623 client = self.NC_CLIENT(self.ip_address,
624 self.netconf_port,
625 self.netconf_username,
626 self.netconf_password,
627 self.timeout)
Chip Boling5561d552017-07-07 15:11:26 -0500628 if client.connected:
629 self._netconf_client = client
630 returnValue(True)
Chip Boling7294b252017-06-15 16:16:55 -0500631
632 timeout = connect_timeout or self.timeout
Chip Boling7294b252017-06-15 16:16:55 -0500633
Chip Boling5561d552017-07-07 15:11:26 -0500634 try:
Adam Dyesse5e428a2019-02-01 08:10:24 -0600635 results = yield client.connect(timeout)
Chip Boling5561d552017-07-07 15:11:26 -0500636 self._netconf_client = client
637 returnValue(results)
638
639 except Exception as e:
640 self.log.exception('Failed to create NETCONF Client', e=e)
641 self._netconf_client = None
642 raise
643
644 @inlineCallbacks
Chip Boling7294b252017-06-15 16:16:55 -0500645 def make_restconf_connection(self, get_timeout=None):
Chip Boling5561d552017-07-07 15:11:26 -0500646 client = self._rest_client
647
648 if client is None:
649 client = AdtranRestClient(self.ip_address,
650 self.rest_port,
651 self.rest_username,
652 self.rest_password,
653 self.timeout)
Chip Boling7294b252017-06-15 16:16:55 -0500654
655 timeout = get_timeout or self.timeout
Chip Boling5561d552017-07-07 15:11:26 -0500656
657 try:
658 request = client.request('GET', self.HELLO_URI, name='hello', timeout=timeout)
659 results = yield request
660 if isinstance(results, dict) and 'module-info' in results:
661 self._rest_client = client
662 returnValue(results)
663 else:
664 from twisted.internet.error import ConnectError
665 self._rest_client = None
666 raise ConnectError(string='Results received but unexpected data type or contents')
667 except Exception:
668 self._rest_client = None
669 raise
Chip Boling7294b252017-06-15 16:16:55 -0500670
671 def create_logical_device(self, device):
Chip Boling5561d552017-07-07 15:11:26 -0500672 version = device.images.image[0].version
673
Chip Boling7294b252017-06-15 16:16:55 -0500674 ld = LogicalDevice(
675 # NOTE: not setting id and datapath_id will let the adapter agent pick id
Chip Bolingb1ddfc42018-10-16 14:17:32 -0500676 desc=ofp_desc(mfr_desc='VOLTHA Project',
Chip Boling7294b252017-06-15 16:16:55 -0500677 hw_desc=device.hardware_version,
Chip Boling5561d552017-07-07 15:11:26 -0500678 sw_desc=version,
Chip Boling7294b252017-06-15 16:16:55 -0500679 serial_num=device.serial_number,
680 dp_desc='n/a'),
Chip Boling27275992017-09-22 15:17:04 -0500681 switch_features=ofp_switch_features(n_buffers=256,
682 n_tables=2,
Chip Boling7294b252017-06-15 16:16:55 -0500683 capabilities=(
Chip Boling5561d552017-07-07 15:11:26 -0500684 OFPC_FLOW_STATS |
685 OFPC_TABLE_STATS |
686 OFPC_GROUP_STATS |
Chip Boling7294b252017-06-15 16:16:55 -0500687 OFPC_PORT_STATS)),
688 root_device_id=device.id)
689
Chip Bolingfd1fd372017-12-20 13:34:12 -0600690 ld_initialized = self.adapter_agent.create_logical_device(ld,
Chip Bolingbffef5e2018-08-07 14:53:12 -0500691 dpid=self.mac_address)
Chip Boling7294b252017-06-15 16:16:55 -0500692 return ld_initialized
693
694 @inlineCallbacks
695 def create_logical_ports(self, device, ld_initialized, reconciling):
Chip Boling7294b252017-06-15 16:16:55 -0500696 if not reconciling:
Chip Boling27275992017-09-22 15:17:04 -0500697 # Add the ports to the logical device
698
Adam Dyesse5e428a2019-02-01 08:10:24 -0600699 for port in self.all_ports:
Chip Boling7294b252017-06-15 16:16:55 -0500700 lp = port.get_logical_port()
701 if lp is not None:
702 self.adapter_agent.add_logical_port(ld_initialized.id, lp)
703
Chip Bolingab8863d2018-03-22 14:50:31 -0500704 # Clean up all EVCs, EVC maps and ACLs (exceptions are ok)
Chip Boling252c7772017-08-16 10:13:17 -0500705 try:
706 from flow.evc import EVC
707 self.startup = yield EVC.remove_all(self.netconf_client)
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500708 from flow.utility_evc import UtilityEVC
709 self.startup = yield UtilityEVC.remove_all(self.netconf_client)
Chip Boling252c7772017-08-16 10:13:17 -0500710
711 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500712 self.log.exception('evc-cleanup', e=e)
Chip Boling252c7772017-08-16 10:13:17 -0500713
714 try:
715 from flow.evc_map import EVCMap
716 self.startup = yield EVCMap.remove_all(self.netconf_client)
717
718 except Exception as e:
Chip Boling27275992017-09-22 15:17:04 -0500719 self.log.exception('evc-map-cleanup', e=e)
Chip Boling252c7772017-08-16 10:13:17 -0500720
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500721 from flow.acl import ACL
722 ACL.clear_all(device.id)
Chip Bolingab8863d2018-03-22 14:50:31 -0500723 try:
Chip Bolingab8863d2018-03-22 14:50:31 -0500724 self.startup = yield ACL.remove_all(self.netconf_client)
725
726 except Exception as e:
727 self.log.exception('acl-cleanup', e=e)
728
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500729 from flow.flow_entry import FlowEntry
Chip Bolingb4868192018-11-01 16:38:44 -0500730 FlowEntry.clear_all(self)
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500731
Chip Bolingbffef5e2018-08-07 14:53:12 -0500732 from download import Download
733 Download.clear_all(self.netconf_client)
734
Chip Boling27275992017-09-22 15:17:04 -0500735 # Start/stop the interfaces as needed. These are deferred calls
Chip Boling7294b252017-06-15 16:16:55 -0500736
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500737 dl = []
738 for port in self.northbound_ports.itervalues():
739 try:
Chip Boling27275992017-09-22 15:17:04 -0500740 dl.append(port.start())
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500741 except Exception as e:
742 self.log.exception('northbound-port-startup', e=e)
Chip Boling7294b252017-06-15 16:16:55 -0500743
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500744 for port in self.southbound_ports.itervalues():
745 try:
Chip Boling27275992017-09-22 15:17:04 -0500746 dl.append(port.start() if port.admin_state == AdminState.ENABLED else port.stop())
Chip Bolingef0e2fa2017-10-06 14:33:01 -0500747 except Exception as e:
748 self.log.exception('southbound-port-startup', e=e)
Chip Boling27275992017-09-22 15:17:04 -0500749
Chip Bolingff3087f2017-10-12 09:26:29 -0500750 results = yield defer.gatherResults(dl, consumeErrors=True)
Chip Boling7294b252017-06-15 16:16:55 -0500751
Chip Boling5561d552017-07-07 15:11:26 -0500752 returnValue(results)
Chip Boling7294b252017-06-15 16:16:55 -0500753
754 @inlineCallbacks
755 def device_information(self, device):
756 """
757 Examine the various managment models and extract device information for
758 VOLTHA use
759
760 :param device: A voltha.Device object, with possible device-type
761 specific extensions.
762 :return: (Deferred or None).
763 """
764 yield defer.Deferred(lambda c: c.callback("Not Required"))
765
Chip Boling3e3b1a92017-05-16 11:51:18 -0500766 @inlineCallbacks
767 def enumerate_northbound_ports(self, device):
768 """
769 Enumerate all northbound ports of a device. You should override
Chip Boling3e3b1a92017-05-16 11:51:18 -0500770 a non-recoverable error, throw an appropriate exception.
771
772 :param device: A voltha.Device object, with possible device-type
773 specific extensions.
774 :return: (Deferred or None).
775 """
776 yield defer.Deferred(lambda c: c.callback("Not Required"))
777
778 @inlineCallbacks
779 def process_northbound_ports(self, device, results):
780 """
781 Process the results from the 'enumerate_northbound_ports' method.
782 You should override this method in your derived class as necessary and
783 create an NNI Port object (of your own choosing) that supports a 'get_port'
784 method. Once created, insert it into this base class's northbound_ports
785 collection.
786
787 Should you encounter a non-recoverable error, throw an appropriate exception.
788
789 :param device: A voltha.Device object, with possible device-type
790 specific extensions.
791 :param results: Results from the 'enumerate_northbound_ports' method that
792 you implemented. The type and contents are up to you to
793 :return:
794 """
795 yield defer.Deferred(lambda c: c.callback("Not Required"))
796
797 @inlineCallbacks
798 def enumerate_southbound_ports(self, device):
799 """
800 Enumerate all southbound ports of a device. You should override
801 this method in your derived class as necessary. Should you encounter
802 a non-recoverable error, throw an appropriate exception.
803
804 :param device: A voltha.Device object, with possible device-type
805 specific extensions.
806 :return: (Deferred or None).
807 """
808 yield defer.Deferred(lambda c: c.callback("Not Required"))
809
810 @inlineCallbacks
811 def process_southbound_ports(self, device, results):
812 """
813 Process the results from the 'enumerate_southbound_ports' method.
814 You should override this method in your derived class as necessary and
815 create an Port object (of your own choosing) that supports a 'get_port'
816 method. Once created, insert it into this base class's southbound_ports
817 collection.
818
819 Should you encounter a non-recoverable error, throw an appropriate exception.
820
821 :param device: A voltha.Device object, with possible device-type
822 specific extensions.
823 :param results: Results from the 'enumerate_southbound_ports' method that
824 you implemented. The type and contents are up to you to
825 :return:
826 """
827 yield defer.Deferred(lambda c: c.callback("Not Required"))
828
Chip Boling5561d552017-07-07 15:11:26 -0500829 # TODO: Move some of the items below from here and the EVC to a utility class
830
831 def is_nni_port(self, port):
832 return port in self.northbound_ports
833
834 def is_uni_port(self, port):
835 raise NotImplementedError('implement in derived class')
836
837 def is_pon_port(self, port):
838 raise NotImplementedError('implement in derived class')
839
840 def is_logical_port(self, port):
841 return not self.is_nni_port(port) and not self.is_uni_port(port) and not self.is_pon_port(port)
842
843 def get_port_name(self, port):
844 raise NotImplementedError('implement in derived class')
845
Chip Bolingb4868192018-11-01 16:38:44 -0500846 def initialize_resource_manager(self):
847 raise NotImplementedError('implement in derived class')
848
Chip Boling3e3b1a92017-05-16 11:51:18 -0500849 @inlineCallbacks
Chip Boling7294b252017-06-15 16:16:55 -0500850 def complete_device_specific_activation(self, _device, _reconciling):
Chip Bolingab8863d2018-03-22 14:50:31 -0500851 # NOTE: Override this in your derived class for any device startup completion
Adam Dyesse5e428a2019-02-01 08:10:24 -0600852 yield defer.Deferred()
Chip Boling3e3b1a92017-05-16 11:51:18 -0500853
Chip Boling9fef0fd2018-12-11 09:43:59 -0600854 @inlineCallbacks
Chip Boling7294b252017-06-15 16:16:55 -0500855 def disable(self):
856 """
857 This is called when a previously enabled device needs to be disabled based on a NBI call.
858 """
859 self.log.info('disabling', device_id=self.device_id)
Adam Dyesse5e428a2019-02-01 08:10:24 -0600860 self._cancel_tasks()
Chip Bolingce5bfc02018-08-09 13:57:49 -0500861
Chip Boling7294b252017-06-15 16:16:55 -0500862 # Get the latest device reference
863 device = self.adapter_agent.get_device(self.device_id)
Chip Bolingce5bfc02018-08-09 13:57:49 -0500864 device.reason = 'Disabling'
865 self.adapter_agent.update_device(device)
Chip Boling7294b252017-06-15 16:16:55 -0500866
Chip Boling27275992017-09-22 15:17:04 -0500867 # Drop registration for ONU detection
868 # self.adapter_agent.unregister_for_onu_detect_state(self.device.id)
Adam Dyesse5e428a2019-02-01 08:10:24 -0600869 self._suspend_heartbeat()
Adam Dyess9e39f7e2019-02-07 08:56:48 -0600870
Chip Boling7294b252017-06-15 16:16:55 -0500871 # Update the operational status to UNKNOWN
Chip Boling7294b252017-06-15 16:16:55 -0500872 device.oper_status = OperStatus.UNKNOWN
873 device.connect_status = ConnectStatus.UNREACHABLE
874 self.adapter_agent.update_device(device)
875
Chip Boling7294b252017-06-15 16:16:55 -0500876 # Disable all child devices first
877 self.adapter_agent.update_child_devices_state(self.device_id,
878 admin_state=AdminState.DISABLED)
879
880 # Remove the peer references from this device
881 self.adapter_agent.delete_all_peer_references(self.device_id)
882
Chip Boling5d5a6d82019-01-03 13:34:46 -0600883 # Remove the logical device to clear out logical device ports for any
884 # previously activated ONUs
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500885 self._delete_logical_device()
886
Chip Boling7294b252017-06-15 16:16:55 -0500887 # Set all ports to disabled
888 self.adapter_agent.disable_all_ports(self.device_id)
889
Chip Boling5561d552017-07-07 15:11:26 -0500890 dl = []
Adam Dyesse5e428a2019-02-01 08:10:24 -0600891 for port in self.all_ports:
Chip Boling5561d552017-07-07 15:11:26 -0500892 dl.append(port.stop())
Chip Boling7294b252017-06-15 16:16:55 -0500893
Chip Boling69fba862017-08-18 15:11:32 -0500894 # NOTE: Flows removed before this method is called
895 # Wait for completion
896
Chip Bolingfd1fd372017-12-20 13:34:12 -0600897 self.startup = defer.gatherResults(dl, consumeErrors=True)
Chip Boling9fef0fd2018-12-11 09:43:59 -0600898 yield self.startup
Chip Boling7294b252017-06-15 16:16:55 -0500899
Chip Boling9fef0fd2018-12-11 09:43:59 -0600900 if self.netconf_client:
901 self.netconf_client.close()
Chip Boling7294b252017-06-15 16:16:55 -0500902
Chip Boling9fef0fd2018-12-11 09:43:59 -0600903 self._netconf_client = None
904 self._rest_client = None
Chip Boling69fba862017-08-18 15:11:32 -0500905
Chip Bolingce5bfc02018-08-09 13:57:49 -0500906 device.reason = ''
907 self.adapter_agent.update_device(device)
Chip Boling7294b252017-06-15 16:16:55 -0500908 self.log.info('disabled', device_id=device.id)
Chip Boling9fef0fd2018-12-11 09:43:59 -0600909 returnValue(None)
Chip Boling7294b252017-06-15 16:16:55 -0500910
911 @inlineCallbacks
Chip Bolingfd1fd372017-12-20 13:34:12 -0600912 def reenable(self, done_deferred=None):
Chip Boling7294b252017-06-15 16:16:55 -0500913 """
914 This is called when a previously disabled device needs to be enabled based on a NBI call.
Chip Bolingfd1fd372017-12-20 13:34:12 -0600915 :param done_deferred: (Deferred) Deferred to fire when done
Chip Boling7294b252017-06-15 16:16:55 -0500916 """
917 self.log.info('re-enabling', device_id=self.device_id)
Adam Dyesse5e428a2019-02-01 08:10:24 -0600918 self._cancel_tasks()
Chip Bolingce5bfc02018-08-09 13:57:49 -0500919
920 if not self._initial_enable_complete:
921 # Never contacted the device on the initial startup, do 'activate' steps instead
922 self.startup = reactor.callLater(0, self.activate, done_deferred, False)
923 returnValue('activating')
924
Chip Boling7294b252017-06-15 16:16:55 -0500925 # Get the latest device reference
926 device = self.adapter_agent.get_device(self.device_id)
927
928 # Update the connect status to REACHABLE
929 device.connect_status = ConnectStatus.REACHABLE
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500930 device.oper_status = OperStatus.ACTIVATING
Chip Boling7294b252017-06-15 16:16:55 -0500931 self.adapter_agent.update_device(device)
932
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500933 # Reenable any previously configured southbound ports
934 for port in self.southbound_ports.itervalues():
Chip Boling9fef0fd2018-12-11 09:43:59 -0600935 self.log.debug('reenable-pon-port', pon_id=port.pon_id)
936 port.enabled = True
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500937
938 # Flows should not exist on re-enable. They are re-pushed
939 if len(self._evcs):
940 self.log.warn('evcs-found', evcs=self._evcs)
941 self._evcs.clear()
Chip Bolingfd1fd372017-12-20 13:34:12 -0600942
Chip Boling7294b252017-06-15 16:16:55 -0500943 try:
944 yield self.make_restconf_connection()
945
946 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -0500947 self.log.exception('adtran-hello-reconnect', e=e)
Chip Boling7294b252017-06-15 16:16:55 -0500948
Chip Boling5561d552017-07-07 15:11:26 -0500949 try:
950 yield self.make_netconf_connection()
Chip Boling7294b252017-06-15 16:16:55 -0500951
Chip Boling5561d552017-07-07 15:11:26 -0500952 except Exception as e:
Chip Boling252c7772017-08-16 10:13:17 -0500953 self.log.exception('NETCONF-re-connection', e=e)
Chip Boling7294b252017-06-15 16:16:55 -0500954
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500955 # Recreate the logical device
956 # NOTE: This causes a flow update event
957 ld_initialized = self.create_logical_device(device)
Chip Boling7294b252017-06-15 16:16:55 -0500958
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500959 # Create logical ports for all southbound and northbound interfaces
Chip Boling27275992017-09-22 15:17:04 -0500960 try:
961 self.startup = self.create_logical_ports(device, ld_initialized, False)
962 yield self.startup
963
964 except Exception as e:
965 self.log.exception('logical-port-creation', e=e)
Chip Boling7294b252017-06-15 16:16:55 -0500966
967 device = self.adapter_agent.get_device(device.id)
968 device.parent_id = ld_initialized.id
969 device.oper_status = OperStatus.ACTIVE
Chip Boling5561d552017-07-07 15:11:26 -0500970 device.reason = ''
Chip Boling7294b252017-06-15 16:16:55 -0500971 self.logical_device_id = ld_initialized.id
972
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500973 # update device active status now
974 self.adapter_agent.update_device(device)
975
Chip Boling7294b252017-06-15 16:16:55 -0500976 # Reenable all child devices
977 self.adapter_agent.update_child_devices_state(device.id,
978 admin_state=AdminState.ENABLED)
Chip Boling5d5a6d82019-01-03 13:34:46 -0600979 # Schedule the heartbeat for the device
980 self.log.debug('starting-heartbeat')
981 self.start_heartbeat(delay=5)
Chip Boling5561d552017-07-07 15:11:26 -0500982
Chip Boling7294b252017-06-15 16:16:55 -0500983 self.log.info('re-enabled', device_id=device.id)
Chip Bolingfd1fd372017-12-20 13:34:12 -0600984
985 if done_deferred is not None:
986 done_deferred.callback('Done')
987
Chip Bolingc64c8dc2018-04-20 14:42:24 -0500988 returnValue('reenabled')
Chip Boling7294b252017-06-15 16:16:55 -0500989
990 @inlineCallbacks
991 def reboot(self):
992 """
993 This is called to reboot a device based on a NBI call. The admin state of the device
994 will not change after the reboot.
995 """
Adam Dyesse5e428a2019-02-01 08:10:24 -0600996 self.log.debug('reboot', device_id=self.device_id)
Chip Boling7294b252017-06-15 16:16:55 -0500997
Chip Bolingce5bfc02018-08-09 13:57:49 -0500998 if not self._initial_enable_complete:
999 # Never contacted the device on the initial startup, do 'activate' steps instead
1000 returnValue('failed')
1001
Adam Dyesse5e428a2019-02-01 08:10:24 -06001002 self._cancel_tasks()
Chip Boling27275992017-09-22 15:17:04 -05001003 # Issue reboot command
1004
Adam Dyesse5e428a2019-02-01 08:10:24 -06001005 try:
1006 yield self.netconf_client.rpc(AdtranDeviceHandler.RESTART_RPC)
Chip Boling27275992017-09-22 15:17:04 -05001007
Adam Dyesse5e428a2019-02-01 08:10:24 -06001008 except Exception as e:
1009 self.log.exception('NETCONF-shutdown', e=e)
1010 returnValue(defer.fail(Failure()))
Chip Boling27275992017-09-22 15:17:04 -05001011
1012 # self.adapter_agent.unregister_for_onu_detect_state(self.device.id)
Chip Boling7294b252017-06-15 16:16:55 -05001013 # Update the operational status to ACTIVATING and connect status to
1014 # UNREACHABLE
1015
1016 device = self.adapter_agent.get_device(self.device_id)
1017 previous_oper_status = device.oper_status
1018 previous_conn_status = device.connect_status
1019 device.oper_status = OperStatus.ACTIVATING
1020 device.connect_status = ConnectStatus.UNREACHABLE
1021 self.adapter_agent.update_device(device)
1022
1023 # Update the child devices connect state to UNREACHABLE
1024 self.adapter_agent.update_child_devices_state(self.device_id,
1025 connect_status=ConnectStatus.UNREACHABLE)
Chip Boling7294b252017-06-15 16:16:55 -05001026
Chip Boling27275992017-09-22 15:17:04 -05001027 # Shutdown communications with OLT. Typically it takes about 2 seconds
1028 # or so after the reply before the restart actually occurs
Chip Boling7294b252017-06-15 16:16:55 -05001029
Adam Dyesse5e428a2019-02-01 08:10:24 -06001030 response = yield self._close_netconf_connection()
1031 if hasattr(response, 'ok'):
Chip Boling27275992017-09-22 15:17:04 -05001032 self.log.debug('Restart response XML was: {}'.format('ok' if response.ok else 'bad'))
Chip Boling7294b252017-06-15 16:16:55 -05001033
Chip Boling5561d552017-07-07 15:11:26 -05001034 self._rest_client = None
Chip Boling7294b252017-06-15 16:16:55 -05001035
1036 # Run remainder of reboot process as a new task. The OLT then may be up in a
1037 # few moments or may take 3 minutes or more depending on any self tests enabled
1038
Chip Boling5561d552017-07-07 15:11:26 -05001039 current_time = time.time()
Chip Boling7294b252017-06-15 16:16:55 -05001040 timeout = current_time + self.restart_failure_timeout
1041
Chip Boling27275992017-09-22 15:17:04 -05001042 self.startup = reactor.callLater(10, self._finish_reboot, timeout,
1043 previous_oper_status,
1044 previous_conn_status)
1045 returnValue(self.startup)
Chip Boling7294b252017-06-15 16:16:55 -05001046
1047 @inlineCallbacks
1048 def _finish_reboot(self, timeout, previous_oper_status, previous_conn_status):
1049 # Now wait until REST & NETCONF are re-established or we timeout
1050
Chip Boling252c7772017-08-16 10:13:17 -05001051 self.log.info('Resuming-activity',
Chip Boling5561d552017-07-07 15:11:26 -05001052 remaining=timeout - time.time(), timeout=timeout, current=time.time())
1053
1054 if self.rest_client is None:
Chip Boling7294b252017-06-15 16:16:55 -05001055 try:
Chip Boling27275992017-09-22 15:17:04 -05001056 yield self.make_restconf_connection(get_timeout=10)
Chip Boling5561d552017-07-07 15:11:26 -05001057
1058 except Exception:
1059 self.log.debug('No RESTCONF connection yet')
1060 self._rest_client = None
1061
1062 if self.netconf_client is None:
1063 try:
1064 yield self.make_netconf_connection(connect_timeout=10)
Adam Dyesse5e428a2019-02-01 08:10:24 -06001065 except:
1066 yield self._close_netconf_connection()
Chip Boling7294b252017-06-15 16:16:55 -05001067
Adam Dyesse5e428a2019-02-01 08:10:24 -06001068 if self.netconf_client is None or self.rest_client is None:
Chip Boling5561d552017-07-07 15:11:26 -05001069 current_time = time.time()
Chip Boling7294b252017-06-15 16:16:55 -05001070 if current_time < timeout:
Chip Boling27275992017-09-22 15:17:04 -05001071 self.startup = reactor.callLater(5, self._finish_reboot, timeout,
Chip Boling521735d2018-12-20 09:58:01 -06001072 previous_oper_status,
Chip Boling27275992017-09-22 15:17:04 -05001073 previous_conn_status)
1074 returnValue(self.startup)
Chip Boling7294b252017-06-15 16:16:55 -05001075
Adam Dyesse5e428a2019-02-01 08:10:24 -06001076 if self.netconf_client is None:
Chip Boling252c7772017-08-16 10:13:17 -05001077 self.log.error('NETCONF-restore-failure')
Chip Boling7294b252017-06-15 16:16:55 -05001078 pass # TODO: What is best course of action if cannot get clients back?
1079
1080 if self.rest_client is None:
Chip Boling252c7772017-08-16 10:13:17 -05001081 self.log.error('RESTCONF-restore-failure')
Chip Boling7294b252017-06-15 16:16:55 -05001082 pass # TODO: What is best course of action if cannot get clients back?
1083
Chip Boling5561d552017-07-07 15:11:26 -05001084 # Pause additional 5 seconds to let allow OLT microservices to complete some more initialization
Chip Boling7294b252017-06-15 16:16:55 -05001085 yield asleep(5)
Chip Bolingab8863d2018-03-22 14:50:31 -05001086 # TODO: Update device info. The software images may have changed...
Chip Boling7294b252017-06-15 16:16:55 -05001087 # Get the latest device reference
1088
1089 device = self.adapter_agent.get_device(self.device_id)
1090 device.oper_status = previous_oper_status
1091 device.connect_status = previous_conn_status
1092 self.adapter_agent.update_device(device)
1093
1094 # Update the child devices connect state to REACHABLE
1095 self.adapter_agent.update_child_devices_state(self.device_id,
1096 connect_status=ConnectStatus.REACHABLE)
Chip Boling69fba862017-08-18 15:11:32 -05001097 # Restart ports to previous state
Chip Boling69fba862017-08-18 15:11:32 -05001098 dl = []
1099
Adam Dyesse5e428a2019-02-01 08:10:24 -06001100 for port in self.all_ports:
Chip Boling69fba862017-08-18 15:11:32 -05001101 dl.append(port.restart())
1102
1103 try:
Chip Bolingfd1fd372017-12-20 13:34:12 -06001104 yield defer.gatherResults(dl, consumeErrors=True)
Chip Bolingb4868192018-11-01 16:38:44 -05001105
Chip Boling69fba862017-08-18 15:11:32 -05001106 except Exception as e:
1107 self.log.exception('port-restart', e=e)
1108
Chip Boling27275992017-09-22 15:17:04 -05001109 # Re-subscribe for ONU detection
1110 # self.adapter_agent.register_for_onu_detect_state(self.device.id)
Chip Boling69fba862017-08-18 15:11:32 -05001111 # Request reflow of any EVC/EVC-MAPs
Chip Boling69fba862017-08-18 15:11:32 -05001112 if len(self._evcs) > 0:
1113 dl = []
1114 for evc in self.evcs:
1115 dl.append(evc.reflow())
1116
1117 try:
1118 yield defer.gatherResults(dl)
1119 except Exception as e:
1120 self.log.exception('flow-restart', e=e)
Chip Boling7294b252017-06-15 16:16:55 -05001121
Chip Boling7294b252017-06-15 16:16:55 -05001122 self.log.info('rebooted', device_id=self.device_id)
Chip Boling5561d552017-07-07 15:11:26 -05001123 returnValue('Rebooted')
Chip Boling7294b252017-06-15 16:16:55 -05001124
Adam Dyesse5e428a2019-02-01 08:10:24 -06001125 def _cancel_tasks(self):
1126 # Cancel any outstanding tasks
1127 d, self.startup = self.startup, None
1128 try:
1129 if d is not None and not d.called:
1130 d.cancel()
1131 except:
1132 pass
1133
Chip Boling7294b252017-06-15 16:16:55 -05001134 @inlineCallbacks
1135 def delete(self):
1136 """
1137 This is called to delete a device from the PON based on a NBI call.
1138 If the device is an OLT then the whole PON will be deleted.
1139 """
1140 self.log.info('deleting', device_id=self.device_id)
Adam Dyesse5e428a2019-02-01 08:10:24 -06001141 self._cancel_tasks()
1142 self._suspend_heartbeat()
Chip Bolingce5bfc02018-08-09 13:57:49 -05001143
1144 # Get the latest device reference
1145 device = self.adapter_agent.get_device(self.device_id)
1146 device.reason = 'Deleting'
1147 self.adapter_agent.update_device(device)
1148
Chip Boling27275992017-09-22 15:17:04 -05001149 # self.adapter_agent.unregister_for_onu_detect_state(self.device.id)
Chip Boling7294b252017-06-15 16:16:55 -05001150
Chip Boling5561d552017-07-07 15:11:26 -05001151 # Remove all flows from the device
1152 # TODO: Create a bulk remove-all by device-id
Chip Boling7294b252017-06-15 16:16:55 -05001153
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001154 evcs = self._evcs
Chip Boling69fba862017-08-18 15:11:32 -05001155 self._evcs.clear()
Chip Boling5561d552017-07-07 15:11:26 -05001156
Chip Boling69fba862017-08-18 15:11:32 -05001157 for evc in evcs:
1158 evc.delete() # TODO: implement bulk-flow procedures
Chip Boling7294b252017-06-15 16:16:55 -05001159
1160 # Remove all child devices
1161 self.adapter_agent.delete_all_child_devices(self.device_id)
1162
Chip Bolingc64c8dc2018-04-20 14:42:24 -05001163 # Remove the logical device (should already be gone if disable came first)
1164 self._delete_logical_device()
Chip Boling7294b252017-06-15 16:16:55 -05001165
1166 # Remove the peer references from this device
1167 self.adapter_agent.delete_all_peer_references(self.device_id)
1168
1169 # Tell all ports to stop any background processing
1170
Adam Dyesse5e428a2019-02-01 08:10:24 -06001171 for port in self.all_ports:
Chip Boling7294b252017-06-15 16:16:55 -05001172 port.delete()
1173
1174 self.northbound_ports.clear()
1175 self.southbound_ports.clear()
1176
1177 # Shutdown communications with OLT
Adam Dyesse5e428a2019-02-01 08:10:24 -06001178 yield self._close_netconf_connection()
Chip Boling5561d552017-07-07 15:11:26 -05001179 self._rest_client = None
Chip Boling99c388e2018-11-20 09:33:57 -06001180 mgr, self.resource_mgr = self.resource_mgr, None
Adam Dyesse5e428a2019-02-01 08:10:24 -06001181 if mgr:
Chip Boling99c388e2018-11-20 09:33:57 -06001182 del mgr
Chip Boling7294b252017-06-15 16:16:55 -05001183
1184 self.log.info('deleted', device_id=self.device_id)
1185
Chip Boling99c388e2018-11-20 09:33:57 -06001186 def delete_child_device(self, proxy_address):
1187 self.log.debug('sending-deactivate-onu',
1188 olt_device_id=self.device_id,
1189 proxy_address=proxy_address)
1190 try:
1191 children = self.adapter_agent.get_child_devices(self.device_id)
1192 for child in children:
1193 if child.proxy_address.onu_id == proxy_address.onu_id and \
1194 child.proxy_address.channel_id == proxy_address.channel_id:
1195 self.adapter_agent.delete_child_device(self.device_id,
1196 child.id,
1197 onu_device=child)
1198 break
1199
1200 except Exception as e:
1201 self.log.error('adapter_agent error', error=e)
1202
Chip Boling5561d552017-07-07 15:11:26 -05001203 def packet_out(self, egress_port, msg):
Chip Bolingab8863d2018-03-22 14:50:31 -05001204 raise NotImplementedError('Overload in a derived class')
Chip Boling5561d552017-07-07 15:11:26 -05001205
1206 def update_pm_config(self, device, pm_config):
1207 # TODO: This has not been tested
1208 self.log.info('update_pm_config', pm_config=pm_config)
1209 self.pm_metrics.update(pm_config)
1210
Chip Boling3e3b1a92017-05-16 11:51:18 -05001211 @inlineCallbacks
1212 def get_device_info(self, device):
1213 """
1214 Perform an initial network operation to discover the device hardware
1215 and software version. Serial Number would be helpful as well.
1216
1217 Upon successfully retrieving the information, remember to call the
1218 'start_heartbeat' method to keep in contact with the device being managed
1219
1220 :param device: A voltha.Device object, with possible device-type
1221 specific extensions. Such extensions shall be described as part of
1222 the device type specification returned by device_types().
1223 """
Adam Dyesse5e428a2019-02-01 08:10:24 -06001224 yield None
Chip Boling7294b252017-06-15 16:16:55 -05001225 device = {}
Chip Boling7294b252017-06-15 16:16:55 -05001226 returnValue(device)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001227
1228 def start_heartbeat(self, delay=10):
Chip Boling27275992017-09-22 15:17:04 -05001229 assert delay > 1, 'Minimum heartbeat is 1 second'
Chip Boling252c7772017-08-16 10:13:17 -05001230 self.log.info('Starting-Device-Heartbeat ***')
Chip Boling3e3b1a92017-05-16 11:51:18 -05001231 self.heartbeat = reactor.callLater(delay, self.check_pulse)
Chip Boling5561d552017-07-07 15:11:26 -05001232 return self.heartbeat
Chip Boling3e3b1a92017-05-16 11:51:18 -05001233
Adam Dyesse5e428a2019-02-01 08:10:24 -06001234 def _suspend_heartbeat(self):
1235 # Suspend any active health-checks / pings
1236 h, self.heartbeat = self.heartbeat, None
1237 try:
1238 if h is not None and not h.called:
1239 h.cancel()
1240 except:
1241 pass
1242
Chip Boling3e3b1a92017-05-16 11:51:18 -05001243 def check_pulse(self):
1244 if self.logical_device_id is not None:
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001245 try:
1246 self.heartbeat = self.rest_client.request('GET', self.HELLO_URI,
Adam Dyesse5e428a2019-02-01 08:10:24 -06001247 name='hello', timeout=self.HEARTBEAT_TIMEOUT)
1248 self.heartbeat.addCallbacks(self._heartbeat_success)
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001249 except Exception as e:
Adam Dyesse5e428a2019-02-01 08:10:24 -06001250 self.heartbeat = reactor.callLater(self.HEARTBEAT_TIMEOUT, self._heartbeat_fail, e)
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001251
Chip Bolingfd1fd372017-12-20 13:34:12 -06001252 def on_heatbeat_alarm(self, active):
Adam Dyesse5e428a2019-02-01 08:10:24 -06001253 if active and (self.netconf_client is None or not self.netconf_client.connected):
Chip Bolingfd1fd372017-12-20 13:34:12 -06001254 self.make_netconf_connection(close_existing_client=True)
1255
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001256 def heartbeat_check_status(self, _):
Chip Boling3e3b1a92017-05-16 11:51:18 -05001257 """
1258 Check the number of heartbeat failures against the limit and emit an alarm if needed
1259 """
1260 device = self.adapter_agent.get_device(self.device_id)
1261
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001262 try:
Chip Bolingbffef5e2018-08-07 14:53:12 -05001263 from voltha.extensions.alarms.heartbeat_alarm import HeartbeatAlarm
Chip Bolingfd1fd372017-12-20 13:34:12 -06001264
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001265 if self.heartbeat_miss >= self.heartbeat_failed_limit:
1266 if device.connect_status == ConnectStatus.REACHABLE:
1267 self.log.warning('heartbeat-failed', count=self.heartbeat_miss)
1268 device.connect_status = ConnectStatus.UNREACHABLE
1269 device.oper_status = OperStatus.FAILED
1270 device.reason = self.heartbeat_last_reason
1271 self.adapter_agent.update_device(device)
Chip Bolingbffef5e2018-08-07 14:53:12 -05001272 HeartbeatAlarm(self.alarms, 'olt', self.heartbeat_miss).raise_alarm()
Chip Bolingfd1fd372017-12-20 13:34:12 -06001273 self.on_heatbeat_alarm(True)
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001274 else:
1275 # Update device states
1276 if device.connect_status != ConnectStatus.REACHABLE:
1277 device.connect_status = ConnectStatus.REACHABLE
1278 device.oper_status = OperStatus.ACTIVE
1279 device.reason = ''
1280 self.adapter_agent.update_device(device)
Chip Bolingbffef5e2018-08-07 14:53:12 -05001281 HeartbeatAlarm(self.alarms, 'olt').clear_alarm()
Chip Bolingfd1fd372017-12-20 13:34:12 -06001282 self.on_heatbeat_alarm(False)
Chip Bolingff3087f2017-10-12 09:26:29 -05001283
1284 if self.netconf_client is None or not self.netconf_client.connected:
1285 self.make_netconf_connection(close_existing_client=True)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001286
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001287 except Exception as e:
1288 self.log.exception('heartbeat-check', e=e)
Chip Boling3e3b1a92017-05-16 11:51:18 -05001289
1290 # Reschedule next heartbeat
1291 if self.logical_device_id is not None:
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001292 self.heartbeat_count += 1
Chip Boling3e3b1a92017-05-16 11:51:18 -05001293 self.heartbeat = reactor.callLater(self.heartbeat_interval, self.check_pulse)
1294
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001295 def _heartbeat_success(self, results):
1296 self.log.debug('heartbeat-success')
1297 self.heartbeat_miss = 0
1298 self.heartbeat_last_reason = ''
1299 self.heartbeat_check_status(results)
1300
1301 def _heartbeat_fail(self, failure):
Chip Boling3e3b1a92017-05-16 11:51:18 -05001302 self.heartbeat_miss += 1
1303 self.log.info('heartbeat-miss', failure=failure,
Chip Bolingef0e2fa2017-10-06 14:33:01 -05001304 count=self.heartbeat_count,
1305 miss=self.heartbeat_miss)
1306 self.heartbeat_last_reason = 'RESTCONF connectivity error'
Chip Boling3e3b1a92017-05-16 11:51:18 -05001307 self.heartbeat_check_status(None)
1308
Chip Boling3e3b1a92017-05-16 11:51:18 -05001309 @staticmethod
1310 def parse_module_revision(revision):
1311 try:
1312 return datetime.datetime.strptime(revision, '%Y-%m-%d')
1313 except Exception:
1314 return None