Passed
Pull Request — master (#74)
by Antonio
03:05
created

build.main.Main._send_flow_mods_from_request()   A

Complexity

Conditions 5

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 20
rs 9.2333
c 0
b 0
f 0
ccs 0
cts 13
cp 0
cc 5
nop 3
crap 30
1
"""kytos/flow_manager NApp installs, lists and deletes switch flows."""
2
from flask import jsonify, request
3
from kytos.core import KytosEvent, KytosNApp, log, rest
4
from kytos.core.helpers import listen_to
5
6
from napps.kytos.of_core.v0x01.flow import Flow as Flow10
7
from napps.kytos.of_core.v0x04.flow import Flow as Flow13
8
9
10
class Main(KytosNApp):
11
    """Main class to be used by Kytos controller."""
12
13
    def setup(self):
14
        """Replace the 'init' method for the KytosApp subclass.
15
16
        The setup method is automatically called by the run method.
17
        Users shouldn't call this method directly.
18
        """
19
        log.debug("flow-manager starting")
20
        self.flow_mods_sent = {}
21
22
    def execute(self):
23
        """Run once on NApp 'start' or in a loop.
24
25
        The execute method is called by the run method of KytosNApp class.
26
        Users shouldn't call this method directly.
27
        """
28
        pass
29
30
    def shutdown(self):
31
        """Shutdown routine of the NApp."""
32
        log.debug("flow-manager stopping")
33
34
    @rest('v2/flows')
35
    @rest('v2/flows/<dpid>')
36
    def list(self, dpid=None):
37
        """Retrieve all flows from a switch identified by dpid.
38
39
        If no dpid is specified, return all flows from all switches.
40
        """
41
        if dpid is None:
42
            switches = self.controller.switches.values()
43
        else:
44
            switches = [self.controller.get_switch_by_dpid(dpid)]
45
46
        switch_flows = {}
47
48
        for switch in switches:
49
            flows_dict = [flow.as_dict() for flow in switch.flows]
50
            switch_flows[switch.dpid] = {'flows': flows_dict}
51
52
        return jsonify(switch_flows)
53
54
    @rest('v2/flows', methods=['POST'])
55
    @rest('v2/flows/<dpid>', methods=['POST'])
56
    def add(self, dpid=None):
57
        """Install new flows in the switch identified by dpid.
58
59
        If no dpid is specified, install flows in all switches.
60
        """
61
        return self._send_flow_mods_from_request(dpid, "add")
62
63
    @rest('v2/delete', methods=['POST'])
64
    @rest('v2/delete/<dpid>', methods=['POST'])
65
    def delete(self, dpid=None):
66
        """Delete existing flows in the switch identified by dpid.
67
68
        If no dpid is specified, delete flows from all switches.
69
        """
70
        return self._send_flow_mods_from_request(dpid, "delete")
71
72
    def _get_all_switches_enabled(self):
73
        """Get a list of all switches enabled."""
74
        switches = self.controller.switches.values()
75
        return [switch for switch in switches if switch.is_enabled()]
76
77
    def _send_flow_mods_from_request(self, dpid, command):
78
        """Install FlowsMods from request."""
79
        flows_dict = request.get_json()
80
81
        if flows_dict is None:
82
            return jsonify({"response": 'flows dict is none.'}), 404
83
84
        if dpid:
85
            switch = self.controller.get_switch_by_dpid(dpid)
86
            if not switch:
87
                return jsonify({"response": 'dpid not found.'}), 404
88
            elif switch.is_enabled() is False:
89
                return jsonify({"response": 'switch is disabled.'}), 404
90
            else:
91
                self._install_flows(command, flows_dict, [switch])
92
        else:
93
            self._install_flows(command, flows_dict,
94
                                self._get_all_switches_enabled())
95
96
        return jsonify({"response": "FlowMod Messages Sent"})
97
98
    def _install_flows(self, command, flows_dict, switches=[]):
99
        """Execute all procedures to install flows in the switches.
100
101
        Args:
102
            command: Flow command to be installed
103
            flows_dict: Dictionary with flows to be installed in the switches.
104
            switches: A list of switches
105
        """
106
        for switch in switches:
107
            serializer = self._get_flow_serializer(switch)
108
            flows = flows_dict.get('flows', [])
109
            for flow_dict in flows:
110
                flow = serializer.from_dict(flow_dict, switch)
111
                if command == "delete":
112
                    flow_mod = flow.as_of_delete_flow_mod()
113
                elif command == "add":
114
                    flow_mod = flow.as_of_add_flow_mod()
115
                self._send_flow_mod(flow.switch, flow_mod)
0 ignored issues
show
introduced by
The variable flow_mod does not seem to be defined for all execution paths.
Loading history...
116
                self.flow_mods_sent[flow_mod.header.xid] = flow
117
118
                self._send_napp_event(switch, flow, command)
119
120
    def _send_flow_mod(self, switch, flow_mod):
121
        event_name = 'kytos/flow_manager.messages.out.ofpt_flow_mod'
122
123
        content = {'destination': switch.connection,
124
                   'message': flow_mod}
125
126
        event = KytosEvent(name=event_name, content=content)
127
        self.controller.buffers.msg_out.put(event)
128
129
    def _send_napp_event(self, switch, flow, command):
130
        """Send an Event to other apps informing about a FlowMod."""
131
        if command == 'add':
132
            name = 'kytos/flow_manager.flow.added'
133
        elif command == 'delete':
134
            name = 'kytos/flow_manager.flow.removed'
135
        elif command == 'error':
136
            name = 'kytos/flow_manager.flow.error'
137
        content = {'datapath': switch,
138
                   'flow': flow}
139
        event_app = KytosEvent(name, content)
0 ignored issues
show
introduced by
The variable name does not seem to be defined for all execution paths.
Loading history...
140
        self.controller.buffers.app.put(event_app)
141
142
    @staticmethod
143
    def _get_flow_serializer(switch):
144
        """Return the serializer with for the switch OF protocol version."""
145
        version = switch.connection.protocol.version
146
        return Flow10 if version == 0x01 else Flow13
147
148
    @listen_to('.*.of_core.*.ofpt_error')
149
    def handle_errors(self, event):
150
        """Receive OpenFlow error and send a event.
151
152
            The event is sent only if the error is related to a request made
153
            by flow_manager.
154
        """
155
        xid = event.content["message"].header.xid.value
156
        try:
157
            flow = self.flow_mods_sent[xid]
158
            self._send_napp_event(flow.switch, flow, 'error')
159
        except KeyError:
160
            pass
161