backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / app / ofctl_rest.py
1 # Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import logging
17 import json
18 import ast
19
20 from ryu.base import app_manager
21 from ryu.controller import ofp_event
22 from ryu.controller import dpset
23 from ryu.controller.handler import MAIN_DISPATCHER
24 from ryu.controller.handler import set_ev_cls
25 from ryu.exception import RyuException
26 from ryu.ofproto import ofproto_v1_0
27 from ryu.ofproto import ofproto_v1_2
28 from ryu.ofproto import ofproto_v1_3
29 from ryu.ofproto import ofproto_v1_4
30 from ryu.ofproto import ofproto_v1_5
31 from ryu.lib import ofctl_v1_0
32 from ryu.lib import ofctl_v1_2
33 from ryu.lib import ofctl_v1_3
34 from ryu.lib import ofctl_v1_4
35 from ryu.lib import ofctl_v1_5
36 from ryu.app.wsgi import ControllerBase
37 from ryu.app.wsgi import Response
38 from ryu.app.wsgi import WSGIApplication
39
40 LOG = logging.getLogger('ryu.app.ofctl_rest')
41
42 # supported ofctl versions in this restful app
43 supported_ofctl = {
44     ofproto_v1_0.OFP_VERSION: ofctl_v1_0,
45     ofproto_v1_2.OFP_VERSION: ofctl_v1_2,
46     ofproto_v1_3.OFP_VERSION: ofctl_v1_3,
47     ofproto_v1_4.OFP_VERSION: ofctl_v1_4,
48     ofproto_v1_5.OFP_VERSION: ofctl_v1_5,
49 }
50
51 # REST API
52 #
53
54 # Retrieve the switch stats
55 #
56 # get the list of all switches
57 # GET /stats/switches
58 #
59 # get the desc stats of the switch
60 # GET /stats/desc/<dpid>
61 #
62 # get flows desc stats of the switch
63 # GET /stats/flowdesc/<dpid>
64 #
65 # get flows desc stats of the switch filtered by the fields
66 # POST /stats/flowdesc/<dpid>
67 #
68 # get flows stats of the switch
69 # GET /stats/flow/<dpid>
70 #
71 # get flows stats of the switch filtered by the fields
72 # POST /stats/flow/<dpid>
73 #
74 # get aggregate flows stats of the switch
75 # GET /stats/aggregateflow/<dpid>
76 #
77 # get aggregate flows stats of the switch filtered by the fields
78 # POST /stats/aggregateflow/<dpid>
79 #
80 # get table stats of the switch
81 # GET /stats/table/<dpid>
82 #
83 # get table features stats of the switch
84 # GET /stats/tablefeatures/<dpid>
85 #
86 # get ports stats of the switch
87 # GET /stats/port/<dpid>[/<port>]
88 # Note: Specification of port number is optional
89 #
90 # get queues stats of the switch
91 # GET /stats/queue/<dpid>[/<port>[/<queue_id>]]
92 # Note: Specification of port number and queue id are optional
93 #       If you want to omitting the port number and setting the queue id,
94 #       please specify the keyword "ALL" to the port number
95 #       e.g. GET /stats/queue/1/ALL/1
96 #
97 # get queues config stats of the switch
98 # GET /stats/queueconfig/<dpid>[/<port>]
99 # Note: Specification of port number is optional
100 #
101 # get queues desc stats of the switch
102 # GET /stats/queuedesc/<dpid>[/<port>[/<queue_id>]]
103 # Note: Specification of port number and queue id are optional
104 #       If you want to omitting the port number and setting the queue id,
105 #       please specify the keyword "ALL" to the port number
106 #       e.g. GET /stats/queuedesc/1/ALL/1
107 #
108 # get meter features stats of the switch
109 # GET /stats/meterfeatures/<dpid>
110 #
111 # get meter config stats of the switch
112 # GET /stats/meterconfig/<dpid>[/<meter_id>]
113 # Note: Specification of meter id is optional
114 #
115 # get meter desc stats of the switch
116 # GET /stats/meterdesc/<dpid>[/<meter_id>]
117 # Note: Specification of meter id is optional
118 #
119 # get meters stats of the switch
120 # GET /stats/meter/<dpid>[/<meter_id>]
121 # Note: Specification of meter id is optional
122 #
123 # get group features stats of the switch
124 # GET /stats/groupfeatures/<dpid>
125 #
126 # get groups desc stats of the switch
127 # GET /stats/groupdesc/<dpid>[/<group_id>]
128 # Note: Specification of group id is optional (OpenFlow 1.5 or later)
129 #
130 # get groups stats of the switch
131 # GET /stats/group/<dpid>[/<group_id>]
132 # Note: Specification of group id is optional
133 #
134 # get ports description of the switch
135 # GET /stats/portdesc/<dpid>[/<port_no>]
136 # Note: Specification of port number is optional (OpenFlow 1.5 or later)
137
138 # Update the switch stats
139 #
140 # add a flow entry
141 # POST /stats/flowentry/add
142 #
143 # modify all matching flow entries
144 # POST /stats/flowentry/modify
145 #
146 # modify flow entry strictly matching wildcards and priority
147 # POST /stats/flowentry/modify_strict
148 #
149 # delete all matching flow entries
150 # POST /stats/flowentry/delete
151 #
152 # delete flow entry strictly matching wildcards and priority
153 # POST /stats/flowentry/delete_strict
154 #
155 # delete all flow entries of the switch
156 # DELETE /stats/flowentry/clear/<dpid>
157 #
158 # add a meter entry
159 # POST /stats/meterentry/add
160 #
161 # modify a meter entry
162 # POST /stats/meterentry/modify
163 #
164 # delete a meter entry
165 # POST /stats/meterentry/delete
166 #
167 # add a group entry
168 # POST /stats/groupentry/add
169 #
170 # modify a group entry
171 # POST /stats/groupentry/modify
172 #
173 # delete a group entry
174 # POST /stats/groupentry/delete
175 #
176 # modify behavior of the physical port
177 # POST /stats/portdesc/modify
178 #
179 # modify role of controller
180 # POST /stats/role
181 #
182 #
183 # send a experimeter message
184 # POST /stats/experimenter/<dpid>
185
186
187 class CommandNotFoundError(RyuException):
188     message = 'No such command : %(cmd)s'
189
190
191 class PortNotFoundError(RyuException):
192     message = 'No such port info: %(port_no)s'
193
194
195 def stats_method(method):
196     def wrapper(self, req, dpid, *args, **kwargs):
197         # Get datapath instance from DPSet
198         try:
199             dp = self.dpset.get(int(str(dpid), 0))
200         except ValueError:
201             LOG.exception('Invalid dpid: %s', dpid)
202             return Response(status=400)
203         if dp is None:
204             LOG.error('No such Datapath: %s', dpid)
205             return Response(status=404)
206
207         # Get lib/ofctl_* module
208         try:
209             ofctl = supported_ofctl.get(dp.ofproto.OFP_VERSION)
210         except KeyError:
211             LOG.exception('Unsupported OF version: %s',
212                           dp.ofproto.OFP_VERSION)
213             return Response(status=501)
214
215         # Invoke StatsController method
216         try:
217             ret = method(self, req, dp, ofctl, *args, **kwargs)
218             return Response(content_type='application/json',
219                             body=json.dumps(ret))
220         except ValueError:
221             LOG.exception('Invalid syntax: %s', req.body)
222             return Response(status=400)
223         except AttributeError:
224             LOG.exception('Unsupported OF request in this version: %s',
225                           dp.ofproto.OFP_VERSION)
226             return Response(status=501)
227
228     return wrapper
229
230
231 def command_method(method):
232     def wrapper(self, req, *args, **kwargs):
233         # Parse request json body
234         try:
235             if req.body:
236                 # We use ast.literal_eval() to parse request json body
237                 # instead of json.loads().
238                 # Because we need to parse binary format body
239                 # in send_experimenter().
240                 body = ast.literal_eval(req.body.decode('utf-8'))
241             else:
242                 body = {}
243         except SyntaxError:
244             LOG.exception('Invalid syntax: %s', req.body)
245             return Response(status=400)
246
247         # Get datapath_id from request parameters
248         dpid = body.get('dpid', None)
249         if not dpid:
250             try:
251                 dpid = kwargs.pop('dpid')
252             except KeyError:
253                 LOG.exception('Cannot get dpid from request parameters')
254                 return Response(status=400)
255
256         # Get datapath instance from DPSet
257         try:
258             dp = self.dpset.get(int(str(dpid), 0))
259         except ValueError:
260             LOG.exception('Invalid dpid: %s', dpid)
261             return Response(status=400)
262         if dp is None:
263             LOG.error('No such Datapath: %s', dpid)
264             return Response(status=404)
265
266         # Get lib/ofctl_* module
267         try:
268             ofctl = supported_ofctl.get(dp.ofproto.OFP_VERSION)
269         except KeyError:
270             LOG.exception('Unsupported OF version: version=%s',
271                           dp.ofproto.OFP_VERSION)
272             return Response(status=501)
273
274         # Invoke StatsController method
275         try:
276             method(self, req, dp, ofctl, body, *args, **kwargs)
277             return Response(status=200)
278         except ValueError:
279             LOG.exception('Invalid syntax: %s', req.body)
280             return Response(status=400)
281         except AttributeError:
282             LOG.exception('Unsupported OF request in this version: %s',
283                           dp.ofproto.OFP_VERSION)
284             return Response(status=501)
285         except CommandNotFoundError as e:
286             LOG.exception(e.message)
287             return Response(status=404)
288         except PortNotFoundError as e:
289             LOG.exception(e.message)
290             return Response(status=404)
291
292     return wrapper
293
294
295 class StatsController(ControllerBase):
296     def __init__(self, req, link, data, **config):
297         super(StatsController, self).__init__(req, link, data, **config)
298         self.dpset = data['dpset']
299         self.waiters = data['waiters']
300
301     def get_dpids(self, req, **_kwargs):
302         dps = list(self.dpset.dps.keys())
303         body = json.dumps(dps)
304         return Response(content_type='application/json', body=body)
305
306     @stats_method
307     def get_desc_stats(self, req, dp, ofctl, **kwargs):
308         return ofctl.get_desc_stats(dp, self.waiters)
309
310     @stats_method
311     def get_flow_desc(self, req, dp, ofctl, **kwargs):
312         flow = req.json if req.body else {}
313         return ofctl.get_flow_desc(dp, self.waiters, flow)
314
315     @stats_method
316     def get_flow_stats(self, req, dp, ofctl, **kwargs):
317         flow = req.json if req.body else {}
318         return ofctl.get_flow_stats(dp, self.waiters, flow)
319
320     @stats_method
321     def get_aggregate_flow_stats(self, req, dp, ofctl, **kwargs):
322         flow = req.json if req.body else {}
323         return ofctl.get_aggregate_flow_stats(dp, self.waiters, flow)
324
325     @stats_method
326     def get_table_stats(self, req, dp, ofctl, **kwargs):
327         return ofctl.get_table_stats(dp, self.waiters)
328
329     @stats_method
330     def get_table_features(self, req, dp, ofctl, **kwargs):
331         return ofctl.get_table_features(dp, self.waiters)
332
333     @stats_method
334     def get_port_stats(self, req, dp, ofctl, port=None, **kwargs):
335         if port == "ALL":
336             port = None
337
338         return ofctl.get_port_stats(dp, self.waiters, port)
339
340     @stats_method
341     def get_queue_stats(self, req, dp, ofctl,
342                         port=None, queue_id=None, **kwargs):
343         if port == "ALL":
344             port = None
345
346         if queue_id == "ALL":
347             queue_id = None
348
349         return ofctl.get_queue_stats(dp, self.waiters, port, queue_id)
350
351     @stats_method
352     def get_queue_config(self, req, dp, ofctl, port=None, **kwargs):
353         if port == "ALL":
354             port = None
355
356         return ofctl.get_queue_config(dp, self.waiters, port)
357
358     @stats_method
359     def get_queue_desc(self, req, dp, ofctl,
360                        port=None, queue=None, **_kwargs):
361         if port == "ALL":
362             port = None
363
364         if queue == "ALL":
365             queue = None
366
367         return ofctl.get_queue_desc(dp, self.waiters, port, queue)
368
369     @stats_method
370     def get_meter_features(self, req, dp, ofctl, **kwargs):
371         return ofctl.get_meter_features(dp, self.waiters)
372
373     @stats_method
374     def get_meter_config(self, req, dp, ofctl, meter_id=None, **kwargs):
375         if meter_id == "ALL":
376             meter_id = None
377
378         return ofctl.get_meter_config(dp, self.waiters, meter_id)
379
380     @stats_method
381     def get_meter_desc(self, req, dp, ofctl, meter_id=None, **kwargs):
382         if meter_id == "ALL":
383             meter_id = None
384
385         return ofctl.get_meter_desc(dp, self.waiters, meter_id)
386
387     @stats_method
388     def get_meter_stats(self, req, dp, ofctl, meter_id=None, **kwargs):
389         if meter_id == "ALL":
390             meter_id = None
391
392         return ofctl.get_meter_stats(dp, self.waiters, meter_id)
393
394     @stats_method
395     def get_group_features(self, req, dp, ofctl, **kwargs):
396         return ofctl.get_group_features(dp, self.waiters)
397
398     @stats_method
399     def get_group_desc(self, req, dp, ofctl, group_id=None, **kwargs):
400         if dp.ofproto.OFP_VERSION < ofproto_v1_5.OFP_VERSION:
401             return ofctl.get_group_desc(dp, self.waiters)
402         else:
403             return ofctl.get_group_desc(dp, self.waiters, group_id)
404
405     @stats_method
406     def get_group_stats(self, req, dp, ofctl, group_id=None, **kwargs):
407         if group_id == "ALL":
408             group_id = None
409
410         return ofctl.get_group_stats(dp, self.waiters, group_id)
411
412     @stats_method
413     def get_port_desc(self, req, dp, ofctl, port_no=None, **kwargs):
414         if dp.ofproto.OFP_VERSION < ofproto_v1_5.OFP_VERSION:
415             return ofctl.get_port_desc(dp, self.waiters)
416         else:
417             return ofctl.get_port_desc(dp, self.waiters, port_no)
418
419     @stats_method
420     def get_role(self, req, dp, ofctl, **kwargs):
421         return ofctl.get_role(dp, self.waiters)
422
423     @command_method
424     def mod_flow_entry(self, req, dp, ofctl, flow, cmd, **kwargs):
425         cmd_convert = {
426             'add': dp.ofproto.OFPFC_ADD,
427             'modify': dp.ofproto.OFPFC_MODIFY,
428             'modify_strict': dp.ofproto.OFPFC_MODIFY_STRICT,
429             'delete': dp.ofproto.OFPFC_DELETE,
430             'delete_strict': dp.ofproto.OFPFC_DELETE_STRICT,
431         }
432         mod_cmd = cmd_convert.get(cmd, None)
433         if mod_cmd is None:
434             raise CommandNotFoundError(cmd=cmd)
435
436         ofctl.mod_flow_entry(dp, flow, mod_cmd)
437
438     @command_method
439     def delete_flow_entry(self, req, dp, ofctl, flow, **kwargs):
440         if ofproto_v1_0.OFP_VERSION == dp.ofproto.OFP_VERSION:
441             flow = {}
442         else:
443             flow = {'table_id': dp.ofproto.OFPTT_ALL}
444
445         ofctl.mod_flow_entry(dp, flow, dp.ofproto.OFPFC_DELETE)
446
447     @command_method
448     def mod_meter_entry(self, req, dp, ofctl, meter, cmd, **kwargs):
449         cmd_convert = {
450             'add': dp.ofproto.OFPMC_ADD,
451             'modify': dp.ofproto.OFPMC_MODIFY,
452             'delete': dp.ofproto.OFPMC_DELETE,
453         }
454         mod_cmd = cmd_convert.get(cmd, None)
455         if mod_cmd is None:
456             raise CommandNotFoundError(cmd=cmd)
457
458         ofctl.mod_meter_entry(dp, meter, mod_cmd)
459
460     @command_method
461     def mod_group_entry(self, req, dp, ofctl, group, cmd, **kwargs):
462         cmd_convert = {
463             'add': dp.ofproto.OFPGC_ADD,
464             'modify': dp.ofproto.OFPGC_MODIFY,
465             'delete': dp.ofproto.OFPGC_DELETE,
466         }
467         mod_cmd = cmd_convert.get(cmd, None)
468         if mod_cmd is None:
469             raise CommandNotFoundError(cmd=cmd)
470
471         ofctl.mod_group_entry(dp, group, mod_cmd)
472
473     @command_method
474     def mod_port_behavior(self, req, dp, ofctl, port_config, cmd, **kwargs):
475         port_no = port_config.get('port_no', None)
476         port_no = int(str(port_no), 0)
477
478         port_info = self.dpset.port_state[int(dp.id)].get(port_no)
479         if port_info:
480             port_config.setdefault('hw_addr', port_info.hw_addr)
481             if dp.ofproto.OFP_VERSION < ofproto_v1_4.OFP_VERSION:
482                 port_config.setdefault('advertise', port_info.advertised)
483             else:
484                 port_config.setdefault('properties', port_info.properties)
485         else:
486             raise PortNotFoundError(port_no=port_no)
487
488         if cmd != 'modify':
489             raise CommandNotFoundError(cmd=cmd)
490
491         ofctl.mod_port_behavior(dp, port_config)
492
493     @command_method
494     def send_experimenter(self, req, dp, ofctl, exp, **kwargs):
495         ofctl.send_experimenter(dp, exp)
496
497     @command_method
498     def set_role(self, req, dp, ofctl, role, **kwargs):
499         ofctl.set_role(dp, role)
500
501
502 class RestStatsApi(app_manager.RyuApp):
503     OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION,
504                     ofproto_v1_2.OFP_VERSION,
505                     ofproto_v1_3.OFP_VERSION,
506                     ofproto_v1_4.OFP_VERSION,
507                     ofproto_v1_5.OFP_VERSION]
508     _CONTEXTS = {
509         'dpset': dpset.DPSet,
510         'wsgi': WSGIApplication
511     }
512
513     def __init__(self, *args, **kwargs):
514         super(RestStatsApi, self).__init__(*args, **kwargs)
515         self.dpset = kwargs['dpset']
516         wsgi = kwargs['wsgi']
517         self.waiters = {}
518         self.data = {}
519         self.data['dpset'] = self.dpset
520         self.data['waiters'] = self.waiters
521         mapper = wsgi.mapper
522
523         wsgi.registory['StatsController'] = self.data
524         path = '/stats'
525         uri = path + '/switches'
526         mapper.connect('stats', uri,
527                        controller=StatsController, action='get_dpids',
528                        conditions=dict(method=['GET']))
529
530         uri = path + '/desc/{dpid}'
531         mapper.connect('stats', uri,
532                        controller=StatsController, action='get_desc_stats',
533                        conditions=dict(method=['GET']))
534
535         uri = path + '/flowdesc/{dpid}'
536         mapper.connect('stats', uri,
537                        controller=StatsController, action='get_flow_stats',
538                        conditions=dict(method=['GET', 'POST']))
539
540         uri = path + '/flow/{dpid}'
541         mapper.connect('stats', uri,
542                        controller=StatsController, action='get_flow_stats',
543                        conditions=dict(method=['GET', 'POST']))
544
545         uri = path + '/aggregateflow/{dpid}'
546         mapper.connect('stats', uri,
547                        controller=StatsController,
548                        action='get_aggregate_flow_stats',
549                        conditions=dict(method=['GET', 'POST']))
550
551         uri = path + '/table/{dpid}'
552         mapper.connect('stats', uri,
553                        controller=StatsController, action='get_table_stats',
554                        conditions=dict(method=['GET']))
555
556         uri = path + '/tablefeatures/{dpid}'
557         mapper.connect('stats', uri,
558                        controller=StatsController, action='get_table_features',
559                        conditions=dict(method=['GET']))
560
561         uri = path + '/port/{dpid}'
562         mapper.connect('stats', uri,
563                        controller=StatsController, action='get_port_stats',
564                        conditions=dict(method=['GET']))
565
566         uri = path + '/port/{dpid}/{port}'
567         mapper.connect('stats', uri,
568                        controller=StatsController, action='get_port_stats',
569                        conditions=dict(method=['GET']))
570
571         uri = path + '/queue/{dpid}'
572         mapper.connect('stats', uri,
573                        controller=StatsController, action='get_queue_stats',
574                        conditions=dict(method=['GET']))
575
576         uri = path + '/queue/{dpid}/{port}'
577         mapper.connect('stats', uri,
578                        controller=StatsController, action='get_queue_stats',
579                        conditions=dict(method=['GET']))
580
581         uri = path + '/queue/{dpid}/{port}/{queue_id}'
582         mapper.connect('stats', uri,
583                        controller=StatsController, action='get_queue_stats',
584                        conditions=dict(method=['GET']))
585
586         uri = path + '/queueconfig/{dpid}'
587         mapper.connect('stats', uri,
588                        controller=StatsController, action='get_queue_config',
589                        conditions=dict(method=['GET']))
590
591         uri = path + '/queueconfig/{dpid}/{port}'
592         mapper.connect('stats', uri,
593                        controller=StatsController, action='get_queue_config',
594                        conditions=dict(method=['GET']))
595
596         uri = path + '/queuedesc/{dpid}'
597         mapper.connect('stats', uri,
598                        controller=StatsController, action='get_queue_desc',
599                        conditions=dict(method=['GET']))
600
601         uri = path + '/queuedesc/{dpid}/{port}'
602         mapper.connect('stats', uri,
603                        controller=StatsController, action='get_queue_desc',
604                        conditions=dict(method=['GET']))
605
606         uri = path + '/queuedesc/{dpid}/{port}/{queue}'
607         mapper.connect('stats', uri,
608                        controller=StatsController, action='get_queue_desc',
609                        conditions=dict(method=['GET']))
610
611         uri = path + '/meterfeatures/{dpid}'
612         mapper.connect('stats', uri,
613                        controller=StatsController, action='get_meter_features',
614                        conditions=dict(method=['GET']))
615
616         uri = path + '/meterconfig/{dpid}'
617         mapper.connect('stats', uri,
618                        controller=StatsController, action='get_meter_config',
619                        conditions=dict(method=['GET']))
620
621         uri = path + '/meterconfig/{dpid}/{meter_id}'
622         mapper.connect('stats', uri,
623                        controller=StatsController, action='get_meter_config',
624                        conditions=dict(method=['GET']))
625
626         uri = path + '/meterdesc/{dpid}'
627         mapper.connect('stats', uri,
628                        controller=StatsController, action='get_meter_desc',
629                        conditions=dict(method=['GET']))
630
631         uri = path + '/meterdesc/{dpid}/{meter_id}'
632         mapper.connect('stats', uri,
633                        controller=StatsController, action='get_meter_desc',
634                        conditions=dict(method=['GET']))
635
636         uri = path + '/meter/{dpid}'
637         mapper.connect('stats', uri,
638                        controller=StatsController, action='get_meter_stats',
639                        conditions=dict(method=['GET']))
640
641         uri = path + '/meter/{dpid}/{meter_id}'
642         mapper.connect('stats', uri,
643                        controller=StatsController, action='get_meter_stats',
644                        conditions=dict(method=['GET']))
645
646         uri = path + '/groupfeatures/{dpid}'
647         mapper.connect('stats', uri,
648                        controller=StatsController, action='get_group_features',
649                        conditions=dict(method=['GET']))
650
651         uri = path + '/groupdesc/{dpid}'
652         mapper.connect('stats', uri,
653                        controller=StatsController, action='get_group_desc',
654                        conditions=dict(method=['GET']))
655
656         uri = path + '/groupdesc/{dpid}/{group_id}'
657         mapper.connect('stats', uri,
658                        controller=StatsController, action='get_group_desc',
659                        conditions=dict(method=['GET']))
660
661         uri = path + '/group/{dpid}'
662         mapper.connect('stats', uri,
663                        controller=StatsController, action='get_group_stats',
664                        conditions=dict(method=['GET']))
665
666         uri = path + '/group/{dpid}/{group_id}'
667         mapper.connect('stats', uri,
668                        controller=StatsController, action='get_group_stats',
669                        conditions=dict(method=['GET']))
670
671         uri = path + '/portdesc/{dpid}'
672         mapper.connect('stats', uri,
673                        controller=StatsController, action='get_port_desc',
674                        conditions=dict(method=['GET']))
675
676         uri = path + '/portdesc/{dpid}/{port_no}'
677         mapper.connect('stats', uri,
678                        controller=StatsController, action='get_port_desc',
679                        conditions=dict(method=['GET']))
680
681         uri = path + '/role/{dpid}'
682         mapper.connect('stats', uri,
683                        controller=StatsController, action='get_role',
684                        conditions=dict(method=['GET']))
685
686         uri = path + '/flowentry/{cmd}'
687         mapper.connect('stats', uri,
688                        controller=StatsController, action='mod_flow_entry',
689                        conditions=dict(method=['POST']))
690
691         uri = path + '/flowentry/clear/{dpid}'
692         mapper.connect('stats', uri,
693                        controller=StatsController, action='delete_flow_entry',
694                        conditions=dict(method=['DELETE']))
695
696         uri = path + '/meterentry/{cmd}'
697         mapper.connect('stats', uri,
698                        controller=StatsController, action='mod_meter_entry',
699                        conditions=dict(method=['POST']))
700
701         uri = path + '/groupentry/{cmd}'
702         mapper.connect('stats', uri,
703                        controller=StatsController, action='mod_group_entry',
704                        conditions=dict(method=['POST']))
705
706         uri = path + '/portdesc/{cmd}'
707         mapper.connect('stats', uri,
708                        controller=StatsController, action='mod_port_behavior',
709                        conditions=dict(method=['POST']))
710
711         uri = path + '/experimenter/{dpid}'
712         mapper.connect('stats', uri,
713                        controller=StatsController, action='send_experimenter',
714                        conditions=dict(method=['POST']))
715
716         uri = path + '/role'
717         mapper.connect('stats', uri,
718                        controller=StatsController, action='set_role',
719                        conditions=dict(method=['POST']))
720
721     @set_ev_cls([ofp_event.EventOFPStatsReply,
722                  ofp_event.EventOFPDescStatsReply,
723                  ofp_event.EventOFPFlowStatsReply,
724                  ofp_event.EventOFPAggregateStatsReply,
725                  ofp_event.EventOFPTableStatsReply,
726                  ofp_event.EventOFPTableFeaturesStatsReply,
727                  ofp_event.EventOFPPortStatsReply,
728                  ofp_event.EventOFPQueueStatsReply,
729                  ofp_event.EventOFPQueueDescStatsReply,
730                  ofp_event.EventOFPMeterStatsReply,
731                  ofp_event.EventOFPMeterFeaturesStatsReply,
732                  ofp_event.EventOFPMeterConfigStatsReply,
733                  ofp_event.EventOFPGroupStatsReply,
734                  ofp_event.EventOFPGroupFeaturesStatsReply,
735                  ofp_event.EventOFPGroupDescStatsReply,
736                  ofp_event.EventOFPPortDescStatsReply
737                  ], MAIN_DISPATCHER)
738     def stats_reply_handler(self, ev):
739         msg = ev.msg
740         dp = msg.datapath
741
742         if dp.id not in self.waiters:
743             return
744         if msg.xid not in self.waiters[dp.id]:
745             return
746         lock, msgs = self.waiters[dp.id][msg.xid]
747         msgs.append(msg)
748
749         flags = 0
750         if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
751             flags = dp.ofproto.OFPSF_REPLY_MORE
752         elif dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
753             flags = dp.ofproto.OFPSF_REPLY_MORE
754         elif dp.ofproto.OFP_VERSION >= ofproto_v1_3.OFP_VERSION:
755             flags = dp.ofproto.OFPMPF_REPLY_MORE
756
757         if msg.flags & flags:
758             return
759         del self.waiters[dp.id][msg.xid]
760         lock.set()
761
762     @set_ev_cls([ofp_event.EventOFPSwitchFeatures,
763                  ofp_event.EventOFPQueueGetConfigReply,
764                  ofp_event.EventOFPRoleReply,
765                  ], MAIN_DISPATCHER)
766     def features_reply_handler(self, ev):
767         msg = ev.msg
768         dp = msg.datapath
769
770         if dp.id not in self.waiters:
771             return
772         if msg.xid not in self.waiters[dp.id]:
773             return
774         lock, msgs = self.waiters[dp.id][msg.xid]
775         msgs.append(msg)
776
777         del self.waiters[dp.id][msg.xid]
778         lock.set()