blob: 25eda2296e47ccfde1f0d24378db15015995c63d [file] [log] [blame]
Zsolt Haraszti66862032016-11-28 14:28:39 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Haraszti66862032016-11-28 14:28:39 -08003#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18A device agent is instantiated for each Device and plays an important role
19between the Device object and its adapter.
20"""
21import structlog
Girish Gowdrudf73b702018-08-08 21:17:05 -070022from copy import deepcopy
Zsolt Haraszti66862032016-11-28 14:28:39 -080023from twisted.internet import reactor
24from twisted.internet.defer import inlineCallbacks, returnValue
25
26from voltha.core.config.config_proxy import CallbackType
Lydia Fang01f2e852017-06-28 17:24:58 -070027from voltha.protos.common_pb2 import AdminState, OperStatus, ConnectStatus, \
khenaidoof3593a82018-06-01 16:41:31 -040028 OperationResp
Lydia Fang01f2e852017-06-28 17:24:58 -070029from voltha.protos.device_pb2 import ImageDownload
Zsolt Haraszti66862032016-11-28 14:28:39 -080030from voltha.registry import registry
khenaidoof3593a82018-06-01 16:41:31 -040031from voltha.protos.openflow_13_pb2 import Flows, FlowGroups, FlowChanges, \
32 FlowGroupChanges
33
Zsolt Haraszti66862032016-11-28 14:28:39 -080034
35class InvalidStateTransition(Exception): pass
36
37
38class DeviceAgent(object):
39
40 def __init__(self, core, initial_data):
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080041
Zsolt Haraszti66862032016-11-28 14:28:39 -080042 self.core = core
43 self._tmp_initial_data = initial_data
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080044 self.last_data = None
Sergio Slobodrian2db4c102017-03-09 22:29:23 -050045 self.calback_data = None
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080046
Zsolt Haraszti66862032016-11-28 14:28:39 -080047 self.proxy = core.get_proxy('/devices/{}'.format(initial_data.id))
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080048 self.flows_proxy = core.get_proxy(
49 '/devices/{}/flows'.format(initial_data.id))
50 self.groups_proxy = core.get_proxy(
51 '/devices/{}/flow_groups'.format(initial_data.id))
52
Sergio Slobodrian2db4c102017-03-09 22:29:23 -050053 self.pm_config_proxy = core.get_proxy(
54 '/devices/{}/pm_configs'.format(initial_data.id))
55
Lydia Fang01f2e852017-06-28 17:24:58 -070056 self.img_dnld_proxies = {}
57
Zsolt Haraszti66862032016-11-28 14:28:39 -080058 self.proxy.register_callback(
59 CallbackType.PRE_UPDATE, self._validate_update)
60 self.proxy.register_callback(
61 CallbackType.POST_UPDATE, self._process_update)
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080062
63 self.flows_proxy.register_callback(
khenaidoof3593a82018-06-01 16:41:31 -040064 CallbackType.PRE_UPDATE, self._pre_process_flows)
65
66 self.flows_proxy.register_callback(
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080067 CallbackType.POST_UPDATE, self._flow_table_updated)
68 self.groups_proxy.register_callback(
69 CallbackType.POST_UPDATE, self._group_table_updated)
70
Sergio Slobodrian2db4c102017-03-09 22:29:23 -050071 self.pm_config_proxy.register_callback(
72 CallbackType.POST_UPDATE, self._pm_config_updated)
73
Zsolt Harasztic5c5d102016-12-07 21:12:27 -080074 # to know device capabilities
75 self.device_type = core.get_proxy(
76 '/device_types/{}'.format(initial_data.type)).get()
77
Zsolt Haraszti66862032016-11-28 14:28:39 -080078 self.adapter_agent = None
khenaidoof3593a82018-06-01 16:41:31 -040079 self.flow_changes = None
Zsolt Haraszti89a27302016-12-08 16:53:06 -080080 self.log = structlog.get_logger(device_id=initial_data.id)
Zsolt Haraszti66862032016-11-28 14:28:39 -080081
82 @inlineCallbacks
khenaidoo032d3302017-06-09 14:50:04 -040083 def start(self, device=None, reconcile=False):
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -040084 self.log.debug('starting', device=device)
Zsolt Haraszti66862032016-11-28 14:28:39 -080085 self._set_adapter_agent()
khenaidoo032d3302017-06-09 14:50:04 -040086 if device:
87 # Starting from an existing data, so set the last_data
88 self.last_data = device
89 if reconcile:
90 self.reconcile_existing_device(device)
91 else:
92 yield self._process_update(self._tmp_initial_data)
93
Zsolt Haraszti66862032016-11-28 14:28:39 -080094 del self._tmp_initial_data
khenaidoo032d3302017-06-09 14:50:04 -040095
Zsolt Haraszti89a27302016-12-08 16:53:06 -080096 self.log.info('started')
Zsolt Haraszti66862032016-11-28 14:28:39 -080097 returnValue(self)
98
Khen Nursimulud068d812017-03-06 11:44:18 -050099 @inlineCallbacks
100 def stop(self, device):
101 self.log.debug('stopping', device=device)
102
103 # First, propagate this request to the device agents
104 yield self._delete_device(device)
105
Zsolt Haraszti66862032016-11-28 14:28:39 -0800106 self.proxy.unregister_callback(
107 CallbackType.PRE_UPDATE, self._validate_update)
108 self.proxy.unregister_callback(
109 CallbackType.POST_UPDATE, self._process_update)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800110 self.log.info('stopped')
Zsolt Haraszti66862032016-11-28 14:28:39 -0800111
Khen Nursimulud068d812017-03-06 11:44:18 -0500112 @inlineCallbacks
113 def reboot_device(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400114 self.log.debug('reboot-device', device=device, dry_run=dry_run)
Khen Nursimulud068d812017-03-06 11:44:18 -0500115 if not dry_run:
116 yield self.adapter_agent.reboot_device(device)
117
Lydia Fang01f2e852017-06-28 17:24:58 -0700118 def register_image_download(self, request):
119 try:
120 self.log.debug('register-image-download', request=request)
khenaidoof3593a82018-06-01 16:41:31 -0400121 path = '/devices/{}/image_downloads/{}'.format(request.id,
122 request.name)
Lydia Fang01f2e852017-06-28 17:24:58 -0700123 self.img_dnld_proxies[request.name] = self.core.get_proxy(path)
124 self.img_dnld_proxies[request.name].register_callback(
125 CallbackType.POST_UPDATE, self._update_image)
126 # trigger update callback
127 request.state = ImageDownload.DOWNLOAD_REQUESTED
128 self.img_dnld_proxies[request.name].update('/', request)
129 except Exception as e:
khenaidoof3593a82018-06-01 16:41:31 -0400130 self.log.exception(e.message)
Lydia Fang01f2e852017-06-28 17:24:58 -0700131
132 def activate_image_update(self, request):
133 try:
134 self.log.debug('activate-image-download', request=request)
135 request.image_state = ImageDownload.IMAGE_ACTIVATE
136 self.img_dnld_proxies[request.name].update('/', request)
137 except Exception as e:
khenaidoof3593a82018-06-01 16:41:31 -0400138 self.log.exception(e.message)
Lydia Fang01f2e852017-06-28 17:24:58 -0700139
140 def revert_image_update(self, request):
141 try:
142 self.log.debug('revert-image-download', request=request)
143 request.image_state = ImageDownload.IMAGE_REVERT
144 self.img_dnld_proxies[request.name].update('/', request)
145 except Exception as e:
khenaidoof3593a82018-06-01 16:41:31 -0400146 self.log.exception(e.message)
Lydia Fang01f2e852017-06-28 17:24:58 -0700147
148 @inlineCallbacks
149 def _download_image(self, device, img_dnld):
150 try:
151 self.log.debug('download-image', img_dnld=img_dnld)
152 yield self.adapter_agent.download_image(device, img_dnld)
153 except Exception as e:
154 self.log.exception(e.message)
155
156 def get_image_download_status(self, request):
157 try:
158 self.log.debug('get-image-download-status',
khenaidoof3593a82018-06-01 16:41:31 -0400159 request=request)
Lydia Fang01f2e852017-06-28 17:24:58 -0700160 device = self.proxy.get('/')
lcui33d6a8e2018-08-28 12:51:38 -0700161 return self.adapter_agent.get_image_download_status(device, request)
Lydia Fang01f2e852017-06-28 17:24:58 -0700162 except Exception as e:
163 self.log.exception(e.message)
lcui33d6a8e2018-08-28 12:51:38 -0700164 return None
Lydia Fang01f2e852017-06-28 17:24:58 -0700165
166 def cancel_image_download(self, img_dnld):
167 try:
168 self.log.debug('cancel-image-download',
khenaidoof3593a82018-06-01 16:41:31 -0400169 img_dnld=img_dnld)
Lydia Fang01f2e852017-06-28 17:24:58 -0700170 device = self.proxy.get('/')
171 self.adapter_agent.cancel_image_download(device, img_dnld)
lcui33d6a8e2018-08-28 12:51:38 -0700172 if device.admin_state == AdminState.DOWNLOADING_IMAGE:
173 device.admin_state = AdminState.ENABLED
174 self.proxy.update('/', device)
175 self.proxy.remove('/image_downloads/{}' \
176 .format(img_dnld.name), img_dnld)
Lydia Fang01f2e852017-06-28 17:24:58 -0700177 except Exception as e:
178 self.log.exception(e.message)
179
180 def update_device_image_download(self, img_dnld):
181 try:
182 self.log.debug('update-device-image-download',
khenaidoof3593a82018-06-01 16:41:31 -0400183 img_dnld=img_dnld)
184 self.proxy.update('/image_downloads/{}' \
185 .format(img_dnld.name), img_dnld)
Lydia Fang01f2e852017-06-28 17:24:58 -0700186 except Exception as e:
187 self.log.exception(e.message)
188
189 def unregister_device_image_download(self, name):
190 try:
191 self.log.debug('unregister-device-image-download',
khenaidoof3593a82018-06-01 16:41:31 -0400192 name=name)
Lydia Fang01f2e852017-06-28 17:24:58 -0700193 self.self_proxies[name].unregister_callback(
194 CallbackType.POST_ADD, self._download_image)
195 self.self_proxies[name].unregister_callback(
196 CallbackType.POST_UPDATE, self._process_image)
197 except Exception as e:
khenaidoof3593a82018-06-01 16:41:31 -0400198 self.log.exception(e.message)
Lydia Fang01f2e852017-06-28 17:24:58 -0700199
200 @inlineCallbacks
201 def _update_image(self, img_dnld):
202 try:
203 self.log.debug('update-image', img_dnld=img_dnld)
204 # handle download
205 if img_dnld.state == ImageDownload.DOWNLOAD_REQUESTED:
206 device = self.proxy.get('/')
207 yield self._download_image(device, img_dnld)
lcui33d6a8e2018-08-28 12:51:38 -0700208 # set back to ENABLE to be allowed to activate
209 device.admin_state = AdminState.ENABLED
210 self.proxy.update('/', device)
Lydia Fang01f2e852017-06-28 17:24:58 -0700211 if img_dnld.image_state == ImageDownload.IMAGE_ACTIVATE:
212 device = self.proxy.get('/')
lcui33d6a8e2018-08-28 12:51:38 -0700213 device.admin_state = AdminState.DOWNLOADING_IMAGE
214 self.proxy.update('/', device)
215 yield self.adapter_agent.activate_image_update(device, img_dnld)
lcuie3aefd92019-01-02 22:50:32 -0800216 device.admin_state = AdminState.ENABLED
217 self.proxy.update('/', device)
Lydia Fang01f2e852017-06-28 17:24:58 -0700218 elif img_dnld.image_state == ImageDownload.IMAGE_REVERT:
219 device = self.proxy.get('/')
220 yield self.adapter_agent.revert_image_update(device, img_dnld)
221 except Exception as e:
khenaidoof3593a82018-06-01 16:41:31 -0400222 self.log.exception(e.message)
Lydia Fang01f2e852017-06-28 17:24:58 -0700223
Khen Nursimulud068d812017-03-06 11:44:18 -0500224 @inlineCallbacks
sathishg5ae86222017-06-28 15:16:29 +0530225 def self_test(self, device, dry_run=False):
226 self.log.debug('self-test-device', device=device, dry_run=dry_run)
227 if not dry_run:
228 result = yield self.adapter_agent.self_test(device)
229 returnValue(result)
230
231 @inlineCallbacks
Khen Nursimulud068d812017-03-06 11:44:18 -0500232 def get_device_details(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400233 self.log.debug('get-device-details', device=device, dry_run=dry_run)
Khen Nursimulud068d812017-03-06 11:44:18 -0500234 if not dry_run:
235 yield self.adapter_agent.get_device_details(device)
236
Stephane Barbarie980a0912017-05-11 11:27:06 -0400237 @inlineCallbacks
238 def suppress_alarm(self, filter):
239 self.log.debug('suppress-alarms')
240 try:
241 yield self.adapter_agent.suppress_alarm(filter)
242 except Exception as e:
243 self.log.exception(e.message)
244
245 @inlineCallbacks
246 def unsuppress_alarm(self, filter):
247 self.log.debug('unsuppress-alarms')
248 try:
249 yield self.adapter_agent.unsuppress_alarm(filter)
250 except Exception as e:
251 self.log.exception(e.message)
252
khenaidoo032d3302017-06-09 14:50:04 -0400253 @inlineCallbacks
254 def reconcile_existing_device(self, device, dry_run=False):
255 self.log.debug('reconcile-existing-device',
khenaidoof3593a82018-06-01 16:41:31 -0400256 device=device,
257 dry_run=False)
khenaidoo032d3302017-06-09 14:50:04 -0400258 if not dry_run:
259 yield self.adapter_agent.reconcile_device(device)
260
Zsolt Haraszti66862032016-11-28 14:28:39 -0800261 def _set_adapter_agent(self):
262 adapter_name = self._tmp_initial_data.adapter
263 if adapter_name == '':
264 proxy = self.core.get_proxy('/')
265 known_device_types = dict(
266 (dt.id, dt) for dt in proxy.get('/device_types'))
267 device_type = known_device_types[self._tmp_initial_data.type]
268 adapter_name = device_type.adapter
269 assert adapter_name != ''
270 self.adapter_agent = registry('adapter_loader').get_agent(adapter_name)
271
272 @inlineCallbacks
273 def _validate_update(self, device):
274 """
275 Called before each update, it allows the blocking of the update
276 (by raising an exception), or even the augmentation of the incoming
277 data.
278 """
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800279 self.log.debug('device-pre-update', device=device)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800280 yield self._process_state_transitions(device, dry_run=True)
281 returnValue(device)
282
283 @inlineCallbacks
284 def _process_update(self, device):
285 """
286 Called after the device object was updated (individually or part of
287 a transaction), and it is used to propagate the change down to the
288 adapter
289 """
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800290 self.log.debug('device-post-update', device=device)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800291
292 # first, process any potential state transition
293 yield self._process_state_transitions(device)
294
295 # finally, store this data as last data so we can see what changed
296 self.last_data = device
297
298 @inlineCallbacks
299 def _process_state_transitions(self, device, dry_run=False):
300
301 old_admin_state = getattr(self.last_data, 'admin_state',
khenaidoof3593a82018-06-01 16:41:31 -0400302 AdminState.UNKNOWN)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800303 new_admin_state = device.admin_state
khenaidoo032d3302017-06-09 14:50:04 -0400304 self.log.debug('device-admin-states', old_state=old_admin_state,
rshettyc26a3c32017-07-27 11:06:38 +0530305 new_state=new_admin_state, dry_run=dry_run)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800306 transition_handler = self.admin_state_fsm.get(
307 (old_admin_state, new_admin_state), None)
308 if transition_handler is None:
rshettyc26a3c32017-07-27 11:06:38 +0530309 self.log.debug('No Operation', old_state=old_admin_state,
310 new_state=new_admin_state, dry_run=dry_run)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800311 pass # no-op
312 elif transition_handler is False:
313 raise InvalidStateTransition('{} -> {}'.format(
314 old_admin_state, new_admin_state))
315 else:
316 assert callable(transition_handler)
317 yield transition_handler(self, device, dry_run)
318
319 @inlineCallbacks
320 def _activate_device(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400321 self.log.debug('activate-device', device=device, dry_run=dry_run)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800322 if not dry_run:
Zsolt Haraszti66862032016-11-28 14:28:39 -0800323 device.oper_status = OperStatus.ACTIVATING
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800324 self.update_device(device)
325 yield self.adapter_agent.adopt_device(device)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800326
Girish61687212018-01-08 12:48:58 +0530327 @inlineCallbacks
Zsolt Haraszti66862032016-11-28 14:28:39 -0800328 def update_device(self, device):
Nicolas Palpacuer253461f2018-06-01 12:01:45 -0400329 self.log.debug('updating-device', device=device.id)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800330 self.last_data = device # so that we don't propagate back
331 self.proxy.update('/', device)
ggowdru64d738a2018-05-10 07:08:06 -0700332 if device.admin_state == AdminState.ENABLED and \
khenaidoof3593a82018-06-01 16:41:31 -0400333 device.oper_status == OperStatus.ACTIVE and \
334 device.connect_status == ConnectStatus.REACHABLE:
Nikolay Titov89004ec2017-06-19 18:22:42 -0400335 self.log.info('replay-create-interfaces ', device=device.id)
336 self.core.xpon_agent.replay_interface(device.id)
Girish61687212018-01-08 12:48:58 +0530337 # if device accepts bulk flow update, lets just call that
338 if self.device_type.accepts_bulk_flow_update:
339 flows = self.flows_proxy.get('/') # gather flows
khenaidoof3593a82018-06-01 16:41:31 -0400340 groups = self.groups_proxy.get('/') # gather flow groups
Girish61687212018-01-08 12:48:58 +0530341 self.log.info('replay-flows ', device=device.id)
342 yield self.adapter_agent.update_flows_bulk(
343 device=device,
344 flows=flows,
345 groups=groups)
346
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500347 def update_device_pm_config(self, device_pm_config, init=False):
khenaidoof3593a82018-06-01 16:41:31 -0400348 self.callback_data = init # so that we don't push init data
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500349 self.pm_config_proxy.update('/', device_pm_config)
350
Zsolt Haraszti66862032016-11-28 14:28:39 -0800351 def _propagate_change(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400352 self.log.debug('propagate-change', device=device, dry_run=dry_run)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800353 if device != self.last_data:
nick369a5062018-05-29 17:11:06 -0400354 self.log.warn('Not-implemented-default-to-noop')
khenaidoof3593a82018-06-01 16:41:31 -0400355 # raise NotImplementedError()
Zsolt Haraszti66862032016-11-28 14:28:39 -0800356 else:
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800357 self.log.debug('no-op')
Zsolt Haraszti66862032016-11-28 14:28:39 -0800358
359 def _abandon_device(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400360 self.log.debug('abandon-device', device=device, dry_run=dry_run)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800361 raise NotImplementedError()
362
Khen Nursimulu49792142017-03-17 12:34:05 -0400363 def _delete_all_flows(self):
364 """ Delete all flows on the device """
365 try:
366 self.flows_proxy.update('/', Flows(items=[]))
367 self.groups_proxy.update('/', FlowGroups(items=[]))
368 except Exception, e:
369 self.exception('flow-delete-exception', e=e)
370
Khen Nursimulud068d812017-03-06 11:44:18 -0500371 @inlineCallbacks
Zsolt Haraszti66862032016-11-28 14:28:39 -0800372 def _disable_device(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400373 try:
374 self.log.debug('disable-device', device=device, dry_run=dry_run)
375 if not dry_run:
376 # Remove all flows before disabling device
Shad Ansari4672fd72019-03-13 13:31:51 -0700377 # Only do this for ONUs. ponsim seems to want the flows
378 # to be removed on onu disable and it does not seem to
379 # hurt the real devices. Flows on OLT are not removed when
380 # OLT is disabled - each adapter may do its own thing
381 # for disable -e.g. OpenOLT disables the NNI interface.
382 if not device.root:
383 self._delete_all_flows()
khenaidoo032d3302017-06-09 14:50:04 -0400384 yield self.adapter_agent.disable_device(device)
385 except Exception, e:
386 self.log.exception('error', e=e)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800387
Khen Nursimulud068d812017-03-06 11:44:18 -0500388 @inlineCallbacks
Zsolt Haraszti66862032016-11-28 14:28:39 -0800389 def _reenable_device(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400390 self.log.debug('reenable-device', device=device, dry_run=dry_run)
Khen Nursimulud068d812017-03-06 11:44:18 -0500391 if not dry_run:
392 yield self.adapter_agent.reenable_device(device)
393
394 @inlineCallbacks
395 def _delete_device(self, device, dry_run=False):
khenaidoo032d3302017-06-09 14:50:04 -0400396 self.log.debug('delete-device', device=device, dry_run=dry_run)
Khen Nursimulud068d812017-03-06 11:44:18 -0500397 if not dry_run:
398 yield self.adapter_agent.delete_device(device)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800399
Scott Bakerd3190952018-09-04 15:47:28 -0700400 def simulate_alarm(self, device, alarm):
401 try:
402 self.log.debug('simulate_alarm', alarm=alarm)
403 return self.adapter_agent.simulate_alarm(device, alarm)
404 except Exception as e:
405 self.log.exception(e.message)
406 return OperationResp(code=OperationResp.OPERATION_FAILURE)
407
Zsolt Haraszti66862032016-11-28 14:28:39 -0800408 admin_state_fsm = {
409
410 # Missing entries yield no-op
411 # False means invalid state change
412
413 (AdminState.UNKNOWN, AdminState.ENABLED): _activate_device,
414
415 (AdminState.PREPROVISIONED, AdminState.UNKNOWN): False,
416 (AdminState.PREPROVISIONED, AdminState.ENABLED): _activate_device,
Lydia Fang01f2e852017-06-28 17:24:58 -0700417 (AdminState.PREPROVISIONED, AdminState.DOWNLOADING_IMAGE): False,
Zsolt Haraszti66862032016-11-28 14:28:39 -0800418
419 (AdminState.ENABLED, AdminState.UNKNOWN): False,
420 (AdminState.ENABLED, AdminState.ENABLED): _propagate_change,
421 (AdminState.ENABLED, AdminState.DISABLED): _disable_device,
422 (AdminState.ENABLED, AdminState.PREPROVISIONED): _abandon_device,
423
424 (AdminState.DISABLED, AdminState.UNKNOWN): False,
425 (AdminState.DISABLED, AdminState.PREPROVISIONED): _abandon_device,
Lydia Fang01f2e852017-06-28 17:24:58 -0700426 (AdminState.DISABLED, AdminState.ENABLED): _reenable_device,
427 (AdminState.DISABLED, AdminState.DOWNLOADING_IMAGE): False,
428
429 (AdminState.DOWNLOADING_IMAGE, AdminState.DISABLED): False
Zsolt Haraszti66862032016-11-28 14:28:39 -0800430
431 }
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800432
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500433 ## <======================= PM CONFIG UPDATE HANDLING ====================
434
khenaidoof3593a82018-06-01 16:41:31 -0400435 # @inlineCallbacks
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500436 def _pm_config_updated(self, pm_configs):
437 self.log.debug('pm-config-updated', pm_configs=pm_configs,
438 callback_data=self.callback_data)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400439 device_id = self.proxy.get('/').id
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500440 if not self.callback_data:
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400441 self.adapter_agent.update_adapter_pm_config(device_id, pm_configs)
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500442 self.callback_data = None
443
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800444 ## <======================= FLOW TABLE UPDATE HANDLING ====================
445
khenaidoof3593a82018-06-01 16:41:31 -0400446 def _pre_process_flows(self, flows):
447 """
448 This method is invoked before a device flow table data model is
449 updated. If the device supports accepts_add_remove_flow_updates then it
450 pre-processes the desired flows against what currently
451 exist on the device to figure out which flows to delete and which
452 ones to add. The resulting data is stored locally and the flow table is
453 updated during the post-processing phase, i.e. via the POST_UPDATE
454 callback
455 :param flows: Desired flows
456 :return: None
457 """
Nicolas Palpacuer8b331302018-06-20 16:57:11 -0400458 self.current_flows = self.flows_proxy.get('/')
Saurav Das458b7902019-02-25 22:48:05 -0800459 # self.log.debug('pre-processing-flows',
460 # logical_device_id=self.last_data.id,
461 # desired_flows=flows,
462 # existing_flows=self.current_flows)
khenaidoof3593a82018-06-01 16:41:31 -0400463
Nicolas Palpacuer8b331302018-06-20 16:57:11 -0400464 if self.flow_changes is None:
465 self.flow_changes = FlowChanges()
khenaidoof3593a82018-06-01 16:41:31 -0400466 else:
Nicolas Palpacuer8b331302018-06-20 16:57:11 -0400467 del self.flow_changes.to_add.items[:]
468 del self.flow_changes.to_remove.items[:]
469
470 current_flow_ids = set(f.id for f in self.current_flows.items)
471 desired_flow_ids = set(f.id for f in flows.items)
472
473 ids_to_add = desired_flow_ids.difference(current_flow_ids)
474 ids_to_del = current_flow_ids.difference(desired_flow_ids)
475
476 for f in flows.items:
477 if f.id in ids_to_add:
478 self.flow_changes.to_add.items.extend([f])
479
480 for f in self.current_flows.items:
481 if f.id in ids_to_del:
482 self.flow_changes.to_remove.items.extend([f])
483
484 self.log.debug('pre-processed-flows',
485 logical_device_id=self.last_data.id,
Saurav Das458b7902019-02-25 22:48:05 -0800486 flows_to_add=len(self.flow_changes.to_add.items),
487 flows_to_remove=len(self.flow_changes.to_remove.items))
Nicolas Palpacuer8b331302018-06-20 16:57:11 -0400488
khenaidoof3593a82018-06-01 16:41:31 -0400489
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800490 @inlineCallbacks
491 def _flow_table_updated(self, flows):
Jeff5d5d5042018-07-05 10:54:37 -0700492 try:
493 self.log.debug('flow-table-updated',
Saurav Das458b7902019-02-25 22:48:05 -0800494 logical_device_id=self.last_data.id)
Jeff5d5d5042018-07-05 10:54:37 -0700495 if self.flow_changes is not None and (len(self.flow_changes.to_remove.items) == 0) and (len(
496 self.flow_changes.to_add.items) == 0):
497 self.log.debug('no-flow-update-required',
498 logical_device_id=self.last_data.id)
Nicolas Palpacuere7359fc2018-06-15 14:10:48 -0400499 else:
Jeff5d5d5042018-07-05 10:54:37 -0700500
501 # if device accepts non-bulk flow update, lets just call that first
502 if self.device_type.accepts_add_remove_flow_updates:
503
504 try:
Girish Gowdrudf73b702018-08-08 21:17:05 -0700505 flow_changes = deepcopy(self.flow_changes)
Jeff5d5d5042018-07-05 10:54:37 -0700506 yield self.adapter_agent.update_flows_incrementally(
507 device=self.last_data,
Girish Gowdrudf73b702018-08-08 21:17:05 -0700508 flow_changes=flow_changes,
Jeff5d5d5042018-07-05 10:54:37 -0700509 group_changes=FlowGroupChanges()
510 )
511 except Exception as e:
512 self.log.exception("Failure-updating-flows", e=e)
513
514 # if device accepts bulk flow update, lets just call that
515 elif self.device_type.accepts_bulk_flow_update:
516 self.log.debug('invoking bulk')
517 groups = self.groups_proxy.get('/') # gather flow groups
518 yield self.adapter_agent.update_flows_bulk(
519 device=self.last_data,
520 flows=flows,
521 groups=groups)
522 # add ability to notify called when an flow update completes
523 # see https://jira.opencord.org/browse/CORD-839
524 else:
525 raise NotImplementedError()
526 except Exception as e:
527 self.log.error('error', e=e)
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800528
529 ## <======================= GROUP TABLE UPDATE HANDLING ===================
530
531 @inlineCallbacks
532 def _group_table_updated(self, groups):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800533 self.log.debug('group-table-updated',
khenaidoof3593a82018-06-01 16:41:31 -0400534 logical_device_id=self.last_data.id,
535 flow_groups=groups)
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800536
537 # if device accepts bulk flow update, lets just call that
538 if self.device_type.accepts_bulk_flow_update:
539 flows = self.flows_proxy.get('/') # gather flows
540 yield self.adapter_agent.update_flows_bulk(
541 device=self.last_data,
542 flows=flows,
543 groups=groups)
alshabibb5d77812017-02-01 20:21:49 -0800544 # add ability to notify called when an group update completes
545 # see https://jira.opencord.org/browse/CORD-839
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800546
Khen Nursimulud068d812017-03-06 11:44:18 -0500547 elif self.device_type.accepts_add_remove_flow_updates:
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800548 raise NotImplementedError()
549
550 else:
551 raise NotImplementedError()