Passed
Pull Request — master (#104)
by
unknown
01:57
created

build.main.Main._get_links_dict()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 1
dl 0
loc 4
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
"""Main module of kytos/topology Kytos Network Application.
2
3
Manage the network topology
4
"""
5 1
import time
6
7 1
from flask import jsonify, request
8 1
9 1
from kytos.core import KytosEvent, KytosNApp, log, rest
10 1
from kytos.core.helpers import listen_to
11 1
from kytos.core.interface import Interface
12
from kytos.core.link import Link
13 1
from kytos.core.switch import Switch
14
from napps.kytos.topology import settings
15
from napps.kytos.topology.models import Topology
16 1
from napps.kytos.topology.storehouse import StoreHouse
17
18
DEFAULT_LINK_UP_TIMER = 10
19
20
21
class Main(KytosNApp):  # pylint: disable=too-many-public-methods
22 1
    """Main class of kytos/topology NApp.
23
24 1
    This class is the entry point for this napp.
25 1
    """
26
27 1
    def setup(self):
28 1
        """Initialize the NApp's links list."""
29 1
        self.links = {}
30
        self.store_items = {}
31 1
        self.link_up_timer = getattr(settings, 'LINK_UP_TIMER',
32
                                     DEFAULT_LINK_UP_TIMER)
33
34 1
        self.verify_storehouse('switches')
35
        self.verify_storehouse('interfaces')
36
        self.verify_storehouse('links')
37
38 1
        self.storehouse = StoreHouse(self.controller)
39
40
    def execute(self):
41
        """Do nothing."""
42
43
    def shutdown(self):
44
        """Do nothing."""
45
        log.info('NApp kytos/topology shutting down.')
46
47
    def _get_link_or_create(self, endpoint_a, endpoint_b):
48 1
        new_link = Link(endpoint_a, endpoint_b)
49
50
        for link in self.links.values():
51
            if new_link == link:
52
                return link
53 1
54
        self.links[new_link.id] = new_link
55
        return new_link
56
57
    def _get_switches_dict(self):
58 1
        """Return a dictionary with the known switches."""
59
        switches = {'switches': {}}
60
        for idx, switch in enumerate(self.controller.switches.values()):
61
            switch_data = switch.as_dict()
62
            if not all(key in switch_data['metadata']
63 1
                       for key in ('lat', 'lng')):
64
                # Switches are initialized somewhere in the ocean
65 1
                switch_data['metadata']['lat'] = str(0.0)
66
                switch_data['metadata']['lng'] = str(-30.0+idx*10.0)
67 1
            switches['switches'][switch.id] = switch_data
68
        return switches
69
70
    def _get_links_dict(self):
71
        """Return a dictionary with the known links."""
72
        return {'links': {l.id: l.as_dict() for l in
73
                          self.links.values()}}
74 1
75
    def _get_topology_dict(self):
76
        """Return a dictionary with the known topology."""
77
        return {'topology': {**self._get_switches_dict(),
78
                             **self._get_links_dict()}}
79
80
    def _get_topology(self):
81
        """Return an object representing the topology."""
82
        return Topology(self.controller.switches, self.links)
83 1
84
    def _get_link_from_interface(self, interface):
85
        """Return the link of the interface, or None if it does not exist."""
86
        for link in self.links.values():
87
            if interface in (link.endpoint_a, link.endpoint_b):
88 1
                return link
89
        return None
90
91
    def _restore_status(self, switches_status, interfaces_status):
92
        """Restore the network administrative status saved in StoreHouse."""
93
        # restore Switches
94
        for switch_id, state in switches_status.items():
95
            try:
96
                if state:
97 1
                    self.controller.switches[switch_id].enable()
98
                else:
99
                    self.controller.switches[switch_id].disable()
100
            except KeyError:
101
                error = ('Error while restoring switches status. The '
102
                         f'{switch_id} does not exist.')
103
                raise KeyError(error)
104
        # restore interfaces
105
        for interface_id, state in interfaces_status.items():
106 1
            switch_id = ":".join(interface_id.split(":")[:-1])
107
            interface_number = int(interface_id.split(":")[-1])
108
            try:
109
                switch = self.controller.switches[switch_id]
110
                if state:
111
                    switch.interfaces[interface_number].enable()
112
                else:
113
                    switch.interfaces[interface_number].disable()
114
            except KeyError:
115 1
                error = ('Error while restoring interface status. The '
116
                         f'interface {interface_id} does not exist.')
117
                raise KeyError(error)
118
119
        log.info('Network status restored.')
120
121
    def _load_network_status(self):
122
        """Load network status saved in storehouse."""
123
        switches_status = {}
124
        interfaces_status = {}
125
        status = self.storehouse.get_data()
126
        if status:
127
            switches = status.get(0)['switches']
128 1
            for switch, switch_attributes in switches.items():
129
                # get status the switches
130
                switches_status[switch] = switch_attributes.get('enabled')
131
                interfaces = switch_attributes['interfaces']
132
                # get status the interfaces
133
                for interface, interface_attributes in interfaces.items():
134
                    enabled_value = interface_attributes.get('enabled')
135
                    interfaces_status[interface] = enabled_value
136
137
        else:
138
            error = 'There is no status saved to restore.'
139
            log.info(error)
140
            raise FileNotFoundError(error)
141 1
142
        return switches_status, interfaces_status
143
144
    @rest('v3/')
145
    def get_topology(self):
146
        """Return the latest known topology.
147
148
        This topology is updated when there are network events.
149
        """
150
        return jsonify(self._get_topology_dict())
151
152 1
    @rest('v3/restore')
153
    def restore_network_status(self):
154
        """Restore the network administrative status saved in StoreHouse."""
155
        try:
156
            switches_status, interfaces_status = self._load_network_status()
157
            self._restore_status(switches_status, interfaces_status)
158
        except (KeyError, FileNotFoundError) as exc:
159
            return jsonify(f'{str(exc)}'), 404
160
161
        return jsonify('Administrative status restored.'), 200
162
163
    # Switch related methods
164
    @rest('v3/switches')
165
    def get_switches(self):
166
        """Return a json with all the switches in the topology."""
167
        return jsonify(self._get_switches_dict())
168
169
    @rest('v3/switches/<dpid>/enable', methods=['POST'])
170 1
    def enable_switch(self, dpid):
171
        """Administratively enable a switch in the topology."""
172
        try:
173
            self.controller.switches[dpid].enable()
174
            self.save_status_on_storehouse()
175
            return jsonify("Operation successful"), 201
176
        except KeyError:
177
            return jsonify("Switch not found"), 404
178
179
    @rest('v3/switches/<dpid>/disable', methods=['POST'])
180
    def disable_switch(self, dpid):
181
        """Administratively disable a switch in the topology."""
182
        try:
183
            self.controller.switches[dpid].disable()
184
            self.save_status_on_storehouse()
185
            return jsonify("Operation successful"), 201
186
        except KeyError:
187
            return jsonify("Switch not found"), 404
188 1
189
    @rest('v3/switches/<dpid>/metadata')
190
    def get_switch_metadata(self, dpid):
191
        """Get metadata from a switch."""
192
        try:
193
            return jsonify({"metadata":
194
                            self.controller.switches[dpid].metadata}), 200
195
        except KeyError:
196
            return jsonify("Switch not found"), 404
197
198
    @rest('v3/switches/<dpid>/metadata', methods=['POST'])
199
    def add_switch_metadata(self, dpid):
200
        """Add metadata to a switch."""
201
        metadata = request.get_json()
202
        try:
203
            switch = self.controller.switches[dpid]
204
        except KeyError:
205 1
            return jsonify("Switch not found"), 404
206
207
        switch.extend_metadata(metadata)
208
        self.notify_metadata_changes(switch, 'added')
209
        return jsonify("Operation successful"), 201
210
211
    @rest('v3/switches/<dpid>/metadata/<key>', methods=['DELETE'])
212
    def delete_switch_metadata(self, dpid, key):
213
        """Delete metadata from a switch."""
214
        try:
215
            switch = self.controller.switches[dpid]
216
        except KeyError:
217
            return jsonify("Switch not found"), 404
218
219
        switch.remove_metadata(key)
220
        self.notify_metadata_changes(switch, 'removed')
221
        return jsonify("Operation successful"), 200
222
223
    # Interface related methods
224
    @rest('v3/interfaces')
225
    def get_interfaces(self):
226 1
        """Return a json with all the interfaces in the topology."""
227
        interfaces = {}
228
        switches = self._get_switches_dict()
229
        for switch in switches['switches'].values():
230
            for interface_id, interface in switch['interfaces'].items():
231
                interfaces[interface_id] = interface
232
233
        return jsonify({'interfaces': interfaces})
234
235 View Code Duplication
    @rest('v3/interfaces/switch/<dpid>/enable', methods=['POST'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
236
    @rest('v3/interfaces/<interface_enable_id>/enable', methods=['POST'])
237
    def enable_interface(self, interface_enable_id=None, dpid=None):
238
        """Administratively enable interfaces in the topology."""
239
        error_list = []  # List of interfaces that were not activated.
240
        msg_error = "Some interfaces couldn't be found and activated: "
241
        if dpid is None:
242
            dpid = ":".join(interface_enable_id.split(":")[:-1])
243
        try:
244
            switch = self.controller.switches[dpid]
245
        except KeyError as exc:
246
            return jsonify(f"Switch not found: {exc}"), 404
247
248
        if interface_enable_id:
249 1
            interface_number = int(interface_enable_id.split(":")[-1])
250
251
            try:
252
                switch.interfaces[interface_number].enable()
253
            except KeyError as exc:
254
                error_list.append(f"Switch {dpid} Interface {exc}")
255
        else:
256
            for interface in switch.interfaces.values():
257 1
                interface.enable()
258
        if not error_list:
259
            self.save_status_on_storehouse()
260
            return jsonify("Operation successful"), 200
261
        return jsonify({msg_error:
262
                        error_list}), 409
263
264 View Code Duplication
    @rest('v3/interfaces/switch/<dpid>/disable', methods=['POST'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
265
    @rest('v3/interfaces/<interface_disable_id>/disable', methods=['POST'])
266
    def disable_interface(self, interface_disable_id=None, dpid=None):
267 1
        """Administratively disable interfaces in the topology."""
268
        error_list = []  # List of interfaces that were not deactivated.
269
        msg_error = "Some interfaces couldn't be found and deactivated: "
270
        if dpid is None:
271
            dpid = ":".join(interface_disable_id.split(":")[:-1])
272
        try:
273
            switch = self.controller.switches[dpid]
274
        except KeyError as exc:
275
            return jsonify(f"Switch not found: {exc}"), 404
276
277 1
        if interface_disable_id:
278
            interface_number = int(interface_disable_id.split(":")[-1])
279
280
            try:
281
                switch.interfaces[interface_number].disable()
282
            except KeyError as exc:
283
                error_list.append(f"Switch {dpid} Interface {exc}")
284
        else:
285 1
            for interface in switch.interfaces.values():
286
                interface.disable()
287
        if not error_list:
288
            self.save_status_on_storehouse()
289
            return jsonify("Operation successful"), 200
290
        return jsonify({msg_error:
291
                        error_list}), 409
292
293
    @rest('v3/interfaces/<interface_id>/metadata')
294
    def get_interface_metadata(self, interface_id):
295
        """Get metadata from an interface."""
296
        switch_id = ":".join(interface_id.split(":")[:-1])
297
        interface_number = int(interface_id.split(":")[-1])
298 1
        try:
299
            switch = self.controller.switches[switch_id]
300
        except KeyError:
301
            return jsonify("Switch not found"), 404
302
303
        try:
304
            interface = switch.interfaces[interface_number]
305
        except KeyError:
306
            return jsonify("Interface not found"), 404
307
308
        return jsonify({"metadata": interface.metadata}), 200
309
310
    @rest('v3/interfaces/<interface_id>/metadata', methods=['POST'])
311
    def add_interface_metadata(self, interface_id):
312 1
        """Add metadata to an interface."""
313
        metadata = request.get_json()
314
315
        switch_id = ":".join(interface_id.split(":")[:-1])
316
        interface_number = int(interface_id.split(":")[-1])
317
        try:
318
            switch = self.controller.switches[switch_id]
319 1
        except KeyError:
320 1
            return jsonify("Switch not found"), 404
321 1
322 1
        try:
323 1
            interface = switch.interfaces[interface_number]
324
        except KeyError:
325 1
            return jsonify("Interface not found"), 404
326
327
        interface.extend_metadata(metadata)
328
        self.notify_metadata_changes(interface, 'added')
329
        return jsonify("Operation successful"), 201
330
331
    @rest('v3/interfaces/<interface_id>/metadata/<key>', methods=['DELETE'])
332 1
    def delete_interface_metadata(self, interface_id, key):
333 1
        """Delete metadata from an interface."""
334 1
        switch_id = ":".join(interface_id.split(":")[:-1])
335 1
        interface_number = int(interface_id.split(":")[-1])
336 1
337
        try:
338 1
            switch = self.controller.switches[switch_id]
339
        except KeyError:
340
            return jsonify("Switch not found"), 404
341
342
        try:
343 1
            interface = switch.interfaces[interface_number]
344 1
        except KeyError:
345 1
            return jsonify("Interface not found"), 404
346 1
347
        if interface.remove_metadata(key) is False:
348 1
            return jsonify("Metadata not found"), 404
349
350
        self.notify_metadata_changes(interface, 'removed')
351 1
        return jsonify("Operation successful"), 200
352
353 1
    # Link related methods
354
    @rest('v3/links')
355
    def get_links(self):
356
        """Return a json with all the links in the topology.
357
358 1
        Links are connections between interfaces.
359 1
        """
360 1
        return jsonify(self._get_links_dict()), 200
361 1
362
    @rest('v3/links/<link_id>/enable', methods=['POST'])
363 1
    def enable_link(self, link_id):
364
        """Administratively enable a link in the topology."""
365
        try:
366 1
            self.links[link_id].enable()
367
        except KeyError:
368 1
            return jsonify("Link not found"), 404
369
370
        return jsonify("Operation successful"), 201
371
372
    @rest('v3/links/<link_id>/disable', methods=['POST'])
373
    def disable_link(self, link_id):
374 1
        """Administratively disable a link in the topology."""
375 1
        try:
376 1
            self.links[link_id].disable()
377 1
        except KeyError:
378 1
            return jsonify("Link not found"), 404
379 1
380 1
        return jsonify("Operation successful"), 201
381
382 1
    @rest('v3/links/<link_id>/metadata')
383
    def get_link_metadata(self, link_id):
384
        """Get metadata from a link."""
385
        try:
386
            return jsonify({"metadata": self.links[link_id].metadata}), 200
387
        except KeyError:
388 1
            return jsonify("Link not found"), 404
389 1
390 1
    @rest('v3/links/<link_id>/metadata', methods=['POST'])
391 1
    def add_link_metadata(self, link_id):
392 1
        """Add metadata to a link."""
393 1
        metadata = request.get_json()
394
        try:
395 1
            link = self.links[link_id]
396
        except KeyError:
397
            return jsonify("Link not found"), 404
398 1
399 1
        link.extend_metadata(metadata)
400
        self.notify_metadata_changes(link, 'added')
401 1
        return jsonify("Operation successful"), 201
402 1
403 1
    @rest('v3/links/<link_id>/metadata/<key>', methods=['DELETE'])
404
    def delete_link_metadata(self, link_id, key):
405 1
        """Delete metadata from a link."""
406 1
        try:
407
            link = self.links[link_id]
408 1
        except KeyError:
409
            return jsonify("Link not found"), 404
410
411
        if link.remove_metadata(key) is False:
412
            return jsonify("Metadata not found"), 404
413
414
        self.notify_metadata_changes(link, 'removed')
415
        return jsonify("Operation successful"), 200
416
417
    @listen_to('.*.switch.(new|reconnected)')
418
    def handle_new_switch(self, event):
419
        """Create a new Device on the Topology.
420
421
        Handle the event of a new created switch and update the topology with
422
        this new device.
423
        """
424
        switch = event.content['switch']
425
        switch.activate()
426
        log.debug('Switch %s added to the Topology.', switch.id)
427 1
        self.notify_topology_update()
428
        self.update_instance_metadata(switch)
429 1
430 1
    @listen_to('.*.connection.lost')
431
    def handle_connection_lost(self, event):
432 1
        """Remove a Device from the topology.
433
434 1
        Remove the disconnected Device and every link that has one of its
435
        interfaces.
436 1
        """
437 1
        switch = event.content['source'].switch
438 1
        if switch:
439
            switch.deactivate()
440
            log.debug('Switch %s removed from the Topology.', switch.id)
441 1
            self.notify_topology_update()
442 1
443
    def handle_interface_up(self, event):
444 1
        """Update the topology based on a Port Modify event.
445
446 1
        The event notifies that an interface was changed to 'up'.
447 1
        """
448 1
        interface = event.content['interface']
449
        interface.activate()
450
        self.notify_topology_update()
451
        self.update_instance_metadata(interface)
452
453
    @listen_to('.*.switch.interface.created')
454
    def handle_interface_created(self, event):
455
        """Update the topology based on a Port Create event."""
456 1
        self.handle_interface_up(event)
457 1
458
    def handle_interface_down(self, event):
459 1
        """Update the topology based on a Port Modify event.
460 1
461
        The event notifies that an interface was changed to 'down'.
462 1
        """
463
        interface = event.content['interface']
464
        interface.deactivate()
465 1
        self.handle_interface_link_down(event)
466 1
        self.notify_topology_update()
467 1
468
    @listen_to('.*.switch.interface.deleted')
469 1
    def handle_interface_deleted(self, event):
470
        """Update the topology based on a Port Delete event."""
471
        self.handle_interface_down(event)
472
473
    @listen_to('.*.switch.interface.link_up')
474
    def handle_interface_link_up(self, event):
475
        """Update the topology based on a Port Modify event.
476
477
        The event notifies that an interface's link was changed to 'up'.
478
        """
479
        interface = event.content['interface']
480
        self.handle_link_up(interface)
481
482
    @listen_to('kytos/maintenance.end_switch')
483
    def handle_switch_maintenance_end(self, event):
484
        """Handle the end of the maintenance of a switch."""
485
        switches = event.content['switches']
486
        for switch in switches:
487
            switch.enable()
488
            switch.activate()
489
            for interface in switch.interfaces.values():
490
                interface.enable()
491
                self.handle_link_up(interface)
492
493
    def handle_link_up(self, interface):
494
        """Notify a link is up."""
495 1
        link = self._get_link_from_interface(interface)
496
        if not link:
497
            return
498
        if link.endpoint_a == interface:
499
            other_interface = link.endpoint_b
500
        else:
501
            other_interface = link.endpoint_a
502
        interface.activate()
503
        if other_interface.is_active() is False:
504 1
            return
505
        if link.is_active() is False:
506 1
            link.update_metadata('last_status_change', time.time())
507 1
            link.activate()
508
509 1
            # As each run of this method uses a different thread,
510 1
            # there is no risk this sleep will lock the NApp.
511 1
            time.sleep(self.link_up_timer)
512
513 1
            last_status_change = link.get_metadata('last_status_change')
514
            now = time.time()
515
            if link.is_active() and \
516
                    now - last_status_change >= self.link_up_timer:
517
                self.notify_topology_update()
518
                self.update_instance_metadata(link)
519
                self.notify_link_status_change(link)
520
521
    @listen_to('.*.switch.interface.link_down')
522
    def handle_interface_link_down(self, event):
523
        """Update the topology based on a Port Modify event.
524
525
        The event notifies that an interface's link was changed to 'down'.
526
        """
527
        interface = event.content['interface']
528
        self.handle_link_down(interface)
529
530
    @listen_to('kytos/maintenance.start_switch')
531
    def handle_switch_maintenance_start(self, event):
532 1
        """Handle the start of the maintenance of a switch."""
533
        switches = event.content['switches']
534
        for switch in switches:
535
            switch.disable()
536
            switch.deactivate()
537
            for interface in switch.interfaces.values():
538
                interface.disable()
539
                if interface.is_active():
540
                    self.handle_link_down(interface)
541 1
542
    def handle_link_down(self, interface):
543
        """Notify a link is down."""
544
        link = self._get_link_from_interface(interface)
545
        if link and link.is_active():
546
            link.deactivate()
547
            link.update_metadata('last_status_change', time.time())
548
            self.notify_topology_update()
549
            self.notify_link_status_change(link)
550
551
    @listen_to('.*.interface.is.nni')
552
    def add_links(self, event):
553
        """Update the topology with links related to the NNI interfaces."""
554
        interface_a = event.content['interface_a']
555
        interface_b = event.content['interface_b']
556
557
        link = self._get_link_or_create(interface_a, interface_b)
558
        interface_a.update_link(link)
559
        interface_b.update_link(link)
560
561
        interface_a.nni = True
562
        interface_b.nni = True
563
564
        self.notify_topology_update()
565
566
    # def add_host(self, event):
567
    #    """Update the topology with a new Host."""
568
569
    #    interface = event.content['port']
570
    #    mac = event.content['reachable_mac']
571
572
    #    host = Host(mac)
573
    #    link = self.topology.get_link(interface.id)
574
    #    if link is not None:
575
    #        return
576
577
    #    self.topology.add_link(interface.id, host.id)
578
    #    self.topology.add_device(host)
579
580
    #    if settings.DISPLAY_FULL_DUPLEX_LINKS:
581
    #        self.topology.add_link(host.id, interface.id)
582
583
    def save_status_on_storehouse(self):
584
        """Save the network administrative status using storehouse."""
585
        status = self._get_switches_dict()
586
        status['id'] = 0
587
        self.storehouse.save_status(status)
588
589
    def notify_topology_update(self):
590
        """Send an event to notify about updates on the topology."""
591
        name = 'kytos/topology.updated'
592
        event = KytosEvent(name=name, content={'topology':
593
                                               self._get_topology()})
594
        self.controller.buffers.app.put(event)
595
596
    def notify_link_status_change(self, link):
597
        """Send an event to notify about a status change on a link."""
598
        name = 'kytos/topology.'
599
        if link.is_active():
600
            status = 'link_up'
601
        else:
602
            status = 'link_down'
603
        event = KytosEvent(name=name+status, content={'link': link})
604
        self.controller.buffers.app.put(event)
605
606
    def notify_metadata_changes(self, obj, action):
607
        """Send an event to notify about metadata changes."""
608
        if isinstance(obj, Switch):
609
            entity = 'switch'
610
            entities = 'switches'
611
        elif isinstance(obj, Interface):
612
            entity = 'interface'
613
            entities = 'interfaces'
614
        elif isinstance(obj, Link):
615
            entity = 'link'
616
            entities = 'links'
617
618
        name = f'kytos/topology.{entities}.metadata.{action}'
619
        event = KytosEvent(name=name, content={entity: obj,
0 ignored issues
show
introduced by
The variable entity does not seem to be defined for all execution paths.
Loading history...
620
                                               'metadata': obj.metadata})
621
        self.controller.buffers.app.put(event)
622
        log.debug(f'Metadata from {obj.id} was {action}.')
623
624
    @listen_to('.*.switch.port.created')
625
    def notify_port_created(self, original_event):
626
        """Notify when a port is created."""
627
        name = 'kytos/topology.port.created'
628
        event = KytosEvent(name=name, content=original_event.content)
629
        self.controller.buffers.app.put(event)
630
631
    @listen_to('kytos/topology.*.metadata.*')
632
    def save_metadata_on_store(self, event):
633
        """Send to storehouse the data updated."""
634
        name = 'kytos.storehouse.update'
635
        if 'switch' in event.content:
636
            store = self.store_items.get('switches')
637
            obj = event.content.get('switch')
638
            namespace = 'kytos.topology.switches.metadata'
639
        elif 'interface' in event.content:
640
            store = self.store_items.get('interfaces')
641
            obj = event.content.get('interface')
642
            namespace = 'kytos.topology.iterfaces.metadata'
643
        elif 'link' in event.content:
644
            store = self.store_items.get('links')
645
            obj = event.content.get('link')
646
            namespace = 'kytos.topology.links.metadata'
647
648
        store.data[obj.id] = obj.metadata
0 ignored issues
show
introduced by
The variable store does not seem to be defined for all execution paths.
Loading history...
introduced by
The variable obj does not seem to be defined for all execution paths.
Loading history...
649
        content = {'namespace': namespace,
0 ignored issues
show
introduced by
The variable namespace does not seem to be defined for all execution paths.
Loading history...
650
                   'box_id': store.box_id,
651
                   'data': store.data,
652
                   'callback': self.update_instance}
653
654
        event = KytosEvent(name=name, content=content)
655
        self.controller.buffers.app.put(event)
656
657
    @staticmethod
658
    def update_instance(event, _data, error):
659
        """Display in Kytos console if the data was updated."""
660
        entities = event.content.get('namespace', '').split('.')[-2]
661
        if error:
662
            log.error(f'Error trying to update storehouse {entities}.')
663
        else:
664
            log.debug(f'Storehouse update to entities: {entities}.')
665
666
    def verify_storehouse(self, entities):
667
        """Request a list of box saved by specific entity."""
668
        name = 'kytos.storehouse.list'
669
        content = {'namespace': f'kytos.topology.{entities}.metadata',
670
                   'callback': self.request_retrieve_entities}
671
        event = KytosEvent(name=name, content=content)
672
        self.controller.buffers.app.put(event)
673
        log.info(f'verify data in storehouse for {entities}.')
674
675
    def request_retrieve_entities(self, event, data, _error):
676
        """Create a box or retrieve an existent box from storehouse."""
677
        msg = ''
678
        content = {'namespace': event.content.get('namespace'),
679
                   'callback': self.load_from_store,
680
                   'data': {}}
681
682
        if not data:
683
            name = 'kytos.storehouse.create'
684
            msg = 'Create new box in storehouse'
685
        else:
686
            name = 'kytos.storehouse.retrieve'
687
            content['box_id'] = data[0]
688
            msg = 'Retrieve data from storeohouse.'
689
690
        event = KytosEvent(name=name, content=content)
691
        self.controller.buffers.app.put(event)
692
        log.debug(msg)
693
694
    def load_from_store(self, event, box, error):
695
        """Save the data retrived from storehouse."""
696
        entities = event.content.get('namespace', '').split('.')[-2]
697
        if error:
698
            log.error('Error while get a box from storehouse.')
699
        else:
700
            self.store_items[entities] = box
701
            log.debug('Data updated')
702
703
    def update_instance_metadata(self, obj):
704
        """Update object instance with saved metadata."""
705
        metadata = None
706
        if isinstance(obj, Interface):
707
            all_metadata = self.store_items.get('interfaces', None)
708
            if all_metadata:
709
                metadata = all_metadata.data.get(obj.id)
710
        elif isinstance(obj, Switch):
711
            all_metadata = self.store_items.get('switches', None)
712
            if all_metadata:
713
                metadata = all_metadata.data.get(obj.id)
714
        elif isinstance(obj, Link):
715
            all_metadata = self.store_items.get('links', None)
716
            if all_metadata:
717
                metadata = all_metadata.data.get(obj.id)
718
719
        if metadata:
720
            obj.extend_metadata(metadata)
721
            log.debug(f'Metadata to {obj.id} was updated')
722
723
    @listen_to('kytos/maintenance.start_link')
724
    def handle_link_maintenance_start(self, event):
725
        """Deals with the start of links maintenance."""
726
        notify_links = []
727
        maintenance_links = event.content['links']
728
        for maintenance_link in maintenance_links:
729
            try:
730
                link = self.links[maintenance_link.id]
731
            except KeyError:
732
                continue
733
            notify_links.append(link)
734
        for link in notify_links:
735
            link.disable()
736
            link.deactivate()
737
            link.endpoint_a.deactivate()
738
            link.endpoint_b.deactivate()
739
            link.endpoint_a.disable()
740
            link.endpoint_b.disable()
741
            self.notify_link_status_change(link)
742
743
    @listen_to('kytos/maintenance.end_link')
744
    def handle_link_maintenance_end(self, event):
745
        """Deals with the end of links maintenance."""
746
        notify_links = []
747
        maintenance_links = event.content['links']
748
        for maintenance_link in maintenance_links:
749
            try:
750
                link = self.links[maintenance_link.id]
751
            except KeyError:
752
                continue
753
            notify_links.append(link)
754
        for link in notify_links:
755
            link.enable()
756
            link.activate()
757
            link.endpoint_a.activate()
758
            link.endpoint_b.activate()
759
            link.endpoint_a.enable()
760
            link.endpoint_b.enable()
761
            self.notify_link_status_change(link)
762