Passed
Pull Request — master (#96)
by
unknown
01:59
created

build.main   F

Complexity

Total Complexity 111

Size/Duplication

Total Lines 636
Duplicated Lines 5.66 %

Test Coverage

Coverage 1.84%

Importance

Changes 0
Metric Value
eloc 425
dl 36
loc 636
ccs 7
cts 381
cp 0.0184
rs 2
c 0
b 0
f 0
wmc 111

51 Methods

Rating   Name   Duplication   Size   Complexity  
A Main.enable_switch() 0 9 2
A Main.save_status_on_storehouse() 0 4 1
A Main.notify_port_created() 0 6 1
A Main.disable_switch() 0 9 2
A Main.verify_storehouse() 0 8 1
A Main.load_from_store() 0 8 2
A Main.request_retrieve_entities() 0 18 2
A Main.disable_link() 0 9 2
A Main.enable_interface() 18 18 3
B Main._restore_status() 0 29 7
A Main.add_switch_metadata() 0 12 2
A Main.get_interfaces() 0 10 3
A Main._get_switches_dict() 0 4 1
A Main.delete_interface_metadata() 0 21 4
A Main.add_interface_metadata() 0 20 3
B Main.update_instance_metadata() 0 19 8
A Main.get_switches() 0 4 1
A Main.notify_link_status_change() 0 9 2
A Main.handle_interface_down() 0 9 1
A Main.handle_interface_link_up() 0 13 3
A Main.handle_interface_created() 0 4 1
A Main.get_interface_metadata() 0 16 3
A Main.delete_link_metadata() 0 13 3
A Main.shutdown() 0 3 1
A Main.notify_topology_update() 0 6 1
A Main.enable_link() 0 9 2
A Main.setup() 0 10 1
A Main.handle_new_switch() 0 12 1
A Main.execute() 0 2 1
A Main.get_switch_metadata() 0 8 2
A Main.update_instance() 0 8 2
A Main.handle_connection_lost() 0 12 2
A Main.get_links() 0 7 1
A Main.disable_interface() 18 18 3
A Main._load_network_status() 0 22 4
A Main.get_topology() 0 7 1
A Main.save_metadata_on_store() 0 25 4
A Main.handle_interface_link_down() 0 12 3
A Main._get_links_dict() 0 4 1
A Main.add_links() 0 14 1
A Main.handle_interface_up() 0 9 1
A Main.handle_interface_deleted() 0 4 1
A Main._get_link_or_create() 0 9 3
A Main.restore_network_status() 0 10 2
A Main._get_link_from_interface() 0 6 3
A Main.add_link_metadata() 0 12 2
A Main.notify_metadata_changes() 0 17 4
A Main.delete_switch_metadata() 0 11 2
A Main._get_topology_dict() 0 4 1
A Main.get_link_metadata() 0 7 2
A Main._get_topology() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build.main often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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