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
21from __future__ import print_function
22
23import logging
24import operator
25import os
26import re
27import sys
28import weakref
29
30import six
31
32import ovs.db.data
33import ovs.db.parser
34import ovs.db.schema
35import ovs.db.types
36import ovs.poller
37import ovs.json
38from ovs import jsonrpc
39from ovs import ovsuuid
40from ovs import stream
41from ovs.db import idl
42
43from ryu.lib import hub
44from ryu.lib import ip
45from ryu.lib.ovs import vswitch_idl
46from ryu.lib.stringify import StringifyMixin
47
48
49LOG = logging.getLogger(__name__)       # use ovs.vlog?
50
51
52def 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
88def 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
97def 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
109def 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
140def 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
167def ifind(pred, seq):
168    try:
169        return [i for i in seq if pred(i)][0]
170    except IndexError:
171        return None
172
173
174def not_reached():
175    os.abort()
176
177
178def vsctl_fatal(msg):
179    LOG.error(msg)
180    raise Exception(msg)        # not call ovs.utils.ovs_fatal for reusability
181
182
183class 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
198class 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
209class 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
217class 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
226class 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
234class 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
937class _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
947class _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
956class _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
964class 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
1011class 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
2370def 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
2394def 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
2404if __name__ == '__main__':
2405    main()
2406