backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / ovs / vsctl.py
1 # Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp>
3 #
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
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
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
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 """
18 ``ovs-vsctl`` command like library to speak OVSDB protocol
19 """
20
21 from __future__ import print_function
22
23 import logging
24 import operator
25 import os
26 import re
27 import sys
28 import weakref
29
30 import six
31
32 import ovs.db.data
33 import ovs.db.parser
34 import ovs.db.schema
35 import ovs.db.types
36 import ovs.poller
37 import ovs.json
38 from ovs import jsonrpc
39 from ovs import ovsuuid
40 from ovs import stream
41 from ovs.db import idl
42
43 from ryu.lib import hub
44 from ryu.lib import ip
45 from ryu.lib.ovs import vswitch_idl
46 from ryu.lib.stringify import StringifyMixin
47
48
49 LOG = logging.getLogger(__name__)       # use ovs.vlog?
50
51
52 def valid_ovsdb_addr(addr):
53     """
54     Returns True if the given addr is valid OVSDB server address, otherwise
55     False.
56
57     The valid formats are:
58
59     - ``unix:file``
60     - ``tcp:ip:port``
61     - ``ssl:ip:port``
62
63     If ip is IPv6 address, wrap ip with brackets (e.g., ssl:[::1]:6640).
64
65     :param addr: str value of OVSDB server address.
66     :return: True if valid, otherwise False.
67     """
68     # Assumes Unix socket format: "unix:file"
69     m = re.match(r'unix:(\S+)', addr)
70     if m:
71         file = m.group(1)
72         return os.path.isfile(file)
73     # Assumes TCP/SSL socket format: "tcp:ip:port" or "ssl:ip:port"
74     m = re.match(r'(tcp|ssl):(\S+):(\d+)', addr)
75     if m:
76         address = m.group(2)
77         port = m.group(3)
78         if '[' in address:
79             address = address.strip('[').strip(']')
80             return ip.valid_ipv6(address) and port.isdigit()
81         else:
82             return ip.valid_ipv4(address) and port.isdigit()
83     # Assumes invalid format or unsupported type
84     return False
85
86
87 # for debug
88 def ovsrec_row_changes_to_string(ovsrec_row):
89     if not ovsrec_row._changes:
90         return ovsrec_row._changes
91
92     return dict((key, value.to_string())
93                 for key, value in ovsrec_row._changes.items())
94
95
96 # for debug
97 def ovsrec_row_to_string(ovsrec_row):
98     output = ''
99     output += 'uuid: %s ' % ovsrec_row.uuid
100     if ovsrec_row._data:
101         output += '_data: %s ' % dict((key, value.to_string()) for key, value
102                                       in ovsrec_row._data.items())
103     else:
104         output += '_data: %s ' % ovsrec_row._data
105     output += '_changes: %s' % ovsrec_row_changes_to_string(ovsrec_row)
106     return output
107
108
109 def atom_from_string(base, value_string, symtab=None):
110     type_ = base.type
111     atom = None
112     if type_ == ovs.db.types.IntegerType:
113         atom = ovs.db.data.Atom(type_, int(value_string))
114     elif type_ == ovs.db.types.RealType:
115         # TODO:XXX negation
116         atom = ovs.db.data.Atom(
117             type_, ovs.db.parser.float_to_int(float(value_string)))
118     elif type_ == ovs.db.types.BooleanType:
119         if value_string in ("true", "yes", "on", "1"):
120             atom = ovs.db.data.Atom(type_, True)
121         elif value_string == ("false", "no", "off", "0"):
122             atom = ovs.db.data.Atom(type_, False)
123     elif type_ == ovs.db.types.StringType:
124         # TODO:XXXX escape: if value_string[0] == '"':
125         atom = ovs.db.data.Atom(type_, value_string)
126     elif type_ == ovs.db.types.UuidType:
127         if value_string[0] == "@":
128             assert symtab is not None
129             uuid_ = symtab[value_string]
130             atom = ovs.db.data.Atom(type_, uuid_)
131         else:
132             atom = ovs.db.data.Atom(type_,
133                                     ovs.ovsuuid.from_string(value_string))
134     if atom is None:
135         raise ValueError("expected %s" % type_.to_string(), value_string)
136     atom.check_constraints(base)
137     return atom
138
139
140 def datum_from_string(type_, value_string, symtab=None):
141     value_string = value_string.strip()
142     if type_.is_map():
143         if value_string.startswith('{'):
144             # TODO:dict case
145             LOG.debug('value_string %s', value_string)
146             raise NotImplementedError()
147         d = dict(v.split('=', 1) for v in value_string.split(','))
148         d = dict((atom_from_string(type_.key, key, symtab),
149                   atom_from_string(type_.value, value, symtab))
150                  for key, value in d.items())
151     elif type_.is_set():
152         if value_string.startswith('['):
153             # TODO:set case
154             LOG.debug('value_string %s', value_string)
155             raise NotImplementedError()
156         values = value_string.split(',')
157         d = dict((atom_from_string(type_.key, value, symtab), None)
158                  for value in values)
159     else:
160         atom = atom_from_string(type_.key, value_string, symtab)
161         d = {atom: None}
162
163     datum = ovs.db.data.Datum(type_, d)
164     return datum.to_json()
165
166
167 def ifind(pred, seq):
168     try:
169         return [i for i in seq if pred(i)][0]
170     except IndexError:
171         return None
172
173
174 def not_reached():
175     os.abort()
176
177
178 def vsctl_fatal(msg):
179     LOG.error(msg)
180     raise Exception(msg)        # not call ovs.utils.ovs_fatal for reusability
181
182
183 class VSCtlBridge(object):
184
185     def __init__(self, ovsrec_bridge, name, parent, vlan):
186         super(VSCtlBridge, self).__init__()
187         self.br_cfg = ovsrec_bridge
188         self.name = name
189         self.ports = set()
190         self.parent = parent
191         self.vlan = vlan
192         self.children = set()   # WeakSet is needed?
193
194     def find_vlan_bridge(self, vlan):
195         return ifind(lambda child: child.vlan == vlan, self.children)
196
197
198 class VSCtlPort(object):
199
200     def __init__(self, vsctl_bridge_parent, ovsrec_port):
201         super(VSCtlPort, self).__init__()
202         self.bridge = weakref.ref(vsctl_bridge_parent)  # backpointer
203         self.port_cfg = ovsrec_port
204
205         self.ifaces = set()
206         self.qos = None
207
208
209 class VSCtlIface(object):
210
211     def __init__(self, vsctl_port_parent, ovsrec_iface):
212         super(VSCtlIface, self).__init__()
213         self.port = weakref.ref(vsctl_port_parent)      # backpointer
214         self.iface_cfg = ovsrec_iface
215
216
217 class VSCtlQoS(object):
218
219     def __init__(self, vsctl_port_parent, ovsrec_qos):
220         super(VSCtlQoS, self).__init__()
221         self.port = weakref.ref(vsctl_port_parent)
222         self.qos_cfg = ovsrec_qos
223         self.queues = set()
224
225
226 class VSCtlQueue(object):
227
228     def __init__(self, vsctl_qos_parent, ovsrec_queue):
229         super(VSCtlQueue, self).__init__()
230         self.qos = weakref.ref(vsctl_qos_parent)
231         self.queue_cfg = ovsrec_queue
232
233
234 class VSCtlContext(object):
235
236     def _invalidate_cache(self):
237         self.cache_valid = False
238         self.bridges.clear()
239         self.ports.clear()
240         self.ifaces.clear()
241
242     def __init__(self, idl_, txn, ovsrec_open_vswitch):
243         super(VSCtlContext, self).__init__()
244
245         # Modifiable state
246         # self.table = None
247         self.idl = idl_
248         self.txn = txn
249         self.ovs = ovsrec_open_vswitch
250         self.symtab = None      # TODO:XXX
251         self.verified_ports = False
252
253         # A cache of the contents of the database.
254         self.cache_valid = False
255         self.bridges = {}       # bridge name -> VSCtlBridge
256         self.ports = {}         # port name -> VSCtlPort
257         self.ifaces = {}        # iface name -> VSCtlIface
258
259         self.try_again = False  # used by wait-until command
260
261     def done(self):
262         self._invalidate_cache()
263
264     def verify_bridges(self):
265         self.ovs.verify(vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES)
266
267     def verify_ports(self):
268         if self.verified_ports:
269             return
270
271         self.verify_bridges()
272         for ovsrec_bridge in self.idl.tables[
273                 vswitch_idl.OVSREC_TABLE_BRIDGE].rows.values():
274             ovsrec_bridge.verify(vswitch_idl.OVSREC_BRIDGE_COL_PORTS)
275         for ovsrec_port in self.idl.tables[
276                 vswitch_idl.OVSREC_TABLE_PORT].rows.values():
277             ovsrec_port.verify(vswitch_idl.OVSREC_PORT_COL_INTERFACES)
278         self.verified_ports = True
279
280     def add_bridge_to_cache(self, ovsrec_bridge, name, parent, vlan):
281         vsctl_bridge = VSCtlBridge(ovsrec_bridge, name, parent, vlan)
282         if parent:
283             parent.children.add(vsctl_bridge)
284         self.bridges[name] = vsctl_bridge
285         return vsctl_bridge
286
287     def del_cached_bridge(self, vsctl_bridge):
288         assert not vsctl_bridge.ports
289         assert not vsctl_bridge.children
290
291         parent = vsctl_bridge.parent
292         if parent:
293             parent.children.remove(vsctl_bridge)
294             vsctl_bridge.parent = None  # break circular reference
295         ovsrec_bridge = vsctl_bridge.br_cfg
296         if ovsrec_bridge:
297             ovsrec_bridge.delete()
298             self.ovs_delete_bridge(ovsrec_bridge)
299
300         del self.bridges[vsctl_bridge.name]
301
302     def del_cached_qos(self, vsctl_qos):
303         vsctl_qos.port().qos = None
304         vsctl_qos.port = None
305         vsctl_qos.queues = None
306
307     def add_port_to_cache(self, vsctl_bridge_parent, ovsrec_port):
308         tag = getattr(ovsrec_port, vswitch_idl.OVSREC_PORT_COL_TAG, None)
309         if isinstance(tag, list):
310             if len(tag) == 0:
311                 tag = 0
312             else:
313                 tag = tag[0]
314         if tag is not None and 0 <= tag < 4096:
315             vlan_bridge = vsctl_bridge_parent.find_vlan_bridge(tag)
316             if vlan_bridge:
317                 vsctl_bridge_parent = vlan_bridge
318
319         vsctl_port = VSCtlPort(vsctl_bridge_parent, ovsrec_port)
320         vsctl_bridge_parent.ports.add(vsctl_port)
321         self.ports[ovsrec_port.name] = vsctl_port
322         return vsctl_port
323
324     def del_cached_port(self, vsctl_port):
325         assert not vsctl_port.ifaces
326         vsctl_port.bridge().ports.remove(vsctl_port)
327         vsctl_port.bridge = None
328         port = self.ports.pop(vsctl_port.port_cfg.name)
329         assert port == vsctl_port
330         vsctl_port.port_cfg.delete()
331
332     def add_iface_to_cache(self, vsctl_port_parent, ovsrec_iface):
333         vsctl_iface = VSCtlIface(vsctl_port_parent, ovsrec_iface)
334         vsctl_port_parent.ifaces.add(vsctl_iface)
335         self.ifaces[ovsrec_iface.name] = vsctl_iface
336
337     def add_qos_to_cache(self, vsctl_port_parent, ovsrec_qos):
338         vsctl_qos = VSCtlQoS(vsctl_port_parent, ovsrec_qos)
339         vsctl_port_parent.qos = vsctl_qos
340         return vsctl_qos
341
342     def add_queue_to_cache(self, vsctl_qos_parent, ovsrec_queue):
343         vsctl_queue = VSCtlQueue(vsctl_qos_parent, ovsrec_queue)
344         vsctl_qos_parent.queues.add(vsctl_queue)
345
346     def del_cached_iface(self, vsctl_iface):
347         vsctl_iface.port().ifaces.remove(vsctl_iface)
348         vsctl_iface.port = None
349         del self.ifaces[vsctl_iface.iface_cfg.name]
350         vsctl_iface.iface_cfg.delete()
351
352     def invalidate_cache(self):
353         if not self.cache_valid:
354             return
355         self._invalidate_cache()
356
357     def populate_cache(self):
358         self._populate_cache(self.idl.tables[vswitch_idl.OVSREC_TABLE_BRIDGE])
359
360     @staticmethod
361     def port_is_fake_bridge(ovsrec_port):
362         tag = ovsrec_port.tag
363         if isinstance(tag, list):
364             if len(tag) == 0:
365                 tag = 0
366             else:
367                 tag = tag[0]
368         return ovsrec_port.fake_bridge and 0 <= tag <= 4095
369
370     def _populate_cache(self, ovsrec_bridges):
371         if self.cache_valid:
372             return
373         self.cache_valid = True
374
375         bridges = set()
376         ports = set()
377         for ovsrec_bridge in ovsrec_bridges.rows.values():
378             name = ovsrec_bridge.name
379             if name in bridges:
380                 LOG.warning('%s: database contains duplicate bridge name',
381                             name)
382             bridges.add(name)
383             vsctl_bridge = self.add_bridge_to_cache(ovsrec_bridge, name,
384                                                     None, 0)
385             if not vsctl_bridge:
386                 continue
387             for ovsrec_port in ovsrec_bridge.ports:
388                 port_name = ovsrec_port.name
389                 if port_name in ports:
390                     # Duplicate ovsrec_port name.
391                     # (We will warn about that later.)
392                     continue
393                 ports.add(port_name)
394                 if (self.port_is_fake_bridge(ovsrec_port) and
395                         port_name not in bridges):
396                     bridges.add(port_name)
397                     self.add_bridge_to_cache(None, port_name, vsctl_bridge,
398                                              ovsrec_port.tag)
399
400         bridges = set()
401         for ovsrec_bridge in ovsrec_bridges.rows.values():
402             name = ovsrec_bridge.name
403             if name in bridges:
404                 continue
405             bridges.add(name)
406             vsctl_bridge = self.bridges[name]
407             for ovsrec_port in ovsrec_bridge.ports:
408                 port_name = ovsrec_port.name
409                 vsctl_port = self.ports.get(port_name)
410                 if vsctl_port:
411                     if ovsrec_port == vsctl_port.port_cfg:
412                         LOG.warning('%s: vsctl_port is in multiple bridges '
413                                     '(%s and %s)',
414                                     port_name, vsctl_bridge.name,
415                                     vsctl_port.br.name)
416                     else:
417                         LOG.error('%s: database contains duplicate '
418                                   'vsctl_port name',
419                                   ovsrec_port.name)
420                     continue
421
422                 if (self.port_is_fake_bridge(ovsrec_port) and
423                         port_name in bridges):
424                     continue
425
426                 # LOG.debug('ovsrec_port %s %s %s',
427                 #           ovsrec_port, ovsrec_port._data, ovsrec_port.tag)
428                 vsctl_port = self.add_port_to_cache(vsctl_bridge, ovsrec_port)
429                 # LOG.debug('vsctl_port %s', vsctl_port)
430                 for ovsrec_iface in ovsrec_port.interfaces:
431                     iface = self.ifaces.get(ovsrec_iface.name)
432                     if iface:
433                         if ovsrec_iface == iface.iface_cfg:
434                             LOG.warning(
435                                 '%s: interface is in multiple ports '
436                                 '(%s and %s)',
437                                 ovsrec_iface.name,
438                                 iface.port().port_cfg.name,
439                                 vsctl_port.port_cfg.name)
440                         else:
441                             LOG.error(
442                                 '%s: database contains duplicate interface '
443                                 'name',
444                                 ovsrec_iface.name)
445                         continue
446                     self.add_iface_to_cache(vsctl_port, ovsrec_iface)
447                 ovsrec_qos = ovsrec_port.qos
448                 vsctl_qos = self.add_qos_to_cache(vsctl_port, ovsrec_qos)
449                 if len(ovsrec_qos):
450                     for ovsrec_queue in ovsrec_qos[0].queues:
451                         self.add_queue_to_cache(vsctl_qos, ovsrec_queue)
452
453     def check_conflicts(self, name, msg):
454         self.verify_ports()
455         if name in self.bridges:
456             vsctl_fatal('%s because a bridge named %s already exists' %
457                         (msg, name))
458         if name in self.ports:
459             vsctl_fatal('%s because a port named %s already exists on '
460                         'bridge %s' %
461                         (msg, name, self.ports[name].bridge().name))
462         if name in self.ifaces:
463             vsctl_fatal('%s because an interface named %s already '
464                         'exists on bridge %s' %
465                         (msg, name, self.ifaces[name].port().bridge().name))
466
467     def find_bridge(self, name, must_exist):
468         assert self.cache_valid
469         vsctl_bridge = self.bridges.get(name)
470         if must_exist and not vsctl_bridge:
471             vsctl_fatal('no bridge named %s' % name)
472         self.verify_bridges()
473         return vsctl_bridge
474
475     def find_real_bridge(self, name, must_exist):
476         vsctl_bridge = self.find_bridge(name, must_exist)
477         if vsctl_bridge and vsctl_bridge.parent:
478             vsctl_fatal('%s is a fake bridge' % name)
479         return vsctl_bridge
480
481     def find_bridge_by_id(self, datapath_id, must_exist):
482         assert self.cache_valid
483         for vsctl_bridge in self.bridges.values():
484             if vsctl_bridge.br_cfg.datapath_id[0].strip('"') == datapath_id:
485                 self.verify_bridges()
486                 return vsctl_bridge
487
488         if must_exist:
489             vsctl_fatal('no bridge id %s' % datapath_id)
490         return None
491
492     def find_port(self, name, must_exist):
493         assert self.cache_valid
494         vsctl_port = self.ports.get(name)
495         if vsctl_port and name == vsctl_port.bridge().name:
496             vsctl_port = None
497         if must_exist and not vsctl_port:
498             vsctl_fatal('no vsctl_port named %s' % name)
499         return vsctl_port
500
501     def find_iface(self, name, must_exist):
502         assert self.cache_valid
503         vsctl_iface = self.ifaces.get(name)
504         if vsctl_iface and name == vsctl_iface.port().bridge().name:
505             vsctl_iface = None
506         if must_exist and not vsctl_iface:
507             vsctl_fatal('no interface named %s' % name)
508         self.verify_ports()
509         return vsctl_iface
510
511     def set_qos(self, vsctl_port, type, max_rate):
512         qos = vsctl_port.qos.qos_cfg
513         if not len(qos):
514             ovsrec_qos = self.txn.insert(
515                 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QOS])
516             vsctl_port.port_cfg.qos = [ovsrec_qos]
517         else:
518             ovsrec_qos = qos[0]
519         ovsrec_qos.type = type
520         if max_rate is not None:
521             value_json = ['map', [['max-rate', max_rate]]]
522             self.set_column(ovsrec_qos, 'other_config', value_json)
523         self.add_qos_to_cache(vsctl_port, [ovsrec_qos])
524         return ovsrec_qos
525
526     def set_queue(self, vsctl_qos, max_rate, min_rate,
527                   queue_id):
528
529         ovsrec_qos = vsctl_qos.qos_cfg[0]
530         try:
531             ovsrec_queue = ovsrec_qos.queues[queue_id]
532         except (AttributeError, KeyError):
533             ovsrec_queue = self.txn.insert(
534                 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QUEUE])
535         if max_rate is not None:
536             value_json = ['map', [['max-rate', max_rate]]]
537             self.add_column(ovsrec_queue, 'other_config', value_json)
538         if min_rate is not None:
539             value_json = ['map', [['min-rate', min_rate]]]
540             self.add_column(ovsrec_queue, 'other_config', value_json)
541         value_json = ['map', [[queue_id, ['uuid', str(ovsrec_queue.uuid)]]]]
542         self.add_column(ovsrec_qos, 'queues', value_json)
543         self.add_queue_to_cache(vsctl_qos, ovsrec_queue)
544         return ovsrec_queue
545
546     @staticmethod
547     def _column_set(ovsrec_row, column, ovsrec_value):
548         # need to trigger Row.__setattr__()
549         setattr(ovsrec_row, column, ovsrec_value)
550
551     @staticmethod
552     def _column_insert(ovsrec_row, column, ovsrec_add):
553         value = getattr(ovsrec_row, column)
554         value.append(ovsrec_add)
555         VSCtlContext._column_set(ovsrec_row, column, value)
556
557     @staticmethod
558     def _column_delete(ovsrec_row, column, ovsrec_del):
559         value = getattr(ovsrec_row, column)
560         try:
561             value.remove(ovsrec_del)
562         except ValueError:
563             # Datum.to_python() with _uuid_to_row trims down deleted
564             # references. If ovsrec_del.delete() is called before
565             # _column_delete(), value doesn't include ovsrec_del.
566             pass
567
568         VSCtlContext._column_set(ovsrec_row, column, value)
569
570     @staticmethod
571     def bridge_insert_port(ovsrec_bridge, ovsrec_port):
572         VSCtlContext._column_insert(ovsrec_bridge,
573                                     vswitch_idl.OVSREC_BRIDGE_COL_PORTS,
574                                     ovsrec_port)
575
576     @staticmethod
577     def bridge_delete_port(ovsrec_bridge, ovsrec_port):
578         VSCtlContext._column_delete(ovsrec_bridge,
579                                     vswitch_idl.OVSREC_BRIDGE_COL_PORTS,
580                                     ovsrec_port)
581
582     @staticmethod
583     def port_delete_qos(ovsrec_port, ovsrec_qos):
584         VSCtlContext._column_delete(ovsrec_port,
585                                     vswitch_idl.OVSREC_PORT_COL_QOS,
586                                     ovsrec_qos)
587
588     def ovs_insert_bridge(self, ovsrec_bridge):
589         self._column_insert(self.ovs,
590                             vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES,
591                             ovsrec_bridge)
592
593     def ovs_delete_bridge(self, ovsrec_bridge):
594         self._column_delete(self.ovs,
595                             vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES,
596                             ovsrec_bridge)
597
598     def del_port(self, vsctl_port):
599         if vsctl_port.bridge().parent:
600             ovsrec_bridge = vsctl_port.bridge().parent.br_cfg
601         else:
602             ovsrec_bridge = vsctl_port.bridge().br_cfg
603         self.bridge_delete_port(ovsrec_bridge, vsctl_port.port_cfg)
604
605         for vsctl_iface in vsctl_port.ifaces.copy():
606             self.del_cached_iface(vsctl_iface)
607         self.del_cached_port(vsctl_port)
608
609     def del_bridge(self, vsctl_bridge):
610         for child in vsctl_bridge.children.copy():
611             self.del_bridge(child)
612         for vsctl_port in vsctl_bridge.ports.copy():
613             self.del_port(vsctl_port)
614         self.del_cached_bridge(vsctl_bridge)
615
616     def del_qos(self, vsctl_qos):
617         ovsrec_port = vsctl_qos.port().port_cfg
618         ovsrec_qos = vsctl_qos.qos_cfg
619         if len(ovsrec_qos):
620             self.port_delete_qos(ovsrec_port, ovsrec_qos[0])
621             self.del_cached_qos(vsctl_qos)
622
623     def add_port(self, br_name, port_name, may_exist, fake_iface,
624                  iface_names, settings=None):
625         """
626         :type settings: list of (column, value_json)
627                                 where column is str,
628                                       value_json is json that is represented
629                                       by Datum.to_json()
630         """
631         settings = settings or []
632
633         self.populate_cache()
634         if may_exist:
635             vsctl_port = self.find_port(port_name, False)
636             if vsctl_port:
637                 want_names = set(iface_names)
638                 have_names = set(ovsrec_iface.name for ovsrec_iface in
639                                  vsctl_port.port_cfg.interfaces)
640                 if vsctl_port.bridge().name != br_name:
641                     vsctl_fatal('"%s" but %s is actually attached to '
642                                 'vsctl_bridge %s' %
643                                 (br_name, port_name, vsctl_port.bridge().name))
644                 if want_names != have_names:
645                     want_names_string = ','.join(want_names)
646                     have_names_string = ','.join(have_names)
647                     vsctl_fatal('"%s" but %s actually has interface(s) %s' %
648                                 (want_names_string,
649                                  port_name, have_names_string))
650                 return
651         self.check_conflicts(port_name,
652                              'cannot create a port named %s' % port_name)
653         for iface_name in iface_names:
654             self.check_conflicts(
655                 iface_name, 'cannot create an interface named %s' % iface_name)
656
657         vsctl_bridge = self.find_bridge(br_name, True)
658         ifaces = []
659         for iface_name in iface_names:
660             ovsrec_iface = self.txn.insert(
661                 self.idl.tables[vswitch_idl.OVSREC_TABLE_INTERFACE])
662             ovsrec_iface.name = iface_name
663             ifaces.append(ovsrec_iface)
664
665         ovsrec_port = self.txn.insert(
666             self.idl.tables[vswitch_idl.OVSREC_TABLE_PORT])
667         ovsrec_port.name = port_name
668         ovsrec_port.interfaces = ifaces
669         ovsrec_port.bond_fake_iface = fake_iface
670
671         if vsctl_bridge.parent:
672             tag = vsctl_bridge.vlan
673             ovsrec_port.tag = tag
674         for column, value in settings:
675             # TODO:XXX self.symtab:
676             self.set_column(ovsrec_port, column, value)
677
678         if vsctl_bridge.parent:
679             ovsrec_bridge = vsctl_bridge.parent.br_cfg
680         else:
681             ovsrec_bridge = vsctl_bridge.br_cfg
682         self.bridge_insert_port(ovsrec_bridge, ovsrec_port)
683         vsctl_port = self.add_port_to_cache(vsctl_bridge, ovsrec_port)
684         for ovsrec_iface in ifaces:
685             self.add_iface_to_cache(vsctl_port, ovsrec_iface)
686
687     def add_bridge(self, br_name, parent_name=None, vlan=0, may_exist=False):
688         self.populate_cache()
689         if may_exist:
690             vsctl_bridge = self.find_bridge(br_name, False)
691             if vsctl_bridge:
692                 if not parent_name:
693                     if vsctl_bridge.parent:
694                         vsctl_fatal('"--may-exist add-vsctl_bridge %s" '
695                                     'but %s is a VLAN bridge for VLAN %d' %
696                                     (br_name, br_name, vsctl_bridge.vlan))
697                 else:
698                     if not vsctl_bridge.parent:
699                         vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" '
700                                     'but %s is not a VLAN bridge' %
701                                     (br_name, parent_name, vlan, br_name))
702                     elif vsctl_bridge.parent.name != parent_name:
703                         vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" '
704                                     'but %s has the wrong parent %s' %
705                                     (br_name, parent_name, vlan,
706                                      br_name, vsctl_bridge.parent.name))
707                     elif vsctl_bridge.vlan != vlan:
708                         vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" '
709                                     'but %s is a VLAN bridge for the wrong '
710                                     'VLAN %d' %
711                                     (br_name, parent_name, vlan, br_name,
712                                      vsctl_bridge.vlan))
713                 return
714
715         self.check_conflicts(br_name,
716                              'cannot create a bridge named %s' % br_name)
717
718         txn = self.txn
719         tables = self.idl.tables
720         if not parent_name:
721             ovsrec_iface = txn.insert(
722                 tables[vswitch_idl.OVSREC_TABLE_INTERFACE])
723             ovsrec_iface.name = br_name
724             ovsrec_iface.type = 'internal'
725
726             ovsrec_port = txn.insert(tables[vswitch_idl.OVSREC_TABLE_PORT])
727             ovsrec_port.name = br_name
728             ovsrec_port.interfaces = [ovsrec_iface]
729             ovsrec_port.fake_bridge = False
730
731             ovsrec_bridge = txn.insert(tables[vswitch_idl.OVSREC_TABLE_BRIDGE])
732             ovsrec_bridge.name = br_name
733             ovsrec_bridge.ports = [ovsrec_port]
734
735             self.ovs_insert_bridge(ovsrec_bridge)
736         else:
737             parent = self.find_bridge(parent_name, False)
738             if parent and parent.parent:
739                 vsctl_fatal('cannot create bridge with fake bridge as parent')
740             if not parent:
741                 vsctl_fatal('parent bridge %s does not exist' % parent_name)
742
743             ovsrec_iface = txn.insert(
744                 tables[vswitch_idl.OVSREC_TABLE_INTERFACE])
745             ovsrec_iface.name = br_name
746             ovsrec_iface.type = 'internal'
747
748             ovsrec_port = txn.insert(tables[vswitch_idl.OVSREC_TABLE_PORT])
749             ovsrec_port.name = br_name
750             ovsrec_port.interfaces = [ovsrec_iface]
751             ovsrec_port.fake_bridge = True
752             ovsrec_port.tag = vlan
753
754             self.bridge_insert_port(parent.br_cfg, ovsrec_port)
755
756         self.invalidate_cache()
757
758     @staticmethod
759     def parse_column_key(setting_string):
760         """
761         Parses 'setting_string' as str formatted in <column>[:<key>]
762         and returns str type 'column' and 'key'
763         """
764         if ':' in setting_string:
765             # splits <column>:<key> into <column> and <key>
766             column, key = setting_string.split(':', 1)
767         else:
768             # stores <column> and <value>=None
769             column = setting_string
770             key = None
771
772         return column, key
773
774     @staticmethod
775     def parse_column_key_value(table_schema, setting_string):
776         """
777         Parses 'setting_string' as str formatted in <column>[:<key>]=<value>
778         and returns str type 'column' and json formatted 'value'
779         """
780         if ':' in setting_string:
781             # splits <column>:<key>=<value> into <column> and <key>=<value>
782             column, value = setting_string.split(':', 1)
783         elif '=' in setting_string:
784             # splits <column>=<value> into <column> and <value>
785             column, value = setting_string.split('=', 1)
786         else:
787             # stores <column> and <value>=None
788             column = setting_string
789             value = None
790
791         if value is not None:
792             type_ = table_schema.columns[column].type
793             value = datum_from_string(type_, value)
794
795         return column, value
796
797     def get_column(self, ovsrec_row, column, key=None, if_exists=False):
798         value = getattr(ovsrec_row, column, None)
799         if isinstance(value, dict) and key is not None:
800             value = value.get(key, None)
801             column = '%s:%s' % (column, key)
802
803         if value is None:
804             if if_exists:
805                 return None
806             vsctl_fatal('%s does not contain a column whose name matches "%s"'
807                         % (ovsrec_row._table.name, column))
808
809         return value
810
811     def _pre_mod_column(self, ovsrec_row, column, value_json):
812         if column not in ovsrec_row._table.columns:
813             vsctl_fatal('%s does not contain a column whose name matches "%s"'
814                         % (ovsrec_row._table.name, column))
815
816         column_schema = ovsrec_row._table.columns[column]
817         datum = ovs.db.data.Datum.from_json(
818             column_schema.type, value_json, self.symtab)
819         return datum.to_python(ovs.db.idl._uuid_to_row)
820
821     def set_column(self, ovsrec_row, column, value_json):
822         column_schema = ovsrec_row._table.columns[column]
823         datum = self._pre_mod_column(ovsrec_row, column, value_json)
824
825         if column_schema.type.is_map():
826             values = getattr(ovsrec_row, column, {})
827             values.update(datum)
828         else:
829             values = datum
830
831         setattr(ovsrec_row, column, values)
832
833     def add_column(self, ovsrec_row, column, value_json):
834         column_schema = ovsrec_row._table.columns[column]
835         datum = self._pre_mod_column(ovsrec_row, column, value_json)
836
837         if column_schema.type.is_map():
838             values = getattr(ovsrec_row, column, {})
839             values.update(datum)
840         elif column_schema.type.is_set():
841             values = getattr(ovsrec_row, column, [])
842             values.extend(datum)
843         else:
844             values = datum
845
846         setattr(ovsrec_row, column, values)
847
848     def remove_column(self, ovsrec_row, column, value_json):
849         column_schema = ovsrec_row._table.columns[column]
850         datum = self._pre_mod_column(ovsrec_row, column, value_json)
851
852         if column_schema.type.is_map():
853             values = getattr(ovsrec_row, column, {})
854             for datum_key, datum_value in datum.items():
855                 v = values.get(datum_key, None)
856                 if v == datum_value:
857                     values.pop(datum_key)
858             setattr(ovsrec_row, column, values)
859         elif column_schema.type.is_set():
860             values = getattr(ovsrec_row, column, [])
861             for d in datum:
862                 if d in values:
863                     values.remove(d)
864             setattr(ovsrec_row, column, values)
865         else:
866             values = getattr(ovsrec_row, column, None)
867             default = ovs.db.data.Datum.default(column_schema.type)
868             default = default.to_python(ovs.db.idl._uuid_to_row).to_json()
869             if values == datum:
870                 setattr(ovsrec_row, column, default)
871
872     def _get_row_by_id(self, table_name, vsctl_row_id, record_id):
873         if not vsctl_row_id.table:
874             return None
875
876         if not vsctl_row_id.name_column:
877             if record_id != '.':
878                 return None
879             values = list(self.idl.tables[vsctl_row_id.table].rows.values())
880             if not values or len(values) > 2:
881                 return None
882             referrer = values[0]
883         else:
884             referrer = None
885             for ovsrec_row in self.idl.tables[
886                     vsctl_row_id.table].rows.values():
887                 name = getattr(ovsrec_row, vsctl_row_id.name_column)
888                 assert isinstance(name, (list, str, six.text_type))
889                 if not isinstance(name, list) and name == record_id:
890                     if referrer:
891                         vsctl_fatal('multiple rows in %s match "%s"' %
892                                     (table_name, record_id))
893                     referrer = ovsrec_row
894
895         if not referrer:
896             return None
897
898         final = None
899         if vsctl_row_id.uuid_column:
900             referrer.verify(vsctl_row_id.uuid_column)
901             uuid = getattr(referrer, vsctl_row_id.uuid_column)
902
903             uuid_ = referrer._data[vsctl_row_id.uuid_column]
904             assert uuid_.type.key.type == ovs.db.types.UuidType
905             assert uuid_.type.value is None
906             assert isinstance(uuid, list)
907
908             if len(uuid) == 1:
909                 final = uuid[0]
910         else:
911             final = referrer
912
913         return final
914
915     def get_row(self, vsctl_table, record_id):
916         table_name = vsctl_table.table_name
917         if ovsuuid.is_valid_string(record_id):
918             uuid = ovsuuid.from_string(record_id)
919             return self.idl.tables[table_name].rows.get(uuid)
920         else:
921             for vsctl_row_id in vsctl_table.row_ids:
922                 ovsrec_row = self._get_row_by_id(table_name, vsctl_row_id,
923                                                  record_id)
924                 if ovsrec_row:
925                     return ovsrec_row
926
927         return None
928
929     def must_get_row(self, vsctl_table, record_id):
930         ovsrec_row = self.get_row(vsctl_table, record_id)
931         if not ovsrec_row:
932             vsctl_fatal('no row "%s" in table %s' % (record_id,
933                                                      vsctl_table.table_name))
934         return ovsrec_row
935
936
937 class _CmdShowTable(object):
938
939     def __init__(self, table, name_column, columns, recurse):
940         super(_CmdShowTable, self).__init__()
941         self.table = table
942         self.name_column = name_column
943         self.columns = columns
944         self.recurse = recurse
945
946
947 class _VSCtlRowID(object):
948
949     def __init__(self, table, name_column, uuid_column):
950         super(_VSCtlRowID, self).__init__()
951         self.table = table
952         self.name_column = name_column
953         self.uuid_column = uuid_column
954
955
956 class _VSCtlTable(object):
957
958     def __init__(self, table_name, vsctl_row_id_list):
959         super(_VSCtlTable, self).__init__()
960         self.table_name = table_name
961         self.row_ids = vsctl_row_id_list
962
963
964 class VSCtlCommand(StringifyMixin):
965     """
966     Class to describe artgumens similar to those of ``ovs-vsctl`` command.
967
968     ``command`` specifies the command of ``ovs-vsctl``.
969
970     ``args`` specifies a list or tuple of arguments for the given command.
971
972     ``options`` specifies a list or tuple of options for the given command.
973     Please note that NOT all options of ``ovs-vsctl`` are supported.
974     For example, ``--id`` option is not yet supported.
975     This class supports the followings.
976
977     ================= =========================================================
978     Option            Description
979     ================= =========================================================
980     ``--may-exist``   Does nothing when the given port already exists.
981                       The supported commands are ``add-port`` and
982                       ``add-bond``.
983     ``--fake-iface``  Creates a port as a fake interface.
984                       The supported command is ``add-bond``.
985     ``--must-exist``  Raises exception if the given port does not exist.
986                       The supported command is ``del-port``.
987     ``--with-iface``  Takes effect to the interface which has the same name.
988                       The supported command is ``del-port``.
989     ``--if-exists``   Ignores exception when not found.
990                       The supported command is ``get``.
991     ================= =========================================================
992     """
993
994     def __init__(self, command, args=None, options=None):
995         super(VSCtlCommand, self).__init__()
996         self.command = command
997         self.args = args or []
998         self.options = options or []
999
1000         # Data modified by commands
1001         self.result = None
1002
1003         # internally used by VSCtl
1004         self._prerequisite = None
1005         self._run = None
1006
1007     def has_option(self, option):
1008         return option in self.options
1009
1010
1011 class VSCtl(object):
1012     """
1013     A class to describe an Open vSwitch instance.
1014
1015     ``remote`` specifies the address of the OVS instance.
1016     :py:mod:`ryu.lib.ovs.vsctl.valid_ovsdb_addr` is a convenient function to
1017     validate this address.
1018     """
1019
1020     def _reset(self):
1021         self.schema_helper = None
1022         self.ovs = None
1023         self.txn = None
1024         self.wait_for_reload = True
1025         self.dry_run = False
1026
1027     def __init__(self, remote):
1028         super(VSCtl, self).__init__()
1029         self.remote = remote
1030
1031         self.schema_json = None
1032         self.schema = None
1033         self.schema_helper = None
1034         self.ovs = None
1035         self.txn = None
1036         self.wait_for_reload = True
1037         self.dry_run = False
1038
1039     def _rpc_get_schema_json(self, database):
1040         LOG.debug('remote %s', self.remote)
1041         error, stream_ = stream.Stream.open_block(
1042             stream.Stream.open(self.remote))
1043         if error:
1044             vsctl_fatal('error %s' % os.strerror(error))
1045         rpc = jsonrpc.Connection(stream_)
1046         request = jsonrpc.Message.create_request('get_schema', [database])
1047         error, reply = rpc.transact_block(request)
1048         rpc.close()
1049
1050         if error:
1051             vsctl_fatal(os.strerror(error))
1052         elif reply.error:
1053             vsctl_fatal('error %s' % reply.error)
1054         return reply.result
1055
1056     def _init_schema_helper(self):
1057         if self.schema_json is None:
1058             self.schema_json = self._rpc_get_schema_json(
1059                 vswitch_idl.OVSREC_DB_NAME)
1060             schema_helper = idl.SchemaHelper(None, self.schema_json)
1061             schema_helper.register_all()
1062             self.schema = schema_helper.get_idl_schema()
1063         # LOG.debug('schema_json %s', schema_json)
1064         self.schema_helper = idl.SchemaHelper(None, self.schema_json)
1065
1066     @staticmethod
1067     def _idl_block(idl_):
1068         poller = ovs.poller.Poller()
1069         idl_.wait(poller)
1070         poller.block()
1071
1072     @staticmethod
1073     def _idl_wait(idl_, seqno):
1074         while idl_.change_seqno == seqno and not idl_.run():
1075             VSCtl._idl_block(idl_)
1076
1077     def _run_prerequisites(self, commands):
1078         schema_helper = self.schema_helper
1079         schema_helper.register_table(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH)
1080         if self.wait_for_reload:
1081             # LOG.debug('schema_helper._tables %s', schema_helper._tables)
1082             schema_helper.register_columns(
1083                 vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH,
1084                 [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_CUR_CFG])
1085
1086         for command in commands:
1087             if not command._prerequisite:
1088                 continue
1089             ctx = VSCtlContext(None, None, None)
1090             command._prerequisite(ctx, command)
1091             ctx.done()
1092
1093     def _do_vsctl(self, idl_, commands):
1094         self.txn = idl.Transaction(idl_)
1095         if self.dry_run:
1096             self.txn.dry_run = True
1097
1098         self.txn.add_comment('ovs-vsctl')  # TODO:XXX add operation name. args
1099         ovs_rows = idl_.tables[vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH].rows
1100         if ovs_rows:
1101             ovs_ = list(ovs_rows.values())[0]
1102         else:
1103             # XXX add verification that table is empty
1104             ovs_ = self.txn.insert(
1105                 idl_.tables[vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH])
1106
1107         if self.wait_for_reload:
1108             ovs_.increment(vswitch_idl.OVSREC_OPEN_VSWITCH_COL_NEXT_CFG)
1109
1110         # TODO:XXX
1111         # symtab = ovsdb_symbol_table_create()
1112         ctx = VSCtlContext(idl_, self.txn, ovs_)
1113         for command in commands:
1114             if not command._run:
1115                 continue
1116             command._run(ctx, command)
1117             if ctx.try_again:
1118                 return False
1119         LOG.debug('result:\n%s', [command.result for command in commands])
1120         ctx.done()
1121
1122         # TODO:XXX check if created symbols are really created, referenced.
1123
1124         status = self.txn.commit_block()
1125         next_cfg = 0
1126         if self.wait_for_reload and status == idl.Transaction.SUCCESS:
1127             next_cfg = self.txn.get_increment_new_value()
1128
1129         # TODO:XXX
1130         # if status in (idl.Transaction.UNCHANGED, idl.Transaction.SUCCESS):
1131         #     for command in commands:
1132         #         if not command.post_func:
1133         #             continue
1134         #         ctx = VSCtlContext(idl_, txn, self.ovs)
1135         #         command.post_func(ctx)
1136         #         ctx.done()
1137
1138         txn_ = self.txn
1139         self.txn = None
1140
1141         if status in (idl.Transaction.UNCOMMITTED, idl.Transaction.INCOMPLETE):
1142             not_reached()
1143         elif status == idl.Transaction.ABORTED:
1144             vsctl_fatal('transaction aborted')
1145         elif status == idl.Transaction.UNCHANGED:
1146             LOG.debug('unchanged')
1147         elif status == idl.Transaction.SUCCESS:
1148             LOG.debug('success')
1149         elif status == idl.Transaction.TRY_AGAIN:
1150             return False
1151         elif status == idl.Transaction.ERROR:
1152             vsctl_fatal('transaction error: %s' % txn_.get_error())
1153         elif status == idl.Transaction.NOT_LOCKED:
1154             vsctl_fatal('database not locked')
1155         else:
1156             not_reached()
1157
1158         if self.wait_for_reload and status != idl.Transaction.UNCHANGED:
1159             while True:
1160                 idl_.run()
1161                 if ovs_.cur_cfg >= next_cfg:
1162                     break
1163                 self._idl_block(idl_)
1164
1165         return True
1166
1167     def _do_main(self, commands):
1168         """
1169         :type commands: list of VSCtlCommand
1170         """
1171         self._reset()
1172         self._init_schema_helper()
1173         self._run_prerequisites(commands)
1174
1175         idl_ = idl.Idl(self.remote, self.schema_helper)
1176         seqno = idl_.change_seqno
1177         while True:
1178             self._idl_wait(idl_, seqno)
1179
1180             seqno = idl_.change_seqno
1181             if self._do_vsctl(idl_, commands):
1182                 break
1183
1184             if self.txn:
1185                 self.txn.abort()
1186                 self.txn = None
1187             # TODO:XXX
1188             # ovsdb_symbol_table_destroy(symtab)
1189
1190         idl_.close()
1191
1192     def _run_command(self, commands):
1193         """
1194         :type commands: list of VSCtlCommand
1195         """
1196         all_commands = {
1197             # Open vSwitch commands.
1198             'init': (None, self._cmd_init),
1199             'show': (self._pre_cmd_show, self._cmd_show),
1200             # 'emer-reset':
1201
1202             # Bridge commands.
1203             'add-br': (self._pre_add_br, self._cmd_add_br),
1204             'del-br': (self._pre_get_info, self._cmd_del_br),
1205             'list-br': (self._pre_get_info, self._cmd_list_br),
1206             'br-exists': (self._pre_get_info, self._cmd_br_exists),
1207             'br-to-vlan': (self._pre_get_info, self._cmd_br_to_vlan),
1208             'br-to-parent': (self._pre_get_info, self._cmd_br_to_parent),
1209             'br-set-external-id': (self._pre_cmd_br_set_external_id,
1210                                    self._cmd_br_set_external_id),
1211             'br-get-external-id': (self._pre_cmd_br_get_external_id,
1212                                    self._cmd_br_get_external_id),
1213
1214             # Port. commands
1215             'list-ports': (self._pre_get_info, self._cmd_list_ports),
1216             'add-port': (self._pre_cmd_add_port, self._cmd_add_port),
1217             'add-bond': (self._pre_cmd_add_bond, self._cmd_add_bond),
1218             'del-port': (self._pre_get_info, self._cmd_del_port),
1219             'port-to-br': (self._pre_get_info, self._cmd_port_to_br),
1220
1221             # Interface commands.
1222             'list-ifaces': (self._pre_get_info, self._cmd_list_ifaces),
1223             'iface-to-br': (self._pre_get_info, self._cmd_iface_to_br),
1224
1225             # Controller commands.
1226             'get-controller': (self._pre_controller, self._cmd_get_controller),
1227             'del-controller': (self._pre_controller, self._cmd_del_controller),
1228             'set-controller': (self._pre_controller, self._cmd_set_controller),
1229             'get-fail-mode': (self._pre_fail_mode, self._cmd_get_fail_mode),
1230             'del-fail-mode': (self._pre_fail_mode, self._cmd_del_fail_mode),
1231             'set-fail-mode': (self._pre_fail_mode, self._cmd_set_fail_mode),
1232
1233             # Manager commands.
1234             # 'get-manager':
1235             # 'del-manager':
1236             # 'set-manager':
1237
1238             # SSL commands.
1239             # 'get-ssl':
1240             # 'del-ssl':
1241             # 'set-ssl':
1242
1243             # Auto Attach commands.
1244             # 'add-aa-mapping':
1245             # 'del-aa-mapping':
1246             # 'get-aa-mapping':
1247
1248             # Switch commands.
1249             # 'emer-reset':
1250
1251             # Database commands.
1252             'list': (self._pre_cmd_list, self._cmd_list),
1253             'find': (self._pre_cmd_find, self._cmd_find),
1254             'get': (self._pre_cmd_get, self._cmd_get),
1255             'set': (self._pre_cmd_set, self._cmd_set),
1256             'add': (self._pre_cmd_add, self._cmd_add),
1257             'remove': (self._pre_cmd_remove, self._cmd_remove),
1258             'clear': (self._pre_cmd_clear, self._cmd_clear),
1259             # 'create':
1260             # 'destroy':
1261             # 'wait-until':
1262
1263             # Utility commands. (No corresponding command in ovs-vsctl)
1264             'set-qos': (self._pre_cmd_set_qos, self._cmd_set_qos),
1265             'set-queue': (self._pre_cmd_set_queue, self._cmd_set_queue),
1266             'del-qos': (self._pre_get_info, self._cmd_del_qos),
1267             # for quantum_adapter
1268             'list-ifaces-verbose': (self._pre_cmd_list_ifaces_verbose,
1269                                     self._cmd_list_ifaces_verbose),
1270         }
1271
1272         for command in commands:
1273             funcs = all_commands[command.command]
1274             command._prerequisite, command._run = funcs
1275         self._do_main(commands)
1276
1277     def run_command(self, commands, timeout_sec=None, exception=None):
1278         """
1279         Executes the given commands and sends OVSDB messages.
1280
1281         ``commands`` must be a list of
1282         :py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
1283
1284         If ``timeout_sec`` is specified, raises exception after the given
1285         timeout [sec]. Additionally, if ``exception`` is specified, this
1286         function will wraps exception using the given exception class.
1287
1288         Retruns ``None`` but fills ``result`` attribute for each command
1289         instance.
1290         """
1291         if timeout_sec is None:
1292             self._run_command(commands)
1293         else:
1294             with hub.Timeout(timeout_sec, exception):
1295                 self._run_command(commands)
1296
1297     # Open vSwitch commands:
1298
1299     def _cmd_init(self, _ctx, _command):
1300         # nothing. Just check connection to ovsdb
1301         pass
1302
1303     _CMD_SHOW_TABLES = [
1304         _CmdShowTable(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, None,
1305                       [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_MANAGER_OPTIONS,
1306                        vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES,
1307                        vswitch_idl.OVSREC_OPEN_VSWITCH_COL_OVS_VERSION],
1308                       False),
1309         _CmdShowTable(vswitch_idl.OVSREC_TABLE_BRIDGE,
1310                       vswitch_idl.OVSREC_BRIDGE_COL_NAME,
1311                       [vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER,
1312                        vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE,
1313                        vswitch_idl.OVSREC_BRIDGE_COL_PORTS],
1314                       False),
1315         _CmdShowTable(vswitch_idl.OVSREC_TABLE_PORT,
1316                       vswitch_idl.OVSREC_PORT_COL_NAME,
1317                       [vswitch_idl.OVSREC_PORT_COL_TAG,
1318                        vswitch_idl.OVSREC_PORT_COL_TRUNKS,
1319                        vswitch_idl.OVSREC_PORT_COL_INTERFACES],
1320                       False),
1321         _CmdShowTable(vswitch_idl.OVSREC_TABLE_INTERFACE,
1322                       vswitch_idl.OVSREC_INTERFACE_COL_NAME,
1323                       [vswitch_idl.OVSREC_INTERFACE_COL_TYPE,
1324                        vswitch_idl.OVSREC_INTERFACE_COL_OPTIONS],
1325                       False),
1326         _CmdShowTable(vswitch_idl.OVSREC_TABLE_CONTROLLER,
1327                       vswitch_idl.OVSREC_CONTROLLER_COL_TARGET,
1328                       [vswitch_idl.OVSREC_CONTROLLER_COL_IS_CONNECTED],
1329                       False),
1330         _CmdShowTable(vswitch_idl.OVSREC_TABLE_MANAGER,
1331                       vswitch_idl.OVSREC_MANAGER_COL_TARGET,
1332                       [vswitch_idl.OVSREC_MANAGER_COL_IS_CONNECTED],
1333                       False),
1334     ]
1335
1336     def _pre_cmd_show(self, _ctx, _command):
1337         schema_helper = self.schema_helper
1338         for show in self._CMD_SHOW_TABLES:
1339             schema_helper.register_table(show.table)
1340             if show.name_column:
1341                 schema_helper.register_columns(show.table, [show.name_column])
1342             schema_helper.register_columns(show.table, show.columns)
1343
1344     @staticmethod
1345     def _cmd_show_find_table_by_row(row):
1346         for show in VSCtl._CMD_SHOW_TABLES:
1347             if show.table == row._table.name:
1348                 return show
1349         return None
1350
1351     @staticmethod
1352     def _cmd_show_find_table_by_name(name):
1353         for show in VSCtl._CMD_SHOW_TABLES:
1354             if show.table == name:
1355                 return show
1356         return None
1357
1358     @staticmethod
1359     def _cmd_show_row(ctx, row, level):
1360         _INDENT_SIZE = 4  # # of spaces per indent
1361         show = VSCtl._cmd_show_find_table_by_row(row)
1362         output = ''
1363
1364         output += ' ' * level * _INDENT_SIZE
1365         if show and show.name_column:
1366             output += '%s ' % show.table
1367             datum = getattr(row, show.name_column)
1368             output += datum
1369         else:
1370             output += str(row.uuid)
1371         output += '\n'
1372
1373         if not show or show.recurse:
1374             return
1375
1376         show.recurse = True
1377         for column in show.columns:
1378             datum = row._data[column]
1379             key = datum.type.key
1380             if key.type == ovs.db.types.UuidType and key.ref_table_name:
1381                 ref_show = VSCtl._cmd_show_find_table_by_name(
1382                     key.ref_table_name)
1383                 if ref_show:
1384                     for atom in datum.values:
1385                         ref_row = ctx.idl.tables[ref_show.table].rows.get(
1386                             atom.value)
1387                         if ref_row:
1388                             VSCtl._cmd_show_row(ctx, ref_row, level + 1)
1389                     continue
1390
1391             if not datum.is_default():
1392                 output += ' ' * (level + 1) * _INDENT_SIZE
1393                 output += '%s: %s\n' % (column, datum)
1394
1395         show.recurse = False
1396         return output
1397
1398     def _cmd_show(self, ctx, command):
1399         for row in ctx.idl.tables[
1400                 self._CMD_SHOW_TABLES[0].table].rows.values():
1401             output = self._cmd_show_row(ctx, row, 0)
1402             command.result = output
1403
1404     # Bridge commands:
1405
1406     def _pre_get_info(self, _ctx, _command):
1407         schema_helper = self.schema_helper
1408
1409         schema_helper.register_columns(
1410             vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH,
1411             [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES])
1412         schema_helper.register_columns(
1413             vswitch_idl.OVSREC_TABLE_BRIDGE,
1414             [vswitch_idl.OVSREC_BRIDGE_COL_NAME,
1415              vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER,
1416              vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE,
1417              vswitch_idl.OVSREC_BRIDGE_COL_PORTS])
1418         schema_helper.register_columns(
1419             vswitch_idl.OVSREC_TABLE_PORT,
1420             [vswitch_idl.OVSREC_PORT_COL_NAME,
1421              vswitch_idl.OVSREC_PORT_COL_FAKE_BRIDGE,
1422              vswitch_idl.OVSREC_PORT_COL_TAG,
1423              vswitch_idl.OVSREC_PORT_COL_INTERFACES,
1424              vswitch_idl.OVSREC_PORT_COL_QOS])
1425         schema_helper.register_columns(
1426             vswitch_idl.OVSREC_TABLE_INTERFACE,
1427             [vswitch_idl.OVSREC_INTERFACE_COL_NAME])
1428         schema_helper.register_columns(
1429             vswitch_idl.OVSREC_TABLE_QOS,
1430             [vswitch_idl.OVSREC_QOS_COL_QUEUES])
1431         schema_helper.register_columns(
1432             vswitch_idl.OVSREC_TABLE_QUEUE,
1433             [])
1434
1435     def _cmd_list_br(self, ctx, command):
1436         ctx.populate_cache()
1437         command.result = sorted(ctx.bridges.keys())
1438
1439     def _pre_add_br(self, ctx, command):
1440         self._pre_get_info(ctx, command)
1441
1442         schema_helper = self.schema_helper
1443         schema_helper.register_columns(
1444             vswitch_idl.OVSREC_TABLE_INTERFACE,
1445             [vswitch_idl.OVSREC_INTERFACE_COL_TYPE])
1446
1447     def _cmd_add_br(self, ctx, command):
1448         br_name = command.args[0]
1449         parent_name = None
1450         vlan = 0
1451         if len(command.args) == 1:
1452             pass
1453         elif len(command.args) == 3:
1454             parent_name = command.args[1]
1455             vlan = int(command.args[2])
1456             if vlan < 0 or vlan > 4095:
1457                 vsctl_fatal("vlan must be between 0 and 4095 %d" % vlan)
1458         else:
1459             vsctl_fatal('this command takes exactly 1 or 3 argument')
1460
1461         ctx.add_bridge(br_name, parent_name, vlan)
1462
1463     def _del_br(self, ctx, br_name, must_exist=False):
1464         ctx.populate_cache()
1465         br = ctx.find_bridge(br_name, must_exist)
1466         if br:
1467             ctx.del_bridge(br)
1468
1469     def _cmd_del_br(self, ctx, command):
1470         br_name = command.args[0]
1471         self._del_br(ctx, br_name)
1472
1473     def _br_exists(self, ctx, br_name):
1474         ctx.populate_cache()
1475         br = ctx.find_bridge(br_name, must_exist=False)
1476         return br is not None
1477
1478     def _cmd_br_exists(self, ctx, command):
1479         br_name = command.args[0]
1480         command.result = self._br_exists(ctx, br_name)
1481
1482     def _br_to_vlan(self, ctx, br_name):
1483         ctx.populate_cache()
1484         br = ctx.find_bridge(br_name, must_exist=True)
1485         vlan = br.vlan
1486         if isinstance(vlan, list):
1487             if len(vlan) == 0:
1488                 vlan = 0
1489             else:
1490                 vlan = vlan[0]
1491         return vlan
1492
1493     def _cmd_br_to_vlan(self, ctx, command):
1494         br_name = command.args[0]
1495         command.result = self._br_to_vlan(ctx, br_name)
1496
1497     def _br_to_parent(self, ctx, br_name):
1498         ctx.populate_cache()
1499         br = ctx.find_bridge(br_name, must_exist=True)
1500         return br if br.parent is None else br.parent
1501
1502     def _cmd_br_to_parent(self, ctx, command):
1503         br_name = command.args[0]
1504         command.result = self._br_to_parent(ctx, br_name)
1505
1506     def _pre_cmd_br_set_external_id(self, ctx, _command):
1507         table_name = vswitch_idl.OVSREC_TABLE_BRIDGE
1508         columns = [vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS]
1509         self._pre_mod_columns(ctx, table_name, columns)
1510
1511     def _br_add_external_id(self, ctx, br_name, key, value):
1512         table_name = vswitch_idl.OVSREC_TABLE_BRIDGE
1513         column = vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS
1514         vsctl_table = self._get_table(table_name)
1515         ovsrec_row = ctx.must_get_row(vsctl_table, br_name)
1516
1517         value_json = ['map', [[key, value]]]
1518         ctx.add_column(ovsrec_row, column, value_json)
1519         ctx.invalidate_cache()
1520
1521     def _br_clear_external_id(self, ctx, br_name, key):
1522         table_name = vswitch_idl.OVSREC_TABLE_BRIDGE
1523         column = vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS
1524         vsctl_table = self._get_table(table_name)
1525         ovsrec_row = ctx.must_get_row(vsctl_table, br_name)
1526
1527         values = getattr(ovsrec_row, column, {})
1528         values.pop(key, None)
1529         setattr(ovsrec_row, column, values)
1530         ctx.invalidate_cache()
1531
1532     def _cmd_br_set_external_id(self, ctx, command):
1533         br_name = command.args[0]
1534         key = command.args[1]
1535         if len(command.args) > 2:
1536             self._br_add_external_id(ctx, br_name, key, command.args[2])
1537         else:
1538             self._br_clear_external_id(ctx, br_name, key)
1539
1540     def _pre_cmd_br_get_external_id(self, ctx, _command):
1541         table_name = vswitch_idl.OVSREC_TABLE_BRIDGE
1542         columns = [vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS]
1543         self._pre_get_columns(ctx, table_name, columns)
1544
1545     def _br_get_external_id_value(self, ctx, br_name, key):
1546         external_id = self._br_get_external_id_list(ctx, br_name)
1547
1548         return external_id.get(key, None)
1549
1550     def _br_get_external_id_list(self, ctx, br_name):
1551         table_name = vswitch_idl.OVSREC_TABLE_BRIDGE
1552         column = vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS
1553         vsctl_table = self._get_table(table_name)
1554         ovsrec_row = ctx.must_get_row(vsctl_table, br_name)
1555
1556         return ctx.get_column(ovsrec_row, column)
1557
1558     def _cmd_br_get_external_id(self, ctx, command):
1559         br_name = command.args[0]
1560         if len(command.args) > 1:
1561             command.result = self._br_get_external_id_value(ctx, br_name,
1562                                                             command.args[1])
1563         else:
1564             command.result = self._br_get_external_id_list(ctx, br_name)
1565
1566     # Port commands:
1567
1568     def _list_ports(self, ctx, br_name):
1569         ctx.populate_cache()
1570         br = ctx.find_bridge(br_name, True)
1571         if br.br_cfg:
1572             br.br_cfg.verify(vswitch_idl.OVSREC_BRIDGE_COL_PORTS)
1573         else:
1574             br.parent.br_cfg.verify(vswitch_idl.OVSREC_BRIDGE_COL_PORTS)
1575
1576         return [port.port_cfg.name for port in br.ports
1577                 if port.port_cfg.name != br.name]
1578
1579     def _cmd_list_ports(self, ctx, command):
1580         br_name = command.args[0]
1581         port_names = self._list_ports(ctx, br_name)
1582         command.result = sorted(port_names)
1583
1584     def _pre_add_port(self, _ctx, columns):
1585         schema_helper = self.schema_helper
1586         schema_helper.register_columns(
1587             vswitch_idl.OVSREC_TABLE_PORT,
1588             [vswitch_idl.OVSREC_PORT_COL_NAME,
1589              vswitch_idl.OVSREC_PORT_COL_BOND_FAKE_IFACE])
1590         schema_helper.register_columns(
1591             vswitch_idl.OVSREC_TABLE_PORT, columns)
1592
1593     def _pre_cmd_add_port(self, ctx, command):
1594         self._pre_get_info(ctx, command)
1595
1596         columns = [
1597             ctx.parse_column_key_value(
1598                 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)[0]
1599             for setting in command.args[2:]]
1600
1601         self._pre_add_port(ctx, columns)
1602
1603     def _pre_cmd_add_bond(self, ctx, command):
1604         self._pre_get_info(ctx, command)
1605
1606         if len(command.args) < 3:
1607             vsctl_fatal('this command requires at least 3 arguments')
1608
1609         columns = [
1610             ctx.parse_column_key_value(
1611                 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)[0]
1612             for setting in command.args[3:]]
1613
1614         self._pre_add_port(ctx, columns)
1615
1616     def _cmd_add_port(self, ctx, command):
1617         # '--may_exist' is a typo but for backword compatibility
1618         may_exist = (command.has_option('--may_exist')
1619                      or command.has_option('--may-exist'))
1620
1621         br_name = command.args[0]
1622         port_name = command.args[1]
1623         iface_names = [command.args[1]]
1624         settings = [
1625             ctx.parse_column_key_value(
1626                 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)
1627             for setting in command.args[2:]]
1628
1629         ctx.add_port(br_name, port_name, may_exist,
1630                      False, iface_names, settings)
1631
1632     def _cmd_add_bond(self, ctx, command):
1633         # '--may_exist' is a typo but for backword compatibility
1634         may_exist = (command.has_option('--may_exist')
1635                      or command.has_option('--may-exist'))
1636         fake_iface = command.has_option('--fake-iface')
1637
1638         br_name = command.args[0]
1639         port_name = command.args[1]
1640         iface_names = list(command.args[2])
1641         settings = [
1642             ctx.parse_column_key_value(
1643                 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)
1644             for setting in command.args[3:]]
1645
1646         ctx.add_port(br_name, port_name, may_exist, fake_iface,
1647                      iface_names, settings)
1648
1649     def _del_port(self, ctx, br_name=None, target=None,
1650                   must_exist=False, with_iface=False):
1651         assert target is not None
1652
1653         ctx.populate_cache()
1654         if not with_iface:
1655             vsctl_port = ctx.find_port(target, must_exist)
1656         else:
1657             vsctl_port = ctx.find_port(target, False)
1658             if not vsctl_port:
1659                 vsctl_iface = ctx.find_iface(target, False)
1660                 if vsctl_iface:
1661                     vsctl_port = vsctl_iface.port()
1662                 if must_exist and not vsctl_port:
1663                     vsctl_fatal('no port or interface named %s' % target)
1664
1665         if not vsctl_port:
1666             return
1667         if not br_name:
1668             vsctl_bridge = ctx.find_bridge(br_name, True)
1669             if vsctl_port.bridge() != vsctl_bridge:
1670                 if vsctl_port.bridge().parent == vsctl_bridge:
1671                     vsctl_fatal('bridge %s does not have a port %s (although '
1672                                 'its parent bridge %s does)' %
1673                                 (br_name, target, vsctl_bridge.parent.name))
1674                 else:
1675                     vsctl_fatal('bridge %s does not have a port %s' %
1676                                 (br_name, target))
1677
1678         ctx.del_port(vsctl_port)
1679
1680     def _cmd_del_port(self, ctx, command):
1681         must_exist = command.has_option('--must-exist')
1682         with_iface = command.has_option('--with-iface')
1683         target = command.args[-1]
1684         br_name = command.args[0] if len(command.args) == 2 else None
1685         self._del_port(ctx, br_name, target, must_exist, with_iface)
1686
1687     def _port_to_br(self, ctx, port_name):
1688         ctx.populate_cache()
1689         port = ctx.find_port(port_name, True)
1690         bridge = port.bridge()
1691         if bridge is None:
1692             vsctl_fatal('Bridge associated to port "%s" does not exist' %
1693                         port_name)
1694
1695         return bridge.name
1696
1697     def _cmd_port_to_br(self, ctx, command):
1698         iface_name = command.args[0]
1699         command.result = self._iface_to_br(ctx, iface_name)
1700
1701     # Interface commands:
1702
1703     def _list_ifaces(self, ctx, br_name):
1704         ctx.populate_cache()
1705
1706         br = ctx.find_bridge(br_name, True)
1707         ctx.verify_ports()
1708
1709         iface_names = set()
1710         for vsctl_port in br.ports:
1711             for vsctl_iface in vsctl_port.ifaces:
1712                 iface_name = vsctl_iface.iface_cfg.name
1713                 if iface_name != br_name:
1714                     iface_names.add(iface_name)
1715         return iface_names
1716
1717     def _cmd_list_ifaces(self, ctx, command):
1718         br_name = command.args[0]
1719         iface_names = self._list_ifaces(ctx, br_name)
1720         command.result = sorted(iface_names)
1721
1722     def _iface_to_br(self, ctx, iface_name):
1723         ctx.populate_cache()
1724         iface = ctx.find_iface(iface_name, True)
1725         port = iface.port()
1726         if port is None:
1727             vsctl_fatal('Port associated to iface "%s" does not exist' %
1728                         iface_name)
1729         bridge = port.bridge()
1730         if bridge is None:
1731             vsctl_fatal('Bridge associated to iface "%s" does not exist' %
1732                         iface_name)
1733
1734         return bridge.name
1735
1736     def _cmd_iface_to_br(self, ctx, command):
1737         iface_name = command.args[0]
1738         command.result = self._iface_to_br(ctx, iface_name)
1739
1740     # Utility commands for quantum_adapter:
1741
1742     def _pre_cmd_list_ifaces_verbose(self, ctx, command):
1743         self._pre_get_info(ctx, command)
1744         schema_helper = self.schema_helper
1745         schema_helper.register_columns(
1746             vswitch_idl.OVSREC_TABLE_BRIDGE,
1747             [vswitch_idl.OVSREC_BRIDGE_COL_DATAPATH_ID])
1748         schema_helper.register_columns(
1749             vswitch_idl.OVSREC_TABLE_INTERFACE,
1750             [vswitch_idl.OVSREC_INTERFACE_COL_TYPE,
1751              vswitch_idl.OVSREC_INTERFACE_COL_NAME,
1752              vswitch_idl.OVSREC_INTERFACE_COL_EXTERNAL_IDS,
1753              vswitch_idl.OVSREC_INTERFACE_COL_OPTIONS,
1754              vswitch_idl.OVSREC_INTERFACE_COL_OFPORT])
1755
1756     @staticmethod
1757     def _iface_to_dict(iface_cfg):
1758         _ATTRIBUTE = ['name', 'ofport', 'type', 'external_ids', 'options']
1759         attr = dict((key, getattr(iface_cfg, key)) for key in _ATTRIBUTE)
1760
1761         if attr['ofport']:
1762             attr['ofport'] = attr['ofport'][0]
1763         return attr
1764
1765     def _list_ifaces_verbose(self, ctx, datapath_id, port_name):
1766         ctx.populate_cache()
1767
1768         br = ctx.find_bridge_by_id(datapath_id, True)
1769         ctx.verify_ports()
1770
1771         iface_cfgs = []
1772         if port_name is None:
1773             for vsctl_port in br.ports:
1774                 iface_cfgs.extend(self._iface_to_dict(vsctl_iface.iface_cfg)
1775                                   for vsctl_iface in vsctl_port.ifaces)
1776         else:
1777             # When port is created, ofport column might be None.
1778             # So try with port name if it happended
1779             for vsctl_port in br.ports:
1780                 iface_cfgs.extend(
1781                     self._iface_to_dict(vsctl_iface.iface_cfg)
1782                     for vsctl_iface in vsctl_port.ifaces
1783                     if vsctl_iface.iface_cfg.name == port_name)
1784
1785         return iface_cfgs
1786
1787     def _cmd_list_ifaces_verbose(self, ctx, command):
1788         datapath_id = command.args[0]
1789         port_name = None
1790         if len(command.args) >= 2:
1791             port_name = command.args[1]
1792         LOG.debug('command.args %s', command.args)
1793         iface_cfgs = self._list_ifaces_verbose(ctx, datapath_id, port_name)
1794         command.result = sorted(iface_cfgs)
1795
1796     # Controller commands:
1797
1798     def _verify_controllers(self, ovsrec_bridge):
1799         ovsrec_bridge.verify(vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER)
1800         for controller in ovsrec_bridge.controller:
1801             controller.verify(vswitch_idl.OVSREC_CONTROLLER_COL_TARGET)
1802
1803     def _pre_controller(self, ctx, command):
1804         self._pre_get_info(ctx, command)
1805         self.schema_helper.register_columns(
1806             vswitch_idl.OVSREC_TABLE_CONTROLLER,
1807             [vswitch_idl.OVSREC_CONTROLLER_COL_TARGET])
1808
1809     def _get_controller(self, ctx, br_name):
1810         ctx.populate_cache()
1811         br = ctx.find_bridge(br_name, True)
1812         self._verify_controllers(br.br_cfg)
1813         return set(controller.target for controller in br.br_cfg.controller)
1814
1815     def _cmd_get_controller(self, ctx, command):
1816         br_name = command.args[0]
1817         controller_names = self._get_controller(ctx, br_name)
1818         command.result = sorted(controller_names)
1819
1820     def _delete_controllers(self, ovsrec_controllers):
1821         for controller in ovsrec_controllers:
1822             controller.delete()
1823
1824     def _del_controller(self, ctx, br_name):
1825         ctx.populate_cache()
1826         br = ctx.find_real_bridge(br_name, True)
1827         ovsrec_bridge = br.br_cfg
1828         self._verify_controllers(ovsrec_bridge)
1829         if ovsrec_bridge.controller:
1830             self._delete_controllers(ovsrec_bridge.controller)
1831             ovsrec_bridge.controller = []
1832
1833     def _cmd_del_controller(self, ctx, command):
1834         br_name = command.args[0]
1835         self._del_controller(ctx, br_name)
1836
1837     def _insert_controllers(self, controller_names):
1838         ovsrec_controllers = []
1839         for name in controller_names:
1840             # TODO: check if the name startswith() supported protocols
1841             ovsrec_controller = self.txn.insert(
1842                 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_CONTROLLER])
1843             ovsrec_controller.target = name
1844             ovsrec_controllers.append(ovsrec_controller)
1845         return ovsrec_controllers
1846
1847     def _insert_qos(self):
1848         ovsrec_qos = self.txn.insert(
1849             self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QOS])
1850
1851         return ovsrec_qos
1852
1853     def _set_controller(self, ctx, br_name, controller_names):
1854         ctx.populate_cache()
1855         ovsrec_bridge = ctx.find_real_bridge(br_name, True).br_cfg
1856         self._verify_controllers(ovsrec_bridge)
1857         self._delete_controllers(ovsrec_bridge.controller)
1858         controllers = self._insert_controllers(controller_names)
1859         ovsrec_bridge.controller = controllers
1860
1861     def _cmd_set_controller(self, ctx, command):
1862         br_name = command.args[0]
1863         controller_names = command.args[1:]
1864         self._set_controller(ctx, br_name, controller_names)
1865
1866     def _pre_fail_mode(self, ctx, command):
1867         self._pre_get_info(ctx, command)
1868         self.schema_helper.register_columns(
1869             vswitch_idl.OVSREC_TABLE_BRIDGE,
1870             [vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE])
1871
1872     def _get_fail_mode(self, ctx, br_name):
1873         ctx.populate_cache()
1874         br = ctx.find_bridge(br_name, True)
1875
1876         # Note: Returns first element of fail_mode column
1877         return getattr(br.br_cfg, vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE)[0]
1878
1879     def _cmd_get_fail_mode(self, ctx, command):
1880         br_name = command.args[0]
1881         command.result = self._get_fail_mode(ctx, br_name)
1882
1883     def _del_fail_mode(self, ctx, br_name):
1884         ctx.populate_cache()
1885         br = ctx.find_bridge(br_name, True)
1886         # Note: assuming that [] means empty
1887         setattr(br.br_cfg, vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, [])
1888         ctx.invalidate_cache()
1889
1890     def _cmd_del_fail_mode(self, ctx, command):
1891         br_name = command.args[0]
1892         self._del_fail_mode(ctx, br_name)
1893
1894     def _set_fail_mode(self, ctx, br_name, mode):
1895         ctx.populate_cache()
1896         br = ctx.find_bridge(br_name, True)
1897         setattr(br.br_cfg, vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, mode)
1898         ctx.invalidate_cache()
1899
1900     def _cmd_set_fail_mode(self, ctx, command):
1901         br_name = command.args[0]
1902         mode = command.args[1]
1903         if mode not in ('standalone', 'secure'):
1904             vsctl_fatal('fail-mode must be "standalone" or "secure"')
1905         self._set_fail_mode(ctx, br_name, mode)
1906
1907     # Utility commands:
1908
1909     def _del_qos(self, ctx, port_name):
1910         assert port_name is not None
1911
1912         ctx.populate_cache()
1913         vsctl_port = ctx.find_port(port_name, True)
1914         vsctl_qos = vsctl_port.qos
1915         ctx.del_qos(vsctl_qos)
1916
1917     def _cmd_del_qos(self, ctx, command):
1918         port_name = command.args[0]
1919         self._del_qos(ctx, port_name)
1920
1921     def _set_qos(self, ctx, port_name, type, max_rate):
1922         ctx.populate_cache()
1923         vsctl_port = ctx.find_port(port_name, True)
1924         ovsrec_qos = ctx.set_qos(vsctl_port, type, max_rate)
1925         return ovsrec_qos
1926
1927     def _cmd_set_qos(self, ctx, command):
1928         port_name = command.args[0]
1929         type = command.args[1]
1930         max_rate = command.args[2]
1931         result = self._set_qos(ctx, port_name, type, max_rate)
1932         command.result = [result]
1933
1934     def _pre_cmd_set_qos(self, ctx, command):
1935         self._pre_get_info(ctx, command)
1936         schema_helper = self.schema_helper
1937         schema_helper.register_columns(
1938             vswitch_idl.OVSREC_TABLE_QOS,
1939             [vswitch_idl.OVSREC_QOS_COL_EXTERNAL_IDS,
1940              vswitch_idl.OVSREC_QOS_COL_OTHER_CONFIG,
1941              vswitch_idl.OVSREC_QOS_COL_QUEUES,
1942              vswitch_idl.OVSREC_QOS_COL_TYPE])
1943
1944     def _cmd_set_queue(self, ctx, command):
1945         ctx.populate_cache()
1946         port_name = command.args[0]
1947         queues = command.args[1]
1948         vsctl_port = ctx.find_port(port_name, True)
1949         vsctl_qos = vsctl_port.qos
1950         queue_id = 0
1951         results = []
1952         for queue in queues:
1953             max_rate = queue.get('max-rate', None)
1954             min_rate = queue.get('min-rate', None)
1955             ovsrec_queue = ctx.set_queue(
1956                 vsctl_qos, max_rate, min_rate, queue_id)
1957             results.append(ovsrec_queue)
1958             queue_id += 1
1959         command.result = results
1960
1961     def _pre_cmd_set_queue(self, ctx, command):
1962         self._pre_get_info(ctx, command)
1963         schema_helper = self.schema_helper
1964         schema_helper.register_columns(
1965             vswitch_idl.OVSREC_TABLE_QUEUE,
1966             [vswitch_idl.OVSREC_QUEUE_COL_DSCP,
1967              vswitch_idl.OVSREC_QUEUE_COL_EXTERNAL_IDS,
1968              vswitch_idl.OVSREC_QUEUE_COL_OTHER_CONFIG])
1969
1970     # Database commands:
1971
1972     _TABLES = [
1973         _VSCtlTable(vswitch_idl.OVSREC_TABLE_BRIDGE,
1974                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE,
1975                                  vswitch_idl.OVSREC_BRIDGE_COL_NAME,
1976                                  None)]),
1977         _VSCtlTable(vswitch_idl.OVSREC_TABLE_CONTROLLER,
1978                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE,
1979                                  vswitch_idl.OVSREC_BRIDGE_COL_NAME,
1980                                  vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER)]),
1981         _VSCtlTable(vswitch_idl.OVSREC_TABLE_INTERFACE,
1982                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_INTERFACE,
1983                                  vswitch_idl.OVSREC_INTERFACE_COL_NAME,
1984                                  None)]),
1985         _VSCtlTable(vswitch_idl.OVSREC_TABLE_MIRROR,
1986                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_MIRROR,
1987                                  vswitch_idl.OVSREC_MIRROR_COL_NAME,
1988                                  None)]),
1989         _VSCtlTable(vswitch_idl.OVSREC_TABLE_MANAGER,
1990                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_MANAGER,
1991                                  vswitch_idl.OVSREC_MANAGER_COL_TARGET,
1992                                  None)]),
1993         _VSCtlTable(vswitch_idl.OVSREC_TABLE_NETFLOW,
1994                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE,
1995                                  vswitch_idl.OVSREC_BRIDGE_COL_NAME,
1996                                  vswitch_idl.OVSREC_BRIDGE_COL_NETFLOW)]),
1997         _VSCtlTable(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH,
1998                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH,
1999                                  None,
2000                                  None)]),
2001         _VSCtlTable(vswitch_idl.OVSREC_TABLE_PORT,
2002                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_PORT,
2003                                  vswitch_idl.OVSREC_PORT_COL_NAME,
2004                                  None)]),
2005         _VSCtlTable(vswitch_idl.OVSREC_TABLE_QOS,
2006                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_PORT,
2007                                  vswitch_idl.OVSREC_PORT_COL_NAME,
2008                                  vswitch_idl.OVSREC_PORT_COL_QOS)]),
2009         _VSCtlTable(vswitch_idl.OVSREC_TABLE_QUEUE,
2010                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_QOS,
2011                                  None,
2012                                  vswitch_idl.OVSREC_QOS_COL_QUEUES)]),
2013         _VSCtlTable(vswitch_idl.OVSREC_TABLE_SSL,
2014                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH,
2015                                  None,
2016                                  vswitch_idl.OVSREC_OPEN_VSWITCH_COL_SSL)]),
2017         _VSCtlTable(vswitch_idl.OVSREC_TABLE_SFLOW,
2018                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE,
2019                                  vswitch_idl.OVSREC_BRIDGE_COL_NAME,
2020                                  vswitch_idl.OVSREC_BRIDGE_COL_SFLOW)]),
2021         _VSCtlTable(vswitch_idl.OVSREC_TABLE_FLOW_TABLE,
2022                     [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_FLOW_TABLE,
2023                                  vswitch_idl.OVSREC_FLOW_TABLE_COL_NAME,
2024                                  None)]),
2025     ]
2026
2027     @staticmethod
2028     def _score_partial_match(name, s):
2029         _MAX_SCORE = 0xffffffff
2030         assert len(name) < _MAX_SCORE
2031         s = s[:_MAX_SCORE - 1]  # in practice, this doesn't matter
2032         if name == s:
2033             return _MAX_SCORE
2034
2035         name = name.lower().replace('-', '_')
2036         s = s.lower().replace('-', '_')
2037         if s.startswith(name):
2038             return _MAX_SCORE - 1
2039         if name.startswith(s):
2040             return len(s)
2041
2042         return 0
2043
2044     @staticmethod
2045     def _get_table(table_name):
2046         best_match = None
2047         best_score = 0
2048         for table in VSCtl._TABLES:
2049             score = VSCtl._score_partial_match(table.table_name, table_name)
2050             if score > best_score:
2051                 best_match = table
2052                 best_score = score
2053             elif score == best_score:
2054                 best_match = None
2055
2056         if best_match:
2057             return best_match
2058         elif best_score:
2059             vsctl_fatal('multiple table names match "%s"' % table_name)
2060         else:
2061             vsctl_fatal('unknown table "%s"' % table_name)
2062
2063     def _pre_get_table(self, _ctx, table_name):
2064         vsctl_table = self._get_table(table_name)
2065
2066         schema_helper = self.schema_helper
2067         schema_helper.register_table(vsctl_table.table_name)
2068         for row_id in vsctl_table.row_ids:
2069             if row_id.table:
2070                 schema_helper.register_table(row_id.table)
2071             if row_id.name_column:
2072                 schema_helper.register_columns(row_id.table,
2073                                                [row_id.name_column])
2074             if row_id.uuid_column:
2075                 schema_helper.register_columns(row_id.table,
2076                                                [row_id.uuid_column])
2077         return vsctl_table
2078
2079     def _get_column(self, table_name, column_name):
2080         best_match = None
2081         best_score = 0
2082
2083         columns = self.schema.tables[table_name].columns.keys()
2084         for column in columns:
2085             score = VSCtl._score_partial_match(column, column_name)
2086             if score > best_score:
2087                 best_match = column
2088                 best_score = score
2089             elif score == best_score:
2090                 best_match = None
2091
2092         if best_match:
2093             # ovs.db.schema_helper._keep_table_columns() requires that
2094             # column_name is type of str. Not unicode string
2095             return str(best_match)
2096         elif best_score:
2097             vsctl_fatal('%s contains more than one column whose name '
2098                         'matches "%s"' % (table_name, column_name))
2099         else:
2100             vsctl_fatal('%s does not contain a column whose name matches '
2101                         '"%s"' % (table_name, column_name))
2102
2103     def _pre_get_column(self, _ctx, table_name, column):
2104         column_name = self._get_column(table_name, column)
2105         self.schema_helper.register_columns(table_name, [column_name])
2106
2107     def _pre_get_columns(self, ctx, table_name, columns):
2108         self._pre_get_table(ctx, table_name)
2109         for column in columns:
2110             self._pre_get_column(ctx, table_name, column)
2111
2112     def _pre_cmd_list(self, ctx, command):
2113         table_name = command.args[0]
2114         self._pre_get_table(ctx, table_name)
2115
2116     def _list(self, ctx, table_name, record_id=None):
2117         result = []
2118         for ovsrec_row in ctx.idl.tables[table_name].rows.values():
2119             if record_id is not None and ovsrec_row.name != record_id:
2120                 continue
2121             result.append(ovsrec_row)
2122
2123         return result
2124
2125     def _cmd_list(self, ctx, command):
2126         table_name = command.args[0]
2127         record_id = None
2128         if len(command.args) > 1:
2129             record_id = command.args[1]
2130
2131         command.result = self._list(ctx, table_name, record_id)
2132
2133     def _pre_cmd_find(self, ctx, command):
2134         table_name = command.args[0]
2135         table_schema = self.schema.tables[table_name]
2136         columns = [
2137             ctx.parse_column_key_value(table_schema, column_key_value)[0]
2138             for column_key_value in command.args[1:]]
2139
2140         self._pre_get_columns(ctx, table_name, columns)
2141
2142     def _check_value(self, ovsrec_row, column_value):
2143         """
2144         :type column_value: tuple of column and value_json
2145         """
2146         column, value_json = column_value
2147         column_schema = ovsrec_row._table.columns[column]
2148         value = ovs.db.data.Datum.from_json(
2149             column_schema.type, value_json).to_python(ovs.db.idl._uuid_to_row)
2150         datum = getattr(ovsrec_row, column)
2151         if column_schema.type.is_map():
2152             for k, v in value.items():
2153                 if k in datum and datum[k] == v:
2154                     return True
2155         elif datum == value:
2156             return True
2157
2158         return False
2159
2160     def _find(self, ctx, table_name, column_values):
2161         """
2162         :type column_values: list of (column, value_json)
2163         """
2164         result = []
2165         for ovsrec_row in ctx.idl.tables[table_name].rows.values():
2166             LOG.debug('ovsrec_row %s', ovsrec_row_to_string(ovsrec_row))
2167             if all(self._check_value(ovsrec_row, column_value)
2168                    for column_value in column_values):
2169                 result.append(ovsrec_row)
2170
2171         return result
2172
2173     def _cmd_find(self, ctx, command):
2174         table_name = command.args[0]
2175         table_schema = self.schema.tables[table_name]
2176         column_values = [
2177             ctx.parse_column_key_value(table_schema, column_key_value)
2178             for column_key_value in command.args[1:]]
2179         command.result = self._find(ctx, table_name, column_values)
2180
2181     def _pre_cmd_get(self, ctx, command):
2182         table_name = command.args[0]
2183         columns = [
2184             ctx.parse_column_key(column_key)[0]
2185             for column_key in command.args[2:]]
2186
2187         self._pre_get_columns(ctx, table_name, columns)
2188
2189     def _get(self, ctx, table_name, record_id, column_keys,
2190              id_=None, if_exists=False):
2191         vsctl_table = self._get_table(table_name)
2192         ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
2193
2194         # TODO: Support symbol name
2195         # if id_:
2196         #     symbol, new = ctx.create_symbol(id_)
2197         #     if not new:
2198         #         vsctl_fatal('row id "%s" specified on "get" command was '
2199         #                     'used before it was defined' % id_)
2200         #     symbol.uuid = row.uuid
2201         #     symbol.strong_ref = True
2202
2203         result = []
2204         for column, key in column_keys:
2205             result.append(ctx.get_column(ovsrec_row, column, key, if_exists))
2206
2207         return result
2208
2209     def _cmd_get(self, ctx, command):
2210         id_ = None  # TODO: Support --id option
2211         if_exists = command.has_option('--if-exists')
2212         table_name = command.args[0]
2213         record_id = command.args[1]
2214
2215         column_keys = [
2216             ctx.parse_column_key(column_key)
2217             for column_key in command.args[2:]]
2218
2219         command.result = self._get(
2220             ctx, table_name, record_id, column_keys, id_, if_exists)
2221
2222     def _check_mutable(self, table_name, column):
2223         column_schema = self.schema.tables[table_name].columns[column]
2224         if not column_schema.mutable:
2225             vsctl_fatal('cannot modify read-only column %s in table %s' %
2226                         (column, table_name))
2227
2228     def _pre_mod_columns(self, ctx, table_name, columns):
2229         self._pre_get_table(ctx, table_name)
2230         for column in columns:
2231             self._pre_get_column(ctx, table_name, column)
2232             self._check_mutable(table_name, column)
2233
2234     def _pre_cmd_set(self, ctx, command):
2235         table_name = command.args[0]
2236         table_schema = self.schema.tables[table_name]
2237         columns = [
2238             ctx.parse_column_key_value(table_schema, column_key_value)[0]
2239             for column_key_value in command.args[2:]]
2240
2241         self._pre_mod_columns(ctx, table_name, columns)
2242
2243     def _set(self, ctx, table_name, record_id, column_values):
2244         """
2245         :type column_values: list of (column, value_json)
2246         """
2247         vsctl_table = self._get_table(table_name)
2248         ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
2249         for column, value in column_values:
2250             ctx.set_column(ovsrec_row, column, value)
2251         ctx.invalidate_cache()
2252
2253     def _cmd_set(self, ctx, command):
2254         table_name = command.args[0]
2255         record_id = command.args[1]
2256
2257         # column_key_value: <column>[:<key>]=<value>
2258         table_schema = self.schema.tables[table_name]
2259         column_values = [
2260             ctx.parse_column_key_value(table_schema, column_key_value)
2261             for column_key_value in command.args[2:]]
2262
2263         self._set(ctx, table_name, record_id, column_values)
2264
2265     def _pre_cmd_add(self, ctx, command):
2266         table_name = command.args[0]
2267         columns = [command.args[2]]
2268
2269         self._pre_mod_columns(ctx, table_name, columns)
2270
2271     def _add(self, ctx, table_name, record_id, column_values):
2272         """
2273         :type column_values: list of (column, value_json)
2274         """
2275         vsctl_table = self._get_table(table_name)
2276         ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
2277         for column, value in column_values:
2278             ctx.add_column(ovsrec_row, column, value)
2279         ctx.invalidate_cache()
2280
2281     def _cmd_add(self, ctx, command):
2282         table_name = command.args[0]
2283         record_id = command.args[1]
2284         column = command.args[2]
2285
2286         column_key_value_strings = []
2287         for value in command.args[3:]:
2288             if '=' in value:
2289                 # construct <column>:<key>=value
2290                 column_key_value_strings.append('%s:%s' % (column, value))
2291             else:
2292                 # construct <column>=value
2293                 column_key_value_strings.append('%s=%s' % (column, value))
2294
2295         table_schema = self.schema.tables[table_name]
2296         column_values = [
2297             ctx.parse_column_key_value(table_schema, column_key_value_string)
2298             for column_key_value_string in column_key_value_strings]
2299
2300         self._add(ctx, table_name, record_id, column_values)
2301
2302     def _pre_cmd_remove(self, ctx, command):
2303         table_name = command.args[0]
2304         columns = [command.args[2]]
2305
2306         self._pre_mod_columns(ctx, table_name, columns)
2307
2308     def _remove(self, ctx, table_name, record_id, column_values):
2309         """
2310         :type column_values: list of (column, value_json)
2311         """
2312         vsctl_table = self._get_table(table_name)
2313         ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
2314         for column, value in column_values:
2315             ctx.remove_column(ovsrec_row, column, value)
2316         ctx.invalidate_cache()
2317
2318     def _cmd_remove(self, ctx, command):
2319         table_name = command.args[0]
2320         record_id = command.args[1]
2321         column = command.args[2]
2322
2323         column_key_value_strings = []
2324         for value in command.args[3:]:
2325             if '=' in value:
2326                 # construct <column>:<key>=value
2327                 column_key_value_strings.append('%s:%s' % (column, value))
2328             else:
2329                 # construct <column>=value
2330                 column_key_value_strings.append('%s=%s' % (column, value))
2331
2332         table_schema = self.schema.tables[table_name]
2333         column_values = [
2334             ctx.parse_column_key_value(table_schema, column_key_value_string)
2335             for column_key_value_string in column_key_value_strings]
2336
2337         self._remove(ctx, table_name, record_id, column_values)
2338
2339     def _pre_cmd_clear(self, ctx, command):
2340         table_name = command.args[0]
2341         column = command.args[2]
2342         self._pre_mod_columns(ctx, table_name, [column])
2343
2344     def _clear(self, ctx, table_name, record_id, column):
2345         vsctl_table = self._get_table(table_name)
2346         ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
2347         column_schema = ctx.idl.tables[table_name].columns[column]
2348         if column_schema.type.n_min > 0:
2349             vsctl_fatal('"clear" operation cannot be applied to column %s '
2350                         'of table %s, which is not allowed to be empty' %
2351                         (column, table_name))
2352
2353         # assuming that default datum is empty.
2354         default_datum = ovs.db.data.Datum.default(column_schema.type)
2355         setattr(ovsrec_row, column,
2356                 default_datum.to_python(ovs.db.idl._uuid_to_row))
2357         ctx.invalidate_cache()
2358
2359     def _cmd_clear(self, ctx, command):
2360         table_name = command.args[0]
2361         record_id = command.args[1]
2362         column = command.args[2]
2363         self._clear(ctx, table_name, record_id, column)
2364
2365
2366 #
2367 # Create constants from ovs db schema
2368 #
2369
2370 def schema_print(schema_location, prefix):
2371     prefix = prefix.upper()
2372
2373     json = ovs.json.from_file(schema_location)
2374     schema = ovs.db.schema.DbSchema.from_json(json)
2375
2376     print('# Do NOT edit.')
2377     print('# This is automatically generated by %s' % __file__)
2378     print('# created based on version %s' % (schema.version or 'unknown'))
2379     print('')
2380     print('')
2381     print('%s_DB_NAME = \'%s\'' % (prefix, schema.name))
2382     for table in sorted(schema.tables.values(),
2383                         key=operator.attrgetter('name')):
2384         print('')
2385         print('%s_TABLE_%s = \'%s\'' % (prefix,
2386                                         table.name.upper(), table.name))
2387         for column in sorted(table.columns.values(),
2388                              key=operator.attrgetter('name')):
2389             print('%s_%s_COL_%s = \'%s\'' % (prefix, table.name.upper(),
2390                                              column.name.upper(),
2391                                              column.name))
2392
2393
2394 def main():
2395     if len(sys.argv) <= 2:
2396         print('Usage: %s <schema file>' % sys.argv[0])
2397         print('e.g.:  %s vswitchd/vswitch.ovsschema' % sys.argv[0])
2398
2399     location = sys.argv[1]
2400     prefix = 'OVSREC'
2401     schema_print(location, prefix)
2402
2403
2404 if __name__ == '__main__':
2405     main()