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