text: # Copyright (c) 2015 Openstack Foundation
  #
  #    Licensed under the Apache License, Version 2.0 (the "License"); you may
  #    not use this file except in compliance with the License. You may obtain
  #    a copy of the License at
  #
  #         http://www.apache.org/licenses/LICENSE-2.0
  #
  #    Unless required by applicable law or agreed to in writing, software
  #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  #    License for the specific language governing permissions and limitations
  #    under the License.
  
  import collections
  
  from oslo_log import log as logging
  from oslo_utils import excutils
  
  from neutron.agent.ovsdb import api
  from neutron.agent.ovsdb.native import idlutils
  from neutron.i18n import _LE
  
  LOG = logging.getLogger(__name__)
  
  
  class BaseCommand(api.Command):
      def __init__(self, api):
          self.api = api
          self.result = None
  
      def execute(self, check_error=False, log_errors=True):
          try:
              with self.api.transaction(check_error, log_errors) as txn:
                  txn.add(self)
              return self.result
          except Exception:
              with excutils.save_and_reraise_exception() as ctx:
                  if log_errors:
                      LOG.exception(_LE("Error executing command"))
                  if not check_error:
                      ctx.reraise = False
  
      def __str__(self):
          command_info = self.__dict__
          return "%s(%s)" % (
              self.__class__.__name__,
              ", ".join("%s=%s" % (k, v) for k, v in command_info.items()
                        if k not in ['api', 'result']))
  
  
  class AddBridgeCommand(BaseCommand):
      def __init__(self, api, name, may_exist, datapath_type):
          super(AddBridgeCommand, self).__init__(api)
          self.name = name
          self.may_exist = may_exist
          self.datapath_type = datapath_type
  
      def run_idl(self, txn):
          if self.may_exist:
              br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name',
                                         self.name, None)
              if br:
                  return
          row = txn.insert(self.api._tables['Bridge'])
          row.name = self.name
          if self.datapath_type:
              row.datapath_type = self.datapath_type
          self.api._ovs.verify('bridges')
          self.api._ovs.bridges = self.api._ovs.bridges + [row]
  
          # Add the internal bridge port
          cmd = AddPortCommand(self.api, self.name, self.name, self.may_exist)
          cmd.run_idl(txn)
  
          cmd = DbSetCommand(self.api, 'Interface', self.name,
                             ('type', 'internal'))
          cmd.run_idl(txn)
  
  
  class DelBridgeCommand(BaseCommand):
      def __init__(self, api, name, if_exists):
          super(DelBridgeCommand, self).__init__(api)
          self.name = name
          self.if_exists = if_exists
  
      def run_idl(self, txn):
          try:
              br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name',
                                         self.name)
          except idlutils.RowNotFound:
              if self.if_exists:
                  return
              else:
                  msg = _("Bridge %s does not exist") % self.name
                  LOG.error(msg)
                  raise RuntimeError(msg)
          self.api._ovs.verify('bridges')
          for port in br.ports:
              cmd = DelPortCommand(self.api, port.name, self.name,
                                   if_exists=True)
              cmd.run_idl(txn)
          bridges = self.api._ovs.bridges
          bridges.remove(br)
          self.api._ovs.bridges = bridges
          self.api._tables['Bridge'].rows[br.uuid].delete()
  
  
  class BridgeExistsCommand(BaseCommand):
      def __init__(self, api, name):
          super(BridgeExistsCommand, self).__init__(api)
          self.name = name
  
      def run_idl(self, txn):
          self.result = bool(idlutils.row_by_value(self.api.idl, 'Bridge',
                                                   'name', self.name, None))
  
  
  class ListBridgesCommand(BaseCommand):
      def __init__(self, api):
          super(ListBridgesCommand, self).__init__(api)
  
      def run_idl(self, txn):
          # NOTE (twilson) [x.name for x in rows.values()] if no index
          self.result = [x.name for x in
                         self.api._tables['Bridge'].rows.values()]
  
  
  class BrGetExternalIdCommand(BaseCommand):
      def __init__(self, api, name, field):
          super(BrGetExternalIdCommand, self).__init__(api)
          self.name = name
          self.field = field
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.name)
          self.result = br.external_ids[self.field]
  
  
  class BrSetExternalIdCommand(BaseCommand):
      def __init__(self, api, name, field, value):
          super(BrSetExternalIdCommand, self).__init__(api)
          self.name = name
          self.field = field
          self.value = value
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.name)
          external_ids = getattr(br, 'external_ids', {})
          external_ids[self.field] = self.value
          br.external_ids = external_ids
  
  
  class DbCreateCommand(BaseCommand):
      def __init__(self, api, table, **columns):
          super(DbCreateCommand, self).__init__(api)
          self.table = table
          self.columns = columns
  
      def run_idl(self, txn):
          row = txn.insert(self.api._tables[self.table])
          for col, val in self.columns.items():
              setattr(row, col, val)
          self.result = row
  
  
  class DbDestroyCommand(BaseCommand):
      def __init__(self, api, table, record):
          super(DbDestroyCommand, self).__init__(api)
          self.table = table
          self.record = record
  
      def run_idl(self, txn):
          record = idlutils.row_by_record(self.api.idl, self.table, self.record)
          record.delete()
  
  
  class DbSetCommand(BaseCommand):
      def __init__(self, api, table, record, *col_values):
          super(DbSetCommand, self).__init__(api)
          self.table = table
          self.record = record
          self.col_values = col_values
  
      def run_idl(self, txn):
          record = idlutils.row_by_record(self.api.idl, self.table, self.record)
          for col, val in self.col_values:
              # TODO(twilson) Ugh, the OVS library doesn't like OrderedDict
              # We're only using it to make a unit test work, so we should fix
              # this soon.
              if isinstance(val, collections.OrderedDict):
                  val = dict(val)
              setattr(record, col, val)
  
  
  class DbClearCommand(BaseCommand):
      def __init__(self, api, table, record, column):
          super(DbClearCommand, self).__init__(api)
          self.table = table
          self.record = record
          self.column = column
  
      def run_idl(self, txn):
          record = idlutils.row_by_record(self.api.idl, self.table, self.record)
          # Create an empty value of the column type
          value = type(getattr(record, self.column))()
          setattr(record, self.column, value)
  
  
  class DbGetCommand(BaseCommand):
      def __init__(self, api, table, record, column):
          super(DbGetCommand, self).__init__(api)
          self.table = table
          self.record = record
          self.column = column
  
      def run_idl(self, txn):
          record = idlutils.row_by_record(self.api.idl, self.table, self.record)
          # TODO(twilson) This feels wrong, but ovs-vsctl returns single results
          # on set types without the list. The IDL is returning them as lists,
          # even if the set has the maximum number of items set to 1. Might be
          # able to inspect the Schema and just do this conversion for that case.
          result = idlutils.get_column_value(record, self.column)
          if isinstance(result, list) and len(result) == 1:
              self.result = result[0]
          else:
              self.result = result
  
  
  class SetControllerCommand(BaseCommand):
      def __init__(self, api, bridge, targets):
          super(SetControllerCommand, self).__init__(api)
          self.bridge = bridge
          self.targets = targets
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          controllers = []
          for target in self.targets:
              controller = txn.insert(self.api._tables['Controller'])
              controller.target = target
              controllers.append(controller)
          br.verify('controller')
          br.controller = controllers
  
  
  class DelControllerCommand(BaseCommand):
      def __init__(self, api, bridge):
          super(DelControllerCommand, self).__init__(api)
          self.bridge = bridge
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          br.controller = []
  
  
  class GetControllerCommand(BaseCommand):
      def __init__(self, api, bridge):
          super(GetControllerCommand, self).__init__(api)
          self.bridge = bridge
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          br.verify('controller')
          self.result = [c.target for c in br.controller]
  
  
  class SetFailModeCommand(BaseCommand):
      def __init__(self, api, bridge, mode):
          super(SetFailModeCommand, self).__init__(api)
          self.bridge = bridge
          self.mode = mode
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          br.verify('fail_mode')
          br.fail_mode = self.mode
  
  
  class AddPortCommand(BaseCommand):
      def __init__(self, api, bridge, port, may_exist):
          super(AddPortCommand, self).__init__(api)
          self.bridge = bridge
          self.port = port
          self.may_exist = may_exist
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          if self.may_exist:
              port = idlutils.row_by_value(self.api.idl, 'Port', 'name',
                                           self.port, None)
              if port:
                  return
          port = txn.insert(self.api._tables['Port'])
          port.name = self.port
          br.verify('ports')
          ports = getattr(br, 'ports', [])
          ports.append(port)
          br.ports = ports
  
          iface = txn.insert(self.api._tables['Interface'])
          iface.name = self.port
          port.verify('interfaces')
          ifaces = getattr(port, 'interfaces', [])
          ifaces.append(iface)
          port.interfaces = ifaces
  
  
  class DelPortCommand(BaseCommand):
      def __init__(self, api, port, bridge, if_exists):
          super(DelPortCommand, self).__init__(api)
          self.port = port
          self.bridge = bridge
          self.if_exists = if_exists
  
      def run_idl(self, txn):
          try:
              port = idlutils.row_by_value(self.api.idl, 'Port', 'name',
                                           self.port)
          except idlutils.RowNotFound:
              if self.if_exists:
                  return
              msg = _("Port %s does not exist") % self.port
              raise RuntimeError(msg)
          if self.bridge:
              br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name',
                                         self.bridge)
          else:
              br = next(b for b in self.api._tables['Bridge'].rows.values()
                        if port in b.ports)
  
          if port.uuid not in br.ports and not self.if_exists:
              # TODO(twilson) Make real errors across both implementations
              msg = _("Port %(port)s does not exist on %(bridge)s!") % {
                  'port': self.name, 'bridge': self.bridge
              }
              LOG.error(msg)
              raise RuntimeError(msg)
  
          br.verify('ports')
          ports = br.ports
          ports.remove(port)
          br.ports = ports
  
          # Also remove port/interface directly for indexing?
          port.verify('interfaces')
          for iface in port.interfaces:
              self.api._tables['Interface'].rows[iface.uuid].delete()
          self.api._tables['Port'].rows[port.uuid].delete()
  
  
  class ListPortsCommand(BaseCommand):
      def __init__(self, api, bridge):
          super(ListPortsCommand, self).__init__(api)
          self.bridge = bridge
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          self.result = [p.name for p in br.ports if p.name != self.bridge]
  
  
  class ListIfacesCommand(BaseCommand):
      def __init__(self, api, bridge):
          super(ListIfacesCommand, self).__init__(api)
          self.bridge = bridge
  
      def run_idl(self, txn):
          br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge)
          self.result = [i.name for p in br.ports if p.name != self.bridge
                         for i in p.interfaces]
  
  
  class PortToBridgeCommand(BaseCommand):
      def __init__(self, api, name):
          super(PortToBridgeCommand, self).__init__(api)
          self.name = name
  
      def run_idl(self, txn):
          # TODO(twilson) This is expensive!
          # This traversal of all ports could be eliminated by caching the bridge
          # name on the Port's external_id field
          # In fact, if we did that, the only place that uses to_br functions
          # could just add the external_id field to the conditions passed to find
          port = idlutils.row_by_value(self.api.idl, 'Port', 'name', self.name)
          bridges = self.api._tables['Bridge'].rows.values()
          self.result = next(br.name for br in bridges if port in br.ports)
  
  
  class InterfaceToBridgeCommand(BaseCommand):
      def __init__(self, api, name):
          super(InterfaceToBridgeCommand, self).__init__(api)
          self.name = name
  
      def run_idl(self, txn):
          interface = idlutils.row_by_value(self.api.idl, 'Interface', 'name',
                                            self.name)
          ports = self.api._tables['Port'].rows.values()
          pname = next(
                  port for port in ports if interface in port.interfaces)
  
          bridges = self.api._tables['Bridge'].rows.values()
          self.result = next(br.name for br in bridges if pname in br.ports)
  
  
  class DbListCommand(BaseCommand):
      def __init__(self, api, table, records, columns, if_exists):
          super(DbListCommand, self).__init__(api)
          self.table = table
          self.columns = columns
          self.if_exists = if_exists
          self.records = records
  
      def run_idl(self, txn):
          table_schema = self.api._tables[self.table]
          columns = self.columns or table_schema.columns.keys() + ['_uuid']
          if self.records:
              row_uuids = []
              for record in self.records:
                  try:
                      row_uuids.append(idlutils.row_by_record(
                                       self.api.idl, self.table, record).uuid)
                  except idlutils.RowNotFound:
                      if self.if_exists:
                          continue
                      # NOTE(kevinbenton): this is converted to a RuntimeError
                      # for compat with the vsctl version. It might make more
                      # sense to change this to a RowNotFoundError in the future.
                      raise RuntimeError(_(
                            "Row doesn't exist in the DB. Request info: "
                            "Table=%(table)s. Columns=%(columns)s. "
                            "Records=%(records)s.") % {
                                "table": self.table,
                                "columns": self.columns,
                                "records": self.records,
                            })
          else:
              row_uuids = table_schema.rows.keys()
          self.result = [
              {
                  c: idlutils.get_column_value(table_schema.rows[uuid], c)
                  for c in columns
              }
              for uuid in row_uuids
          ]
  
  
  class DbFindCommand(BaseCommand):
      def __init__(self, api, table, *conditions, **kwargs):
          super(DbFindCommand, self).__init__(api)
          self.table = self.api._tables[table]
          self.conditions = conditions
          self.columns = (kwargs.get('columns') or
                          self.table.columns.keys() + ['_uuid'])
  
      def run_idl(self, txn):
          self.result = [
              {
                  c: idlutils.get_column_value(r, c)
                  for c in self.columns
              }
              for r in self.table.rows.values()
              if idlutils.row_match(r, self.conditions)
          ]
  