1 # Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 Wrapper utility library of :py:mod:`ryu.lib.ovs.vsctl`
25 import ryu.exception as ryu_exc
26 import ryu.lib.dpid as dpid_lib
27 import ryu.lib.ovs.vsctl as ovs_vsctl
28 from ryu.lib.ovs.vsctl import valid_ovsdb_addr
31 LOG = logging.getLogger(__name__)
35 cfg.IntOpt('ovsdb-timeout', default=2, help='ovsdb timeout')
39 class OVSBridgeNotFound(ryu_exc.RyuException):
40 message = 'no bridge for datapath_id %(datapath_id)s'
43 class VifPort(object):
45 def __init__(self, port_name, ofport, vif_id, vif_mac, switch):
46 super(VifPort, self).__init__()
47 self.port_name = port_name
50 self.vif_mac = vif_mac
54 return ('iface-id=%s, '
58 'bridge_name=%s' % (self.vif_id,
65 class TunnelPort(object):
67 def __init__(self, port_name, ofport, tunnel_type, local_ip, remote_ip):
68 super(TunnelPort, self).__init__()
69 self.port_name = port_name
71 self.tunnel_type = tunnel_type
72 self.local_ip = local_ip
73 self.remote_ip = remote_ip
75 def __eq__(self, other):
76 return (self.port_name == other.port_name and
77 self.ofport == other.ofport and
78 self.tunnel_type == other.tunnel_type and
79 self.local_ip == other.local_ip and
80 self.remote_ip == other.remote_ip)
83 return ('port_name=%s, '
87 'remote_ip=%s' % (self.port_name,
94 class OVSBridge(object):
96 Class to provide wrapper utilities of :py:mod:`ryu.lib.ovs.vsctl.VSCtl`
98 ``CONF`` is a instance of ``oslo_config.cfg.ConfigOpts``.
99 Mostly ``self.CONF`` is sufficient to instantiate this class from your Ryu
102 ``datapath_id`` specifies Datapath ID of the target OVS instance.
104 ``ovsdb_addr`` specifies the address of the OVS instance.
105 Automatically validated when you call ``init()`` method.
106 Refer to :py:mod:`ryu.lib.ovs.vsctl.valid_ovsdb_addr` for the format of
109 if ``timeout`` is omitted, ``CONF.ovsdb_timeout`` will be used as the
112 Usage of ``timeout`` and ``exception`` is the same with ``timeout_sec``
113 and ``exception`` of :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
116 def __init__(self, CONF, datapath_id, ovsdb_addr, timeout=None,
118 super(OVSBridge, self).__init__()
119 self.datapath_id = datapath_id
120 self.ovsdb_addr = ovsdb_addr
121 self.vsctl = ovs_vsctl.VSCtl(ovsdb_addr)
122 self.timeout = timeout or CONF.ovsdb_timeout
123 self.exception = exception
127 def run_command(self, commands):
129 Executes the given commands and sends OVSDB messages.
131 ``commands`` must be a list of
132 :py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
134 The given ``timeout`` and ``exception`` when instantiation will be used
135 to call :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
137 self.vsctl.run_command(commands, self.timeout, self.exception)
141 Validates the given ``ovsdb_addr`` and connects to OVS instance.
143 If failed to connect to OVS instance or the given ``datapath_id`` does
144 not match with the Datapath ID of the connected OVS instance, raises
145 :py:mod:`ryu.lib.ovs.bridge.OVSBridgeNotFound` exception.
147 if not valid_ovsdb_addr(self.ovsdb_addr):
148 raise ValueError('Invalid OVSDB address: %s' % self.ovsdb_addr)
149 if self.br_name is None:
150 self.br_name = self._get_bridge_name()
152 def _get_bridge_name(self):
153 """ get Bridge name of a given 'datapath_id' """
154 command = ovs_vsctl.VSCtlCommand(
157 'datapath_id=%s' % dpid_lib.dpid_to_str(self.datapath_id)))
158 self.run_command([command])
159 if not isinstance(command.result, list) or len(command.result) != 1:
160 raise OVSBridgeNotFound(
161 datapath_id=dpid_lib.dpid_to_str(self.datapath_id))
162 return command.result[0].name
164 def get_controller(self):
166 Gets the configured OpenFlow controller address.
168 This method is corresponding to the following ovs-vsctl command::
170 $ ovs-vsctl get-controller <bridge>
172 command = ovs_vsctl.VSCtlCommand('get-controller', [self.br_name])
173 self.run_command([command])
174 result = command.result
175 return result[0] if len(result) == 1 else result
177 def set_controller(self, controllers):
179 Sets the OpenFlow controller address.
181 This method is corresponding to the following ovs-vsctl command::
183 $ ovs-vsctl set-controller <bridge> <target>...
185 command = ovs_vsctl.VSCtlCommand('set-controller', [self.br_name])
186 command.args.extend(controllers)
187 self.run_command([command])
189 def del_controller(self):
191 Deletes the configured OpenFlow controller address.
193 This method is corresponding to the following ovs-vsctl command::
195 $ ovs-vsctl del-controller <bridge>
197 command = ovs_vsctl.VSCtlCommand('del-controller', [self.br_name])
198 self.run_command([command])
200 def list_db_attributes(self, table, record=None):
202 Lists 'record' (or all records) in 'table'.
204 This method is corresponding to the following ovs-vsctl command::
206 $ ovs-vsctl list TBL [REC]
208 command = ovs_vsctl.VSCtlCommand('list', (table, record))
209 self.run_command([command])
211 return command.result
214 def find_db_attributes(self, table, *conditions):
216 Lists records satisfying 'conditions' in 'table'.
218 This method is corresponding to the following ovs-vsctl command::
220 $ ovs-vsctl find TBL CONDITION...
224 Currently, only '=' condition is supported.
225 To support other condition is TODO.
228 args.extend(conditions)
229 command = ovs_vsctl.VSCtlCommand('find', args)
230 self.run_command([command])
232 return command.result
235 def get_db_attribute(self, table, record, column, key=None):
237 Gets values of 'column' in 'record' in 'table'.
239 This method is corresponding to the following ovs-vsctl command::
241 $ ovs-vsctl get TBL REC COL[:KEY]
244 column = '%s:%s' % (column, key)
245 command = ovs_vsctl.VSCtlCommand(
246 'get', (table, record, column))
247 self.run_command([command])
249 return command.result[0]
252 def set_db_attribute(self, table, record, column, value, key=None):
254 Sets 'value' into 'column' in 'record' in 'table'.
256 This method is corresponding to the following ovs-vsctl command::
258 $ ovs-vsctl set TBL REC COL[:KEY]=VALUE
261 column = '%s:%s' % (column, key)
262 command = ovs_vsctl.VSCtlCommand(
263 'set', (table, record, '%s=%s' % (column, value)))
264 self.run_command([command])
266 def add_db_attribute(self, table, record, column, value, key=None):
268 Adds ('key'=)'value' into 'column' in 'record' in 'table'.
270 This method is corresponding to the following ovs-vsctl command::
272 $ ovs-vsctl add TBL REC COL [KEY=]VALUE
275 value = '%s=%s' % (key, value)
276 command = ovs_vsctl.VSCtlCommand(
277 'add', (table, record, column, value))
278 self.run_command([command])
280 def remove_db_attribute(self, table, record, column, value, key=None):
282 Removes ('key'=)'value' into 'column' in 'record' in 'table'.
284 This method is corresponding to the following ovs-vsctl command::
286 $ ovs-vsctl remove TBL REC COL [KEY=]VALUE
289 value = '%s=%s' % (key, value)
290 command = ovs_vsctl.VSCtlCommand(
291 'remove', (table, record, column, value))
292 self.run_command([command])
294 def clear_db_attribute(self, table, record, column):
296 Clears values from 'column' in 'record' in 'table'.
298 This method is corresponding to the following ovs-vsctl command::
300 $ ovs-vsctl clear TBL REC COL
302 command = ovs_vsctl.VSCtlCommand('clear', (table, record, column))
303 self.run_command([command])
305 def db_get_val(self, table, record, column):
307 Gets values of 'column' in 'record' in 'table'.
309 This method is corresponding to the following ovs-vsctl command::
311 $ ovs-vsctl get TBL REC COL
313 command = ovs_vsctl.VSCtlCommand('get', (table, record, column))
314 self.run_command([command])
315 assert len(command.result) == 1
316 return command.result[0]
318 def db_get_map(self, table, record, column):
320 Gets dict type value of 'column' in 'record' in 'table'.
322 This method is corresponding to the following ovs-vsctl command::
324 $ ovs-vsctl get TBL REC COL
326 val = self.db_get_val(table, record, column)
327 assert isinstance(val, dict)
330 def get_datapath_id(self):
332 Gets Datapath ID of OVS instance.
334 This method is corresponding to the following ovs-vsctl command::
336 $ ovs-vsctl get Bridge <bridge> datapath_id
338 return self.db_get_val('Bridge', self.br_name, 'datapath_id')
340 def delete_port(self, port_name):
342 Deletes a port on the OVS instance.
344 This method is corresponding to the following ovs-vsctl command::
346 $ ovs-vsctl --if-exists del-port <bridge> <port>
348 command = ovs_vsctl.VSCtlCommand(
349 'del-port', (self.br_name, port_name), '--if-exists')
350 self.run_command([command])
352 def get_ofport(self, port_name):
354 Gets the OpenFlow port number.
356 This method is corresponding to the following ovs-vsctl command::
358 $ ovs-vsctl get Interface <port> ofport
360 ofport_list = self.db_get_val('Interface', port_name, 'ofport')
361 assert len(ofport_list) == 1
362 return int(ofport_list[0])
364 def get_port_name_list(self):
366 Gets a list of all ports on OVS instance.
368 This method is corresponding to the following ovs-vsctl command::
370 $ ovs-vsctl list-ports <bridge>
372 command = ovs_vsctl.VSCtlCommand('list-ports', (self.br_name, ))
373 self.run_command([command])
374 return command.result
376 def add_bond(self, name, ifaces, bond_mode=None, lacp=None):
378 Creates a bonded port.
380 :param name: Port name to be created
381 :param ifaces: List of interfaces containing at least 2 interfaces
382 :param bond_mode: Bonding mode (active-backup, balance-tcp
384 :param lacp: LACP mode (active, passive or off)
386 assert len(ifaces) >= 2
390 options += 'bond_mode=%(bond_mode)s' % locals()
392 options += 'lacp=%(lacp)s' % locals()
394 command_add = ovs_vsctl.VSCtlCommand(
395 'add-bond', (self.br_name, name, ifaces), options)
396 self.run_command([command_add])
398 def add_tunnel_port(self, name, tunnel_type, remote_ip,
399 local_ip=None, key=None, ofport=None):
401 Creates a tunnel port.
403 :param name: Port name to be created
404 :param tunnel_type: Type of tunnel (gre or vxlan)
405 :param remote_ip: Remote IP address of tunnel
406 :param local_ip: Local IP address of tunnel
407 :param key: Key of GRE or VNI of VxLAN
408 :param ofport: Requested OpenFlow port number
410 options = 'remote_ip=%(remote_ip)s' % locals()
412 options += ',key=%(key)s' % locals()
414 options += ',local_ip=%(local_ip)s' % locals()
416 args = ['Interface', name, 'type=%s' % tunnel_type,
417 'options:%s' % options]
419 args.append('ofport_request=%(ofport)s' % locals())
421 command_add = ovs_vsctl.VSCtlCommand('add-port', (self.br_name, name))
422 command_set = ovs_vsctl.VSCtlCommand('set', args)
423 self.run_command([command_add, command_set])
425 def add_gre_port(self, name, remote_ip,
426 local_ip=None, key=None, ofport=None):
428 Creates a GRE tunnel port.
430 See the description of ``add_tunnel_port()``.
432 self.add_tunnel_port(name, 'gre', remote_ip,
433 local_ip=local_ip, key=key, ofport=ofport)
435 def add_vxlan_port(self, name, remote_ip,
436 local_ip=None, key=None, ofport=None):
438 Creates a VxLAN tunnel port.
440 See the description of ``add_tunnel_port()``.
442 self.add_tunnel_port(name, 'vxlan', remote_ip,
443 local_ip=local_ip, key=key, ofport=ofport)
445 def del_port(self, port_name):
447 Deletes a port on OVS instance.
449 This method is corresponding to the following ovs-vsctl command::
451 $ ovs-vsctl del-port <bridge> <port>
453 command = ovs_vsctl.VSCtlCommand('del-port', (self.br_name, port_name))
454 self.run_command([command])
456 def _get_ports(self, get_port):
458 port_names = self.get_port_name_list()
459 for name in port_names:
460 if self.get_ofport(name) < 0:
462 port = get_port(name)
468 def _vifport(self, name, external_ids):
469 ofport = self.get_ofport(name)
470 return VifPort(name, ofport, external_ids['iface-id'],
471 external_ids['attached-mac'], self)
473 def _get_vif_port(self, name):
474 external_ids = self.db_get_map('Interface', name, 'external_ids')
475 if 'iface-id' in external_ids and 'attached-mac' in external_ids:
476 return self._vifport(name, external_ids)
478 def get_vif_ports(self):
479 """ Returns a VIF object for each VIF port """
480 return self._get_ports(self._get_vif_port)
482 def _get_external_port(self, name):
484 external_ids = self.db_get_map('Interface', name, 'external_ids')
488 # exclude tunnel ports
489 options = self.db_get_map('Interface', name, 'options')
490 if 'remote_ip' in options:
493 ofport = self.get_ofport(name)
494 return VifPort(name, ofport, None, None, self)
496 def get_external_ports(self):
497 return self._get_ports(self._get_external_port)
499 def get_tunnel_port(self, name, tunnel_type='gre'):
500 type_ = self.db_get_val('Interface', name, 'type')
501 if type_ != tunnel_type:
504 options = self.db_get_map('Interface', name, 'options')
505 if 'local_ip' in options and 'remote_ip' in options:
506 ofport = self.get_ofport(name)
507 return TunnelPort(name, ofport, tunnel_type,
508 options['local_ip'], options['remote_ip'])
510 def get_tunnel_ports(self, tunnel_type='gre'):
511 get_tunnel_port = functools.partial(self.get_tunnel_port,
512 tunnel_type=tunnel_type)
513 return self._get_ports(get_tunnel_port)
515 def get_quantum_ports(self, port_name):
516 LOG.debug('port_name %s', port_name)
517 command = ovs_vsctl.VSCtlCommand(
518 'list-ifaces-verbose',
519 [dpid_lib.dpid_to_str(self.datapath_id), port_name])
520 self.run_command([command])
522 return command.result[0]
525 def set_qos(self, port_name, type='linux-htb', max_rate=None, queues=None):
527 Sets a Qos rule and creates Queues on the given port.
529 queues = queues if queues else []
530 command_qos = ovs_vsctl.VSCtlCommand(
532 [port_name, type, max_rate])
533 command_queue = ovs_vsctl.VSCtlCommand(
536 self.run_command([command_qos, command_queue])
537 if command_qos.result and command_queue.result:
538 return command_qos.result + command_queue.result
541 def del_qos(self, port_name):
543 Deletes the Qos rule on the given port.
545 command = ovs_vsctl.VSCtlCommand(
548 self.run_command([command])