Passed
Pull Request — master (#53)
by Vinicius
03:08
created

build.main.Main.end_mw()   A

Complexity

Conditions 4

Size

Total Lines 18
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 18
ccs 14
cts 14
cp 1
rs 9.65
c 0
b 0
f 0
cc 4
nop 2
crap 4
1
"""Main module of kytos/maintenance Kytos Network Application.
2
3
This NApp creates maintenance windows, allowing the maintenance of network
4
devices (a switch, a board, a link) without receiving alerts.
5
"""
6 1
import datetime
7
8 1
import pytz
9 1
from flask import jsonify, request
10 1
from napps.kytos.maintenance.models import MaintenanceWindow as MW
11 1
from napps.kytos.maintenance.models import Scheduler, Status
12 1
from werkzeug.exceptions import BadRequest, NotFound, UnsupportedMediaType
13
14 1
from kytos.core import KytosNApp, rest
15
16
17 1
class Main(KytosNApp):
18
    """Main class of kytos/maintenance NApp.
19
20
    This class is the entry point for this napp.
21
    """
22
23 1
    def setup(self):
24
        """Replace the '__init__' method for the KytosNApp subclass.
25
26
        The setup method is automatically called by the controller when your
27
        application is loaded.
28
29
        So, if you have any setup routine, insert it here.
30
        """
31 1
        self.maintenances = {}
32 1
        self.scheduler = Scheduler()
33
34 1
    def execute(self):
35
        """Run after the setup method execution.
36
37
        You can also use this method in loop mode if you add to the above setup
38
        method a line like the following example:
39
40
            self.execute_as_loop(30)  # 30-second interval.
41
        """
42
43 1
    def shutdown(self):
44
        """Run when your napp is unloaded.
45
46
        If you have some cleanup procedure, insert it here.
47
        """
48
49 1
    @rest('/', methods=['GET'])
50 1
    @rest('/<mw_id>', methods=['GET'])
51 1
    def get_mw(self, mw_id=None):
52
        """Return one or all maintenance windows."""
53 1
        if mw_id is None:
54 1
            return jsonify(
55
                [maintenance.as_dict()
56
                 for maintenance in self.maintenances.values()]), 200
57 1
        try:
58 1
            return jsonify(self.maintenances[mw_id].as_dict()), 200
59 1
        except KeyError:
60 1
            raise NotFound(f'Maintenance with id {mw_id} not found')
61
62 1
    @rest('/', methods=['POST'])
63 1
    def create_mw(self):
64
        """Create a new maintenance window."""
65 1
        now = datetime.datetime.now(pytz.utc)
66 1
        data = request.get_json()
67 1
        if not data:
68
            raise UnsupportedMediaType('The request does not have a json.')
69 1
        try:
70 1
            maintenance = MW.from_dict(data, self.controller)
71
        except ValueError as err:
72
            raise BadRequest(f'{err}')
73 1
        if maintenance is None:
74 1
            raise BadRequest('One or more items are invalid')
75 1
        if maintenance.start < now:
76 1
            raise BadRequest('Start in the past not allowed')
77 1
        if maintenance.end <= maintenance.start:
78 1
            raise BadRequest('End before start not allowed')
79 1
        self.scheduler.add(maintenance)
80 1
        self.maintenances[maintenance.id] = maintenance
81 1
        return jsonify({'mw_id': maintenance.id}), 201
82
83 1
    @rest('/<mw_id>', methods=['PATCH'])
84 1
    def update_mw(self, mw_id):
85
        """Update a maintenance window."""
86 1
        data = request.get_json()
87 1
        if not data:
88 1
            raise UnsupportedMediaType('The request does not have a json.')
89 1
        try:
90 1
            maintenance = self.maintenances[mw_id]
91 1
        except KeyError:
92 1
            raise NotFound(f'Maintenance with id {mw_id} not found')
93 1
        if maintenance.status == Status.RUNNING:
94
            raise BadRequest('Updating a running maintenance is not allowed')
95 1
        try:
96 1
            maintenance.update(data)
97 1
        except ValueError as error:
98 1
            raise BadRequest(f'{error}')
99 1
        self.scheduler.remove(maintenance)
100 1
        self.scheduler.add(maintenance)
101 1
        return jsonify({'response': f'Maintenance {mw_id} updated'}), 200
102
103 1
    @rest('/<mw_id>', methods=['DELETE'])
104 1
    def remove_mw(self, mw_id):
105
        """Delete a maintenance window."""
106 1
        try:
107 1
            maintenance = self.maintenances[mw_id]
108 1
        except KeyError:
109 1
            raise NotFound(f'Maintenance with id {mw_id} not found')
110 1
        if maintenance.status == Status.RUNNING:
111 1
            raise BadRequest('Deleting a running maintenance is not allowed')
112 1
        self.scheduler.remove(maintenance)
113 1
        del self.maintenances[mw_id]
114 1
        return jsonify({'response': f'Maintenance with id {mw_id} '
115
                                    f'successfully removed'}), 200
116
117 1
    @rest('/<mw_id>/end', methods=['PATCH'])
118 1
    def end_mw(self, mw_id):
119
        """Finish a maintenance window right now."""
120 1
        try:
121 1
            maintenance = self.maintenances[mw_id]
122 1
        except KeyError:
123 1
            raise NotFound(f'Maintenance with id {mw_id} not found')
124 1
        now = datetime.datetime.now(pytz.utc)
125 1
        if now < maintenance.start:
126 1
            raise BadRequest(f'Maintenance window {mw_id} has not yet '
127
                             'started.')
128 1
        if now > maintenance.end:
129 1
            raise BadRequest(f'Maintenance window {mw_id} has already '
130
                             'finished.')
131 1
        self.scheduler.remove(maintenance)
132 1
        maintenance.end_mw()
133 1
        return jsonify({'response': f'Maintenance window {mw_id} '
134
                                    f'finished.'}), 200
135
136 1
    @rest('/<mw_id>/extend', methods=['PATCH'])
137 1
    def extend_mw(self, mw_id):
138
        """Extend a running maintenance window."""
139 1
        data = request.get_json()
140 1
        if not data:
141 1
            raise UnsupportedMediaType('The request does not have a json')
142 1
        try:
143 1
            maintenance = self.maintenances[mw_id]
144 1
        except KeyError:
145 1
            raise NotFound(f'Maintenance with id {mw_id} not found')
146 1
        if 'minutes' not in data:
147 1
            raise BadRequest('Minutes of extension must be sent')
148 1
        now = datetime.datetime.now(pytz.utc)
149 1
        if now < maintenance.start:
150 1
            raise BadRequest(f'Maintenance window {mw_id} has not yet '
151
                             'started')
152 1
        if now > maintenance.end:
153 1
            raise BadRequest(f'Maintenance window {mw_id} has already '
154
                             'finished')
155 1
        try:
156 1
            maintenance.end = maintenance.end + \
157
                datetime.timedelta(minutes=data['minutes'])
158 1
        except TypeError:
159 1
            raise BadRequest('Minutes of extension must be integer')
160 1
        self.scheduler.remove(maintenance)
161 1
        self.scheduler.add(maintenance)
162
        return jsonify({'response': f'Maintenance {mw_id} extended'}), 200
163