Completed
Pull Request — master (#120)
by
unknown
02:17
created

build.main.Main.save_metadata_on_store()   A

Complexity

Conditions 4

Size

Total Lines 25
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 4

Importance

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