1 # Copyright 2015 Intel Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 """Wrapper for an OVS bridge for convenient use of ``ovs-vsctl`` and
18 Much of this code is based on ``ovs-lib.py`` from Open Stack:
20 https://github.com/openstack/neutron/blob/6eac1dc99124ca024d6a69b3abfa3bc69c735667/neutron/agent/linux/ovs_lib.py
27 from tools import tasks
28 from conf import settings
30 _OVS_VSCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities',
32 _OVS_OFCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities',
35 _OVS_VAR_DIR = '/usr/local/var/run/openvswitch/'
37 _OVS_BRIDGE_NAME = settings.getValue('VSWITCH_BRIDGE_NAME')
40 """Add/remove/show datapaths using ``ovs-ofctl``.
42 def __init__(self, timeout=10):
45 :param timeout: Timeout to be used for each command
49 self.logger = logging.getLogger(__name__)
50 self.timeout = timeout
54 def run_vsctl(self, args, check_error=False):
55 """Run ``ovs-vsctl`` with supplied arguments.
57 :param args: Arguments to pass to ``ovs-vsctl``
58 :param check_error: Throw exception on error
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)
68 def add_br(self, br_name=_OVS_BRIDGE_NAME, params=None):
71 :param br_name: Name of bridge
73 :return: Instance of :class OFBridge:
78 self.logger.debug('add bridge')
79 self.run_vsctl(['add-br', br_name]+params)
81 return OFBridge(br_name, self.timeout)
83 def del_br(self, br_name=_OVS_BRIDGE_NAME):
86 :param br_name: Name of bridge
90 self.logger.debug('delete bridge')
91 self.run_vsctl(['del-br', br_name])
94 class OFBridge(OFBase):
95 """Control a bridge instance using ``ovs-vsctl`` and ``ovs-ofctl``.
97 def __init__(self, br_name=_OVS_BRIDGE_NAME, timeout=10):
100 :param br_name: Bridge name
101 :param timeout: Timeout to be used for each command
105 super(OFBridge, self).__init__(timeout)
106 self.br_name = br_name
118 def __exit__(self, type_, value, traceback):
126 def run_ofctl(self, args, check_error=False):
127 """Run ``ovs-ofctl`` with supplied arguments.
129 :param args: Arguments to pass to ``ovs-ofctl``
130 :param check_error: Throw exception on error
134 cmd = ['sudo', _OVS_OFCTL_BIN, '-O', 'OpenFlow13', '--timeout',
135 str(self.timeout)] + args
136 return tasks.run_task(
137 cmd, self.logger, 'Running ovs-ofctl...', check_error)
139 def create(self, params=None):
145 self.logger.debug('create bridge')
146 self.add_br(self.br_name, params=params)
151 self.logger.debug('destroy bridge')
152 self.del_br(self.br_name)
157 self.logger.debug('reset bridge')
163 def add_port(self, port_name, params):
164 """Add port to bridge.
166 :param port_name: Name of port
167 :param params: Additional list of parameters to add-port
169 :return: OpenFlow port number for the port
171 self.logger.debug('add port')
172 self.run_vsctl(['add-port', self.br_name, port_name]+params)
174 # This is how port number allocation works currently
175 # This possibly will not work correctly if there are port deletions
177 of_port = len(self._ports) + 1
178 self._ports[port_name] = (of_port, params)
181 def del_port(self, port_name):
182 """Remove port from bridge.
184 :param port_name: Name of port
188 self.logger.debug('delete port')
189 self.run_vsctl(['del-port', self.br_name, port_name])
190 self._ports.pop(port_name)
192 def set_db_attribute(self, table_name, record, column, value):
193 """Set database attribute.
195 :param table_name: Name of table
196 :param record: Name of record
197 :param column: Name of column
198 :param value: Value to set
202 self.logger.debug('set attribute')
203 self.run_vsctl(['set', table_name, record, '%s=%s' % (column, value)])
206 """Get the ports of this bridge
208 Structure of the returned ports dictionary is
209 'portname': (openflow_port_number, extra_parameters)
214 (1, ['--', 'set', 'Interface', 'dpdkport0', 'type=dpdk']),
216 (2, ['--', 'set', 'Interface', 'dpdkvhostport0',
220 :return: Dictionary of ports
224 def clear_db_attribute(self, table_name, record, column):
225 """Clear database attribute.
227 :param table_name: Name of table
228 :param record: Name of record
229 :param column: Name of column
233 self.logger.debug('clear attribute')
234 self.run_vsctl(['clear', table_name, record, column])
238 def add_flow(self, flow):
239 """Add flow to bridge.
241 :param flow: Flow description as a dictionary
242 For flow dictionary structure, see function flow_key
246 if not flow.get('actions'):
247 self.logger.error('add flow requires actions')
250 self.logger.debug('add flow')
251 _flow_key = flow_key(flow)
252 self.logger.debug('key : %s', _flow_key)
253 self.run_ofctl(['add-flow', self.br_name, _flow_key])
255 def del_flow(self, flow):
256 """Delete flow from bridge.
258 :param flow: Flow description as a dictionary
259 For flow dictionary structure, see function flow_key
260 flow=None will delete all flows
264 self.logger.debug('delete flow')
265 _flow_key = flow_key(flow)
266 self.logger.debug('key : %s', _flow_key)
267 self.run_ofctl(['del-flows', self.br_name, _flow_key])
270 """Delete all flows from bridge.
272 self.logger.debug('delete flows')
273 self.run_ofctl(['del-flows', self.br_name])
275 def dump_flows(self):
276 """Dump all flows from bridge.
278 self.logger.debug('dump flows')
279 self.run_ofctl(['dump-flows', self.br_name])
286 """Model a flow key string for ``ovs-ofctl``.
288 Syntax taken from ``ovs-ofctl`` manpages:
289 http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-ofctl.8
291 Example flow dictionary:
295 'actions': ['output:3']
298 :param flow: Flow description as a dictionary
303 _flow_add_key = string.Template('${fields},action=${actions}')
304 _flow_del_key = string.Template('${fields}')
308 user_params = (x for x in list(flow.items()) if x[0] != 'actions')
309 for (key, default) in user_params:
310 field_params.append('%(field)s=%(value)s' %
311 {'field': key, 'value': default})
313 field_params = ','.join(field_params)
316 'fields': field_params,
319 # no actions == delete key
320 if 'actions' in flow:
321 _flow_key_param['actions'] = ','.join(flow['actions'])
323 flow_str = _flow_add_key.substitute(_flow_key_param)
325 flow_str = _flow_del_key.substitute(_flow_key_param)