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