test_spec: Add LTD.MemoryBandwidth.RFC2544.0PacketLoss.Scalability
[vswitchperf.git] / src / ovs / ofctl.py
1 # Copyright 2015 Intel 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 implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """Wrapper for an OVS bridge for convenient use of ``ovs-vsctl`` and
16 ``ovs-ofctl`` on it.
17
18 Much of this code is based on ``ovs-lib.py`` from Open Stack:
19
20 https://github.com/openstack/neutron/blob/6eac1dc99124ca024d6a69b3abfa3bc69c735667/neutron/agent/linux/ovs_lib.py
21 """
22
23 import os
24 import logging
25 import string
26
27 from tools import tasks
28 from conf import settings
29
30 _OVS_VSCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities',
31                               'ovs-vsctl')
32 _OVS_OFCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities',
33                               'ovs-ofctl')
34
35 class OFBase(object):
36     """Add/remove/show datapaths using ``ovs-ofctl``.
37     """
38     def __init__(self, timeout=10):
39         """Initialise logger.
40
41         :param timeout: Timeout to be used for each command
42
43         :returns: None
44         """
45         self.logger = logging.getLogger(__name__)
46         self.timeout = timeout
47
48     # helpers
49
50     def run_vsctl(self, args, check_error=False):
51         """Run ``ovs-vsctl`` with supplied arguments.
52
53         :param args: Arguments to pass to ``ovs-vsctl``
54         :param check_error: Throw exception on error
55
56         :return: None
57         """
58         cmd = ['sudo', _OVS_VSCTL_BIN, '--timeout', str(self.timeout)] + args
59         return tasks.run_task(
60             cmd, self.logger, 'Running ovs-vsctl...', check_error)
61
62     # datapath management
63
64     def add_br(self, br_name='br0'):
65         """Add datapath.
66
67         :param br_name: Name of bridge
68
69         :return: Instance of :class OFBridge:
70         """
71         self.logger.debug('add bridge')
72         self.run_vsctl(['add-br', br_name])
73
74         return OFBridge(br_name, self.timeout)
75
76     def del_br(self, br_name='br0'):
77         """Delete datapath.
78
79         :param br_name: Name of bridge
80
81         :return: None
82         """
83         self.logger.debug('delete bridge')
84         self.run_vsctl(['del-br', br_name])
85
86
87 class OFBridge(OFBase):
88     """Control a bridge instance using ``ovs-vsctl`` and ``ovs-ofctl``.
89     """
90     def __init__(self, br_name='br0', timeout=10):
91         """Initialise bridge.
92
93         :param br_name: Bridge name
94         :param timeout: Timeout to be used for each command
95
96         :returns: None
97         """
98         super(OFBridge, self).__init__(timeout)
99         self.br_name = br_name
100         self._ports = {}
101
102     # context manager
103
104     def __enter__(self):
105         """Create datapath
106
107         :returns: self
108         """
109         return self
110
111     def __exit__(self, type_, value, traceback):
112         """Remove datapath.
113         """
114         if not traceback:
115             self.destroy()
116
117     # helpers
118
119     def run_ofctl(self, args, check_error=False):
120         """Run ``ovs-ofctl`` with supplied arguments.
121
122         :param args: Arguments to pass to ``ovs-ofctl``
123         :param check_error: Throw exception on error
124
125         :return: None
126         """
127         cmd = ['sudo', _OVS_OFCTL_BIN, '-O', 'OpenFlow13', '--timeout', str(self.timeout)] + args
128         return tasks.run_task(
129             cmd, self.logger, 'Running ovs-ofctl...', check_error)
130
131     def create(self):
132         """Create bridge.
133         """
134         self.logger.debug('create bridge')
135         self.add_br(self.br_name)
136
137     def destroy(self):
138         """Destroy bridge.
139         """
140         self.logger.debug('destroy bridge')
141         self.del_br(self.br_name)
142
143     def reset(self):
144         """Reset bridge.
145         """
146         self.logger.debug('reset bridge')
147         self.destroy()
148         self.create()
149
150     # port management
151
152     def add_port(self, port_name, params):
153         """Add port to bridge.
154
155         :param port_name: Name of port
156         :param params: Additional list of parameters to add-port
157
158         :return: OpenFlow port number for the port
159         """
160         self.logger.debug('add port')
161         self.run_vsctl(['add-port', self.br_name, port_name]+params)
162
163         # This is how port number allocation works currently
164         # This possibly will not work correctly if there are port deletions
165         # in between
166         of_port = len(self._ports) + 1
167         self._ports[port_name] = (of_port, params)
168         return of_port
169
170     def del_port(self, port_name):
171         """Remove port from bridge.
172
173         :param port_name: Name of port
174
175         :return: None
176         """
177         self.logger.debug('delete port')
178         self.run_vsctl(['del-port', self.br_name, port_name])
179         self._ports.pop(port_name)
180
181     def set_db_attribute(self, table_name, record, column, value):
182         """Set database attribute.
183
184         :param table_name: Name of table
185         :param record: Name of record
186         :param column: Name of column
187         :param value: Value to set
188
189         :return: None
190         """
191         self.logger.debug('set attribute')
192         self.run_vsctl(['set', table_name, record, '%s=%s' % (column, value)])
193
194     def get_ports(self):
195         """Get the ports of this bridge
196
197         Structure of the returned ports dictionary is
198         'portname': (openflow_port_number, extra_parameters)
199
200         Example:
201         ports = {
202             'dpdkport0':
203                 (1, ['--', 'set', 'Interface', 'dpdkport0', 'type=dpdk']),
204             'dpdkvhostport0':
205                 (2, ['--', 'set', 'Interface', 'dpdkvhostport0',
206                      'type=dpdkvhost'])
207         }
208
209         :return: Dictionary of ports
210         """
211         return self._ports
212
213     def clear_db_attribute(self, table_name, record, column):
214         """Clear database attribute.
215
216         :param table_name: Name of table
217         :param record: Name of record
218         :param column: Name of column
219
220         :return: None
221         """
222         self.logger.debug('clear attribute')
223         self.run_vsctl(['clear', table_name, record, column])
224
225     # flow mangement
226
227     def add_flow(self, flow):
228         """Add flow to bridge.
229
230         :param flow: Flow description as a dictionary
231         For flow dictionary structure, see function flow_key
232
233         :return: None
234         """
235         if not flow.get('actions'):
236             self.logger.error('add flow requires actions')
237             return
238
239         self.logger.debug('add flow')
240         _flow_key = flow_key(flow)
241         self.logger.debug('key : %s', _flow_key)
242         self.run_ofctl(['add-flow', self.br_name, _flow_key])
243
244     def del_flow(self, flow):
245         """Delete flow from bridge.
246
247         :param flow: Flow description as a dictionary
248         For flow dictionary structure, see function flow_key
249         flow=None will delete all flows
250
251         :return: None
252         """
253         self.logger.debug('delete flow')
254         _flow_key = flow_key(flow)
255         self.logger.debug('key : %s', _flow_key)
256         self.run_ofctl(['del-flows', self.br_name, _flow_key])
257
258     def del_flows(self):
259         """Delete all flows from bridge.
260         """
261         self.logger.debug('delete flows')
262         self.run_ofctl(['del-flows', self.br_name])
263
264     def dump_flows(self):
265         """Dump all flows from bridge.
266         """
267         self.logger.debug('dump flows')
268         self.run_ofctl(['dump-flows', self.br_name])
269
270 #
271 # helper functions
272 #
273
274 def flow_key(flow):
275     """Model a flow key string for ``ovs-ofctl``.
276
277     Syntax taken from ``ovs-ofctl`` manpages:
278         http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-ofctl.8
279
280     Example flow dictionary:
281     flow = {
282         'in_port': '1',
283         'idle_timeout': '0',
284         'actions': ['output:3']
285     }
286
287     :param flow: Flow description as a dictionary
288
289     :return: String
290     :rtype: str
291     """
292     _flow_add_key = string.Template('${fields},action=${actions}')
293     _flow_del_key = string.Template('${fields}')
294
295     field_params = []
296
297     user_params = (x for x in list(flow.items()) if x[0] != 'actions')
298     for (key, default) in user_params:
299         field_params.append('%(field)s=%(value)s' %
300                             {'field': key, 'value': default})
301
302     field_params = ','.join(field_params)
303
304     _flow_key_param = {
305         'fields': field_params,
306     }
307
308     # no actions == delete key
309     if 'actions' in flow:
310         _flow_key_param['actions'] = ','.join(flow['actions'])
311
312         flow_str = _flow_add_key.substitute(_flow_key_param)
313     else:
314         flow_str = _flow_del_key.substitute(_flow_key_param)
315
316     return flow_str