Test Failed
Push — master ( 40cb77...09ad2e )
by Antonio
01:49 queued 12s
created

build.main   F

Complexity

Total Complexity 102

Size/Duplication

Total Lines 676
Duplicated Lines 0 %

Test Coverage

Coverage 42.18%

Importance

Changes 0
Metric Value
eloc 401
dl 0
loc 676
ccs 62
cts 147
cp 0.4218
rs 2
c 0
b 0
f 0
wmc 102

25 Methods

Rating   Name   Duplication   Size   Complexity  
A Main.list_circuits() 0 17 3
C Main._evc_dict_with_instances() 0 40 9
B Main.load_evcs() 0 24 6
A Main.update_schedule() 0 50 3
A Main._find_evc_by_schedule_id() 0 21 5
A Main._evc_from_dict() 0 3 1
A Main.handle_link_up() 0 7 4
A Main.load_circuits_by_interface() 0 13 4
A Main.delete_schedule() 0 35 3
A Main.get_circuit() 0 16 2
A Main.add_to_dict_of_sets() 0 5 2
A Main.shutdown() 0 2 1
A Main.setup() 0 25 1
A Main.handle_link_down() 0 8 3
A Main.execute() 0 5 4
B Main.create_circuit() 0 72 6
B Main.list_schedules() 0 29 5
A Main._link_from_dict() 0 20 4
A Main._uni_from_dict() 0 19 5
A Main._is_duplicated_evc() 0 14 4
A Main.json_from_request() 0 23 4
C Main.update() 0 52 10
B Main.create_schedule() 0 76 7
A Main._get_circuits_buffer() 0 13 3
A Main.delete_circuit() 0 33 3

How to fix   Complexity   

Complexity

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/mef_eline Kytos Network Application.
2
3
NApp to provision circuits from user request.
4
"""
5 1
from flask import jsonify, request
6
from werkzeug.exceptions import (BadRequest, Conflict, Forbidden,
7 1
                                 MethodNotAllowed, NotFound,
8 1
                                 UnsupportedMediaType)
9 1
10 1
from kytos.core import KytosNApp, log, rest
11 1
from kytos.core.events import KytosEvent
12 1
from kytos.core.helpers import listen_to
13 1
from kytos.core.interface import TAG, UNI
14 1
from kytos.core.link import Link
15
from napps.kytos.mef_eline import settings
16
from napps.kytos.mef_eline.models import EVC, DynamicPathManager, Path
17 1
from napps.kytos.mef_eline.scheduler import CircuitSchedule, Scheduler
18
from napps.kytos.mef_eline.storehouse import StoreHouse
19
20
21
class Main(KytosNApp):
22
    """Main class of amlight/mef_eline NApp.
23 1
24
    This class is the entry point for this napp.
25
    """
26
27
    def setup(self):
28
        """Replace the '__init__' method for the KytosNApp subclass.
29
30
        The setup method is automatically called by the controller when your
31
        application is loaded.
32 1
33
        So, if you have any setup routine, insert it here.
34
        """
35 1
        # object used to scheduler circuit events
36
        self.sched = Scheduler()
37
38 1
        # object to save and load circuits
39
        self.storehouse = StoreHouse(self.controller)
40 1
41
        # set the controller that will manager the dynamic paths
42
        DynamicPathManager.set_controller(self.controller)
43 1
44
        # dictionary of EVCs created. It acts as a circuit buffer.
45
        # Every create/update/delete must be synced to storehouse.
46
        self.circuits = {}
47
48
        # dictionary of EVCs by interface
49 1
        self._circuits_by_interface = {}
50
51
        self.execute_as_loop(settings.DEPLOY_EVCS_INTERVAL)
52 1
53 1
    def execute(self):
54 1
        """Execute once when the napp is running."""
55
        for circuit in self.circuits.values():
56 1
            if circuit.is_enabled() and not circuit.is_active():
57
                circuit.deploy()
58 1
59
    def shutdown(self):
60
        """Execute when your napp is unloaded.
61 1
62
        If you have some cleanup procedure, insert it here.
63 1
        """
64 1
65 1
    @rest('/v2/evc/', methods=['GET'])
66
    def list_circuits(self):
67 1
        """Endpoint to return circuits stored.
68 1
69
        If archived is set to True return all circuits, else only the ones
70 1
        not archived.
71
        """
72 1
        log.debug('list_circuits /v2/evc')
73
        archived = request.args.get('archived', False)
74
        circuits = self.storehouse.get_data()
75
        if not circuits:
76
            return jsonify({}), 200
77
        if archived:
78
            return jsonify(circuits), 200
79
        return jsonify({circuit_id: circuit
80
                        for circuit_id, circuit in circuits.items()
81
                        if not circuit.get('archived', False)}), 200
82
83
    @rest('/v2/evc/<circuit_id>', methods=['GET'])
84
    def get_circuit(self, circuit_id):
85
        """Endpoint to return a circuit based on id."""
86
        log.debug('get_circuit /v2/evc/%s', circuit_id)
87
        circuits = self.storehouse.get_data()
88
89
        try:
90
            result = circuits[circuit_id]
91
        except KeyError:
92
            result = f'circuit_id {circuit_id} not found'
93
            log.debug('get_circuit result %s %s', result, 404)
94
            raise NotFound(result)
95
96
        status = 200
97
        log.debug('get_circuit result %s %s', result, status)
98 1
        return jsonify(result), status
99
100 1
    @rest('/v2/evc/', methods=['POST'])
101 1
    def create_circuit(self):
102
        """Try to create a new circuit.
103 1
104 1
        Firstly, for EVPL: E-Line NApp verifies if UNI_A's requested C-VID and
105
        UNI_Z's requested C-VID are available from the interfaces' pools. This
106
        is checked when creating the UNI object.
107
108
        Then, E-Line NApp requests a primary and a backup path to the
109 1
        Pathfinder NApp using the attributes primary_links and backup_links
110
        submitted via REST
111
112
        # For each link composing paths in #3:
113 1
        #  - E-Line NApp requests a S-VID available from the link VLAN pool.
114
        #  - Using the S-VID obtained, generate abstract flow entries to be
115
        #    sent to FlowManager
116 1
117
        Push abstract flow entries to FlowManager and FlowManager pushes
118
        OpenFlow entries to datapaths
119 1
120 1
        E-Line NApp generates an event to notify all Kytos NApps of a new EVC
121
        creation
122
123 1
        Finnaly, notify user of the status of its request.
124
        """
125 1
        # Try to create the circuit object
126
        log.debug('create_circuit /v2/evc/')
127 1
        try:
128
            data = request.get_json()
129 1
        except BadRequest:
130
            result = 'The request body is not a well-formed JSON.'
131
            log.debug('create_circuit result %s %s', result, 400)
132
            raise BadRequest(result)
133
134
        if data is None:
135
            result = 'The request body mimetype is not application/json.'
136
            log.debug('create_circuit result %s %s', result, 415)
137
            raise UnsupportedMediaType(result)
138
        try:
139
            evc = self._evc_from_dict(data)
140
        except ValueError as exception:
141
            log.debug('create_circuit result %s %s', exception, 400)
142
            raise BadRequest(str(exception))
143
144
        # verify duplicated evc
145
        if self._is_duplicated_evc(evc):
146
            result = "The EVC already exists."
147
            log.debug('create_circuit result %s %s', result, 409)
148
            raise Conflict(result)
149
150
        # store circuit in dictionary
151
        self.circuits[evc.id] = evc
152
153
        # save circuit
154 1
        self.storehouse.save_evc(evc)
155
156
        # Schedule the circuit deploy
157
        self.sched.add(evc)
158
159
        # Circuit has no schedule, deploy now
160
        if not evc.circuit_scheduler:
161
            evc.deploy()
162
163
        # Notify users
164
        event = KytosEvent(name='kytos.mef_eline.created',
165
                           content=evc.as_dict())
166
        self.controller.buffers.app.put(event)
167
168
        result = {"circuit_id": evc.id}
169
        status = 201
170 1
        log.debug('create_circuit result %s %s', result, status)
171
        return jsonify(result), status
172
173
    @rest('/v2/evc/<circuit_id>', methods=['PATCH'])
174
    def update(self, circuit_id):
175
        """Update a circuit based on payload.
176
177
        The EVC required attributes (name, uni_a, uni_z) can't be updated.
178
        """
179
        log.debug('update /v2/evc/%s', circuit_id)
180 1
        try:
181
            evc = self.circuits[circuit_id]
182
        except KeyError:
183
            result = f'circuit_id {circuit_id} not found'
184
            log.debug('update result %s %s', result, 404)
185
            raise NotFound(result)
186
187
        if evc.archived:
188
            result = "Can't update archived EVC"
189 1
            log.debug('update result %s %s', result, 405)
190
            raise MethodNotAllowed(['GET'], result)
191 1
192
        try:
193
            data = request.get_json()
194
        except BadRequest:
195
            result = 'The request body is not a well-formed JSON.'
196
            log.debug('update result %s %s', result, 400)
197
            raise BadRequest(result)
198
        if data is None:
199
            result = 'The request body mimetype is not application/json.'
200
            log.debug('update result %s %s', result, 415)
201
            raise UnsupportedMediaType(result)
202
203
        try:
204
            enable, path = \
205 1
                evc.update(**self._evc_dict_with_instances(data))
206
        except ValueError as exception:
207
            log.error(exception)
208
            log.debug('update result %s %s', exception, 400)
209
            raise BadRequest(str(exception))
210
211
        if evc.is_active():
212
            if enable is False:  # disable if active
213
                evc.remove()
214
            elif path is not None:  # redeploy if active
215
                evc.remove()
216
                evc.deploy()
217
        else:
218
            if enable is True:  # enable if inactive
219
                evc.deploy()
220
        result = {evc.id: evc.as_dict()}
221 1
        status = 200
222
223
        log.debug('update result %s %s', result, status)
224
        return jsonify(result), status
225
226 1
    @rest('/v2/evc/<circuit_id>', methods=['DELETE'])
227
    def delete_circuit(self, circuit_id):
228 1
        """Remove a circuit.
229
230 1
        First, the flows are removed from the switches, and then the EVC is
231 1
        disabled.
232 1
        """
233
        log.debug('delete_circuit /v2/evc/%s', circuit_id)
234
        try:
235
            evc = self.circuits[circuit_id]
236 1
        except KeyError:
237
            result = f'circuit_id {circuit_id} not found'
238
            log.debug('delete_circuit result %s %s', result, 404)
239
            raise NotFound(result)
240
241 1
        if evc.archived:
242
            result = f'Circuit {circuit_id} already removed'
243
            log.debug('delete_circuit result %s %s', result, 404)
244
            raise NotFound(result)
245 1
246
        log.info('Removing %s', evc)
247
        evc.remove_current_flows()
248
        evc.deactivate()
249
        evc.disable()
250 1
        self.sched.remove(evc)
251
        evc.archive()
252 1
        evc.sync()
253
        log.info('EVC removed. %s', evc)
254
        result = {'response': f'Circuit {circuit_id} removed'}
255
        status = 200
256
257
        log.debug('delete_circuit result %s %s', result, status)
258
        return jsonify(result), status
259
260
    @rest('/v2/evc/schedule', methods=['GET'])
261
    def list_schedules(self):
262
        """Endpoint to return all schedules stored for all circuits.
263
264
        Return a JSON with the following template:
265
        [{"schedule_id": <schedule_id>,
266
         "circuit_id": <circuit_id>,
267
         "schedule": <schedule object>}]
268
        """
269
        log.debug('list_schedules /v2/evc/schedule')
270
        circuits = self.storehouse.get_data().values()
271 1
        if not circuits:
272
            result = {}
273
            status = 200
274
            return jsonify(result), status
275
276
        result = []
277
        status = 200
278
        for circuit in circuits:
279
            circuit_scheduler = circuit.get("circuit_scheduler")
280
            if circuit_scheduler:
281
                for scheduler in circuit_scheduler:
282
                    value = {"schedule_id": scheduler.get("id"),
283
                             "circuit_id": circuit.get("id"),
284
                             "schedule": scheduler}
285
                    result.append(value)
286
287
        log.debug('list_schedules result %s %s', result, status)
288
        return jsonify(result), status
289
290
    @rest('/v2/evc/schedule/', methods=['POST'])
291
    def create_schedule(self):
292
        """
293
        Create a new schedule for a given circuit.
294
295
        This service do no check if there are conflicts with another schedule.
296
        Payload example:
297
            {
298
              "circuit_id":"aa:bb:cc",
299
              "schedule": {
300
                "date": "2019-08-07T14:52:10.967Z",
301
                "interval": "string",
302
                "frequency": "1 * * * *",
303
                "action": "create"
304
              }
305
            }
306
        """
307
        log.debug('create_schedule /v2/evc/schedule/')
308
309
        json_data = self.json_from_request('create_schedule')
310
        try:
311
            circuit_id = json_data['circuit_id']
312
        except TypeError:
313
            result = 'The payload should have a dictionary.'
314
            log.debug('create_schedule result %s %s', result, 400)
315
            raise BadRequest(result)
316
        except KeyError:
317
            result = 'Missing circuit_id.'
318
            log.debug('create_schedule result %s %s', result, 400)
319
            raise BadRequest(result)
320
321
        try:
322
            schedule_data = json_data['schedule']
323
        except KeyError:
324
            result = 'Missing schedule data.'
325
            log.debug('create_schedule result %s %s', result, 400)
326
            raise BadRequest(result)
327
328
        # Get EVC from circuits buffer
329
        circuits = self._get_circuits_buffer()
330
331
        # get the circuit
332
        evc = circuits.get(circuit_id)
333
334
        # get the circuit
335
        if not evc:
336
            result = f'circuit_id {circuit_id} not found'
337
            log.debug('create_schedule result %s %s', result, 404)
338
            raise NotFound(result)
339
        # Can not modify circuits deleted and archived
340
        if evc.archived:
341
            result = f'Circuit {circuit_id} is archived. Update is forbidden.'
342
            log.debug('create_schedule result %s %s', result, 403)
343
            raise Forbidden(result)
344
345
        # new schedule from dict
346
        new_schedule = CircuitSchedule.from_dict(schedule_data)
347
348
        # If there is no schedule, create the list
349
        if not evc.circuit_scheduler:
350
            evc.circuit_scheduler = []
351
352
        # Add the new schedule
353
        evc.circuit_scheduler.append(new_schedule)
354
355
        # Add schedule job
356
        self.sched.add_circuit_job(evc, new_schedule)
357
358
        # save circuit to storehouse
359
        evc.sync()
360
361
        result = new_schedule.as_dict()
362
        status = 201
363
364
        log.debug('create_schedule result %s %s', result, status)
365
        return jsonify(result), status
366
367
    @rest('/v2/evc/schedule/<schedule_id>', methods=['PATCH'])
368
    def update_schedule(self, schedule_id):
369
        """Update a schedule.
370
371
        Change all attributes from the given schedule from a EVC circuit.
372
        The schedule ID is preserved as default.
373
        Payload example:
374
            {
375
              "date": "2019-08-07T14:52:10.967Z",
376
              "interval": "string",
377
              "frequency": "1 * * *",
378
              "action": "create"
379
            }
380
        """
381
        log.debug('update_schedule /v2/evc/schedule/%s', schedule_id)
382
383
        # Try to find a circuit schedule
384
        evc, found_schedule = self._find_evc_by_schedule_id(schedule_id)
385
386
        # Can not modify circuits deleted and archived
387
        if not found_schedule:
388
            result = f'schedule_id {schedule_id} not found'
389
            log.debug('update_schedule result %s %s', result, 404)
390
            raise NotFound(result)
391
        if evc.archived:
392
            result = f'Circuit {evc.id} is archived. Update is forbidden.'
393
            log.debug('update_schedule result %s %s', result, 403)
394
            raise Forbidden(result)
395
396
        data = self.json_from_request('update_schedule')
397
398
        new_schedule = CircuitSchedule.from_dict(data)
399
        new_schedule.id = found_schedule.id
400
        # Remove the old schedule
401
        evc.circuit_scheduler.remove(found_schedule)
402
        # Append the modified schedule
403
        evc.circuit_scheduler.append(new_schedule)
404
405
        # Cancel all schedule jobs
406
        self.sched.cancel_job(found_schedule.id)
407
        # Add the new circuit schedule
408
        self.sched.add_circuit_job(evc, new_schedule)
409
        # Save EVC to the storehouse
410
        evc.sync()
411
412
        result = new_schedule.as_dict()
413
        status = 200
414
415
        log.debug('update_schedule result %s %s', result, status)
416
        return jsonify(result), status
417
418
    @rest('/v2/evc/schedule/<schedule_id>', methods=['DELETE'])
419
    def delete_schedule(self, schedule_id):
420
        """Remove a circuit schedule.
421
422
        Remove the Schedule from EVC.
423
        Remove the Schedule from cron job.
424
        Save the EVC to the Storehouse.
425
        """
426
        log.debug('delete_schedule /v2/evc/schedule/%s', schedule_id)
427
        evc, found_schedule = self._find_evc_by_schedule_id(schedule_id)
428
429
        # Can not modify circuits deleted and archived
430
        if not found_schedule:
431
            result = f'schedule_id {schedule_id} not found'
432
            log.debug('delete_schedule result %s %s', result, 404)
433
            raise NotFound(result)
434
435
        if evc.archived:
436
            result = f'Circuit {evc.id} is archived. Update is forbidden.'
437
            log.debug('delete_schedule result %s %s', result, 403)
438
            raise Forbidden(result)
439
440
        # Remove the old schedule
441
        evc.circuit_scheduler.remove(found_schedule)
442
443
        # Cancel all schedule jobs
444
        self.sched.cancel_job(found_schedule.id)
445
        # Save EVC to the storehouse
446
        evc.sync()
447
448
        result = "Schedule removed"
449
        status = 200
450
451
        log.debug('delete_schedule result %s %s', result, status)
452
        return jsonify(result), status
453
454
    def _is_duplicated_evc(self, evc):
455
        """Verify if the circuit given is duplicated with the stored evcs.
456
457
        Args:
458
            evc (EVC): circuit to be analysed.
459
460
        Returns:
461
            boolean: True if the circuit is duplicated, otherwise False.
462
463
        """
464
        for circuit in self.circuits.values():
465
            if not circuit.archived and circuit == evc:
466
                return True
467
        return False
468
469
    @listen_to('kytos/topology.link_up')
470
    def handle_link_up(self, event):
471
        """Change circuit when link is up or end_maintenance."""
472
        log.debug("Event handle_link_up %s", event)
473
        for evc in self.circuits.values():
474
            if evc.is_enabled() and not evc.archived:
475
                evc.handle_link_up(event.content['link'])
476
477
    @listen_to('kytos/topology.link_down')
478
    def handle_link_down(self, event):
479
        """Change circuit when link is down or under_mantenance."""
480
        log.debug("Event handle_link_down %s", event)
481
        for evc in self.circuits.values():
482
            if evc.is_affected_by_link(event.content['link']):
483
                log.info('handling evc %s' % evc)
484
                evc.handle_link_down()
485
486
    def load_circuits_by_interface(self, circuits):
487
        """Load circuits in storehouse for in-memory dictionary."""
488
        for circuit_id, circuit in circuits.items():
489
            intf_a = circuit['uni_a']['interface_id']
490
            self.add_to_dict_of_sets(intf_a, circuit_id)
491
            intf_z = circuit['uni_z']['interface_id']
492
            self.add_to_dict_of_sets(intf_z, circuit_id)
493
            for path in ('current_path', 'primary_path', 'backup_path'):
494
                for link in circuit[path]:
495
                    intf_a = link['endpoint_a']['id']
496
                    self.add_to_dict_of_sets(intf_a, circuit_id)
497
                    intf_b = link['endpoint_b']['id']
498
                    self.add_to_dict_of_sets(intf_b, circuit_id)
499
500
    def add_to_dict_of_sets(self, intf, circuit_id):
501
        """Add a single item to the dictionary of circuits by interface."""
502
        if intf not in self._circuits_by_interface:
503
            self._circuits_by_interface[intf] = set()
504
        self._circuits_by_interface[intf].add(circuit_id)
505
506
    @listen_to('kytos/topology.port.created')
507
    def load_evcs(self, event):
508
        """Try to load the unloaded EVCs from storehouse."""
509
        log.debug("Event load_evcs %s", event)
510
        circuits = self.storehouse.get_data()
511
        if not self._circuits_by_interface:
512
            self.load_circuits_by_interface(circuits)
513
514
        interface_id = '{}:{}'.format(event.content['switch'],
515
                                      event.content['port'])
516
517
        for circuit_id in self._circuits_by_interface.get(interface_id, []):
518
            if circuit_id in circuits and circuit_id not in self.circuits:
519
                try:
520
                    evc = self._evc_from_dict(circuits[circuit_id])
521
                except ValueError as exception:
522
                    log.info(
523
                        f'Could not load EVC {circuit_id} because {exception}')
524
                    continue
525
                evc.deactivate()
526
                evc.current_path = Path([])
527
                evc.sync()
528
                self.circuits.setdefault(circuit_id, evc)
529
                self.sched.add(evc)
530
531
    def _evc_dict_with_instances(self, evc_dict):
532
        """Convert some dict values to instance of EVC classes.
533
534
        This method will convert: [UNI, Link]
535
        """
536
        data = evc_dict.copy()  # Do not modify the original dict
537
538
        for attribute, value in data.items():
539
            # Get multiple attributes.
540
            # Ex: uni_a, uni_z
541
            if 'uni' in attribute:
542
                try:
543
                    data[attribute] = self._uni_from_dict(value)
544
                except ValueError as exc:
545
                    raise ValueError(f'Error creating UNI: {exc}')
546
547
            if attribute == 'circuit_scheduler':
548
                data[attribute] = []
549
                for schedule in value:
550
                    data[attribute].append(CircuitSchedule.from_dict(schedule))
551
552
            # Get multiple attributes.
553
            # Ex: primary_links,
554
            #     backup_links,
555
            #     current_links_cache,
556
            #     primary_links_cache,
557
            #     backup_links_cache
558
            if 'links' in attribute:
559
                data[attribute] = [self._link_from_dict(link)
560
                                   for link in value]
561
562
            # Get multiple attributes.
563
            # Ex: current_path,
564
            #     primary_path,
565
            #     backup_path
566
            if 'path' in attribute and attribute != 'dynamic_backup_path':
567
                data[attribute] = Path([self._link_from_dict(link)
568
                                        for link in value])
569
570
        return data
571
572
    def _evc_from_dict(self, evc_dict):
573
        data = self._evc_dict_with_instances(evc_dict)
574
        return EVC(self.controller, **data)
575
576
    def _uni_from_dict(self, uni_dict):
577
        """Return a UNI object from python dict."""
578
        if uni_dict is None:
579
            return False
580
581
        interface_id = uni_dict.get("interface_id")
582
        interface = self.controller.get_interface_by_id(interface_id)
583
        if interface is None:
584
            raise ValueError(f'Could not instantiate interface {interface_id}')
585
586
        try:
587
            tag_dict = uni_dict["tag"]
588
        except KeyError:
589
            tag = None
590
        else:
591
            tag = TAG.from_dict(tag_dict)
592
        uni = UNI(interface, tag)
593
594
        return uni
595
596
    def _link_from_dict(self, link_dict):
597
        """Return a Link object from python dict."""
598
        id_a = link_dict.get('endpoint_a').get('id')
599
        id_b = link_dict.get('endpoint_b').get('id')
600
601
        endpoint_a = self.controller.get_interface_by_id(id_a)
602
        endpoint_b = self.controller.get_interface_by_id(id_b)
603
604
        link = Link(endpoint_a, endpoint_b)
605
        if 'metadata' in link_dict:
606
            link.extend_metadata(link_dict.get('metadata'))
607
608
        s_vlan = link.get_metadata('s_vlan')
609
        if s_vlan:
610
            tag = TAG.from_dict(s_vlan)
611
            if tag is False:
612
                error_msg = f'Could not instantiate tag from dict {s_vlan}'
613
                raise ValueError(error_msg)
614
            link.update_metadata('s_vlan', tag)
615
        return link
616
617
    def _find_evc_by_schedule_id(self, schedule_id):
618
        """
619
        Find an EVC and CircuitSchedule based on schedule_id.
620
621
        :param schedule_id: Schedule ID
622
        :return: EVC and Schedule
623
        """
624
        circuits = self._get_circuits_buffer()
625
        found_schedule = None
626
        evc = None
627
628
        # pylint: disable=unused-variable
629
        for c_id, circuit in circuits.items():
630
            for schedule in circuit.circuit_scheduler:
631
                if schedule.id == schedule_id:
632
                    found_schedule = schedule
633
                    evc = circuit
634
                    break
635
            if found_schedule:
636
                break
637
        return evc, found_schedule
638
639
    def _get_circuits_buffer(self):
640
        """
641
        Return the circuit buffer.
642
643
        If the buffer is empty, try to load data from storehouse.
644
        """
645
        if not self.circuits:
646
            # Load storehouse circuits to buffer
647
            circuits = self.storehouse.get_data()
648
            for c_id, circuit in circuits.items():
649
                evc = self._evc_from_dict(circuit)
650
                self.circuits[c_id] = evc
651
        return self.circuits
652
653
    @staticmethod
654
    def json_from_request(caller):
655
        """Return a json from request.
656
657
        If it was not possible to get a json from the request, log, for debug,
658
        who was the caller and the error that ocurred, and raise an
659
        Exception.
660
        """
661
        try:
662
            json_data = request.get_json()
663
        except ValueError as exception:
664
            log.error(exception)
665
            log.debug(f'{caller} result {exception} 400')
666
            raise BadRequest(str(exception))
667
        except BadRequest:
668
            result = 'The request is not a valid JSON.'
669
            log.debug(f'{caller} result {result} 400')
670
            raise BadRequest(result)
671
        if json_data is None:
672
            result = 'Content-Type must be application/json'
673
            log.debug(f'{caller} result {result} 415')
674
            raise UnsupportedMediaType(result)
675
        return json_data
676