return self.ovsdb.db_get(table, record, column).execute(
check_error=check_error, log_errors=log_errors)
+ def db_list(self, table, records=None, columns=None,
+ check_error=True, log_errors=True, if_exists=False):
+ return (self.ovsdb.db_list(table, records=records, columns=columns,
+ if_exists=if_exists).
+ execute(check_error=check_error, log_errors=log_errors))
+
class OVSBridge(BaseOVS):
def __init__(self, br_name):
def get_vif_ports(self):
edge_ports = []
port_names = self.get_port_name_list()
+ port_info = self.db_list(
+ 'Interface', columns=['name', 'external_ids', 'ofport'])
+ by_name = {x['name']: x for x in port_info}
for name in port_names:
- external_ids = self.db_get_val("Interface", name, "external_ids",
- check_error=True)
- ofport = self.db_get_val("Interface", name, "ofport",
- check_error=True)
+ external_ids = by_name[name]['external_ids']
+ ofport = by_name[name]['ofport']
if "iface-id" in external_ids and "attached-mac" in external_ids:
p = VifPort(name, ofport, external_ids["iface-id"],
external_ids["attached-mac"], self)
def get_vif_port_to_ofport_map(self):
port_names = self.get_port_name_list()
- cmd = self.ovsdb.db_list(
- 'Interface', port_names,
- columns=['name', 'external_ids', 'ofport'], if_exists=True)
- results = cmd.execute(check_error=True)
+ results = self.db_list(
+ 'Interface', port_names, ['name', 'external_ids', 'ofport'],
+ if_exists=True)
port_map = {}
for r in results:
# fall back to basic interface name
def get_vif_port_set(self):
edge_ports = set()
port_names = self.get_port_name_list()
- cmd = self.ovsdb.db_list(
- 'Interface', port_names,
- columns=['name', 'external_ids', 'ofport'], if_exists=True)
- results = cmd.execute(check_error=True)
+ results = self.db_list(
+ 'Interface', port_names, ['name', 'external_ids', 'ofport'],
+ if_exists=True)
for result in results:
if result['ofport'] == UNASSIGNED_OFPORT:
LOG.warn(_LW("Found not yet ready openvswitch port: %s"),
"""
port_names = self.get_port_name_list()
- cmd = self.ovsdb.db_list('Port', port_names, columns=['name', 'tag'],
- if_exists=True)
- results = cmd.execute(check_error=True)
+ results = self.db_list('Port', port_names, ['name', 'tag'],
+ if_exists=True)
return {p['name']: p['tag'] for p in results}
+ def get_vifs_by_ids(self, port_ids):
+ interface_info = self.db_list(
+ "Interface", columns=["name", "external_ids", "ofport"])
+ by_id = {x['external_ids'].get('iface-id'): x for x in interface_info}
+ intfs_on_bridge = self.ovsdb.list_ports(self.br_name).execute(
+ check_error=True)
+ result = {}
+ for port_id in port_ids:
+ result[port_id] = None
+ if (port_id not in by_id or
+ by_id[port_id]['name'] not in intfs_on_bridge):
+ LOG.info(_LI("Port %(port_id)s not present in bridge "
+ "%(br_name)s"),
+ {'port_id': port_id, 'br_name': self.br_name})
+ continue
+ pinfo = by_id[port_id]
+ if not self._check_ofport(port_id, pinfo):
+ continue
+ mac = pinfo['external_ids'].get('attached-mac')
+ result[port_id] = VifPort(pinfo['name'], pinfo['ofport'],
+ port_id, mac, self)
+ return result
+
+ @staticmethod
+ def _check_ofport(port_id, port_info):
+ if port_info['ofport'] in [UNASSIGNED_OFPORT, INVALID_OFPORT]:
+ LOG.warn(_LW("ofport: %(ofport)s for VIF: %(vif)s is not a"
+ " positive integer"),
+ {'ofport': port_info['ofport'], 'vif': port_id})
+ return False
+ return True
+
def get_vif_port_by_id(self, port_id):
ports = self.ovsdb.db_find(
'Interface', ('external_ids', '=', {'iface-id': port_id}),
for port in ports:
if self.br_name != self.get_bridge_for_iface(port['name']):
continue
- if port['ofport'] in [UNASSIGNED_OFPORT, INVALID_OFPORT]:
- LOG.warn(_LW("ofport: %(ofport)s for VIF: %(vif)s is not a"
- " positive integer"),
- {'ofport': port['ofport'], 'vif': port_id})
+ if not self._check_ofport(port_id, port):
continue
mac = port['external_ids'].get('attached-mac')
return VifPort(port['name'], port['ofport'], port_id, mac, self)
def _restore_local_vlan_map(self):
cur_ports = self.int_br.get_vif_ports()
+ port_info = self.int_br.db_list(
+ "Port", columns=["name", "other_config", "tag"])
+ by_name = {x['name']: x for x in port_info}
for port in cur_ports:
- local_vlan_map = self.int_br.db_get_val("Port", port.port_name,
- "other_config")
- local_vlan = self.int_br.db_get_val("Port", port.port_name, "tag")
+ # if a port was deleted between get_vif_ports and db_lists, we
+ # will get a KeyError
+ try:
+ local_vlan_map = by_name[port.port_name]['other_config']
+ local_vlan = by_name[port.port_name]['tag']
+ except KeyError:
+ continue
if not local_vlan:
continue
net_uuid = local_vlan_map.get('net_uuid')
port_other_config)
def _bind_devices(self, need_binding_ports):
+ port_info = self.int_br.db_list(
+ "Port", columns=["name", "tag"])
+ tags_by_name = {x['name']: x['tag'] for x in port_info}
for port_detail in need_binding_ports:
lvm = self.local_vlan_map.get(port_detail['network_id'])
if not lvm:
port = port_detail['vif_port']
device = port_detail['device']
# Do not bind a port if it's already bound
- cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
+ cur_tag = tags_by_name.get(port.port_name)
if cur_tag != lvm.vlan:
self.int_br.set_db_attribute(
"Port", port.port_name, "tag", lvm.vlan)
self.conf.host)
except Exception as e:
raise DeviceListRetrievalError(devices=devices, error=e)
+ vif_by_id = self.int_br.get_vifs_by_ids(
+ [vif['device'] for vif in devices_details_list])
for details in devices_details_list:
device = details['device']
LOG.debug("Processing port: %s", device)
- port = self.int_br.get_vif_port_by_id(device)
+ port = vif_by_id.get(device)
if not port:
# The port disappeared and cannot be processed
LOG.info(_LI("Port %s was not found on the integration bridge "
self.assertEqual(self.br.get_vif_port_by_id(vif.vif_id).vif_id,
vif.vif_id)
+ def test_get_vifs_by_ids(self):
+ for i in range(2):
+ self.create_ovs_port()
+ vif_ports = [self.create_ovs_vif_port() for i in range(3)]
+ by_id = self.br.get_vifs_by_ids([v.vif_id for v in vif_ports])
+ # convert to str for comparison of VifPorts
+ by_id = {vid: str(vport) for vid, vport in by_id.items()}
+ self.assertEqual({v.vif_id: str(v) for v in vif_ports}, by_id)
+
def test_delete_ports(self):
# TODO(twilson) I intensely dislike the current delete_ports function
# as the default behavior is really delete_vif_ports(), then it acts
def _test_get_vif_ports(self, is_xen=False):
pname = "tap99"
ofport = 6
- ofport_data = self._encode_ovs_json(['ofport'], [[ofport]])
vif_id = uuidutils.generate_uuid()
mac = "ca:fe:de:ad:be:ef"
id_field = 'xs-vif-uuid' if is_xen else 'iface-id'
external_ids = ('{"data":[[["map",[["attached-mac","%(mac)s"],'
'["%(id_field)s","%(vif)s"],'
- '["iface-status","active"]]]]],'
- '"headings":["external_ids"]}' % {
- 'mac': mac, 'vif': vif_id, 'id_field': id_field})
+ '["iface-status","active"]]], '
+ '"%(name)s", %(ofport)s]],'
+ '"headings":["external_ids", "name", "ofport"]}' % {
+ 'mac': mac, 'vif': vif_id, 'id_field': id_field,
+ 'name': pname, 'ofport': ofport})
# Each element is a tuple of (expected mock call, return_value)
expected_calls_and_values = [
(self._vsctl_mock("list-ports", self.BR_NAME), "%s\n" % pname),
- (self._vsctl_mock("--columns=external_ids", "list",
- "Interface", pname), external_ids),
- (self._vsctl_mock("--columns=ofport", "list", "Interface", pname),
- ofport_data),
+ (self._vsctl_mock("--columns=name,external_ids,ofport", "list",
+ "Interface"), external_ids),
]
if is_xen:
expected_calls_and_values.append(
with testtools.ExpectedException(Exception):
self.br.get_local_port_mac()
+ def test_get_vifs_by_ids(self):
+ db_list_res = [
+ {'name': 'qvo1', 'ofport': 1,
+ 'external_ids': {'iface-id': 'pid1', 'attached-mac': '11'}},
+ {'name': 'qvo2', 'ofport': 2,
+ 'external_ids': {'iface-id': 'pid2', 'attached-mac': '22'}},
+ {'name': 'qvo3', 'ofport': 3,
+ 'external_ids': {'iface-id': 'pid3', 'attached-mac': '33'}},
+ {'name': 'qvo4', 'ofport': -1,
+ 'external_ids': {'iface-id': 'pid4', 'attached-mac': '44'}},
+ ]
+ self.br.db_list = mock.Mock(return_value=db_list_res)
+ self.br.ovsdb = mock.Mock()
+ self.br.ovsdb.list_ports.return_value.execute.return_value = [
+ 'qvo1', 'qvo2', 'qvo4']
+ by_id = self.br.get_vifs_by_ids(['pid1', 'pid2', 'pid3',
+ 'pid4', 'pid5'])
+ # pid3 isn't on bridge and pid4 doesn't have a valid ofport and pid5
+ # isn't present in the db
+ self.assertIsNone(by_id['pid3'])
+ self.assertIsNone(by_id['pid4'])
+ self.assertIsNone(by_id['pid5'])
+ self.assertEqual('pid1', by_id['pid1'].vif_id)
+ self.assertEqual('qvo1', by_id['pid1'].port_name)
+ self.assertEqual(1, by_id['pid1'].ofport)
+ self.assertEqual('pid2', by_id['pid2'].vif_id)
+ self.assertEqual('qvo2', by_id['pid2'].port_name)
+ self.assertEqual(2, by_id['pid2'].ofport)
+
def _test_get_vif_port_by_id(self, iface_id, data, br_name=None,
extra_calls_and_values=None):
headings = ['external_ids', 'name', 'ofport']
cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT')
cfg.CONF.set_default('prevent_arp_spoofing', False, 'AGENT')
kwargs = self.mod_agent.create_agent_config_map(cfg.CONF)
+ mock.patch('neutron.agent.common.ovs_lib.OVSBridge.db_list',
+ return_value=[]).start()
with mock.patch.object(self.mod_agent.OVSNeutronAgent,
'setup_integration_br'),\
mock.patch.object(self.agent, 'provision_local_vlan') as \
provision_local_vlan:
int_br.get_vif_ports.return_value = [port]
- int_br.db_get_val.side_effect = [local_vlan_map, tag]
+ int_br.db_list.return_value = [{
+ 'name': port.port_name, 'other_config': local_vlan_map,
+ 'tag': tag
+ }]
self.agent._restore_local_vlan_map()
if tag:
self.assertTrue(provision_local_vlan.called)
'get_devices_details_list',
return_value=[details]),\
mock.patch.object(self.agent.int_br,
- 'get_vif_port_by_id',
- return_value=port),\
+ 'get_vifs_by_ids',
+ return_value={details['device']: port}),\
mock.patch.object(self.agent, func_name) as func:
skip_devs, need_bound_devices = (
self.agent.treat_devices_added_or_updated([{}], False))
'get_devices_details_list',
return_value=[dev_mock]),\
mock.patch.object(self.agent.int_br,
- 'get_vif_port_by_id',
- return_value=None),\
+ 'get_vifs_by_ids',
+ return_value={}),\
mock.patch.object(self.agent,
'treat_vif_port') as treat_vif_port:
skip_devs = self.agent.treat_devices_added_or_updated([{}], False)
'get_devices_details_list',
return_value=[fake_details_dict]),\
mock.patch.object(self.agent.int_br,
- 'get_vif_port_by_id',
- return_value=mock.MagicMock()),\
+ 'get_vifs_by_ids',
+ return_value={'xxx': mock.MagicMock()}),\
mock.patch.object(self.agent,
'treat_vif_port') as treat_vif_port:
skip_devs, need_bound_devices = (
self.agent,
"treat_devices_added_or_updated",
return_value=([], [])) as device_added_updated,\
+ mock.patch.object(self.agent.int_br, "db_list",
+ return_value=[]),\
mock.patch.object(self.agent,
"treat_devices_removed",
return_value=False) as device_removed:
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.'
'get_bridge_external_bridge_id',
side_effect=pullup_side_effect),\
+ mock.patch(
+ 'neutron.agent.common.ovs_lib.OVSBridge.' 'db_list',
+ return_value=[]),\
mock.patch(
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports',
return_value=[]):
mock.patch('neutron.openstack.common.loopingcall.'
'FixedIntervalLoopingCall',
new=MockFixedIntervalLoopingCall),\
+ mock.patch(
+ 'neutron.agent.common.ovs_lib.OVSBridge.' 'db_list',
+ return_value=[]),\
mock.patch(
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports',
return_value=[]):
self.mock_int_bridge.add_patch_port.side_effect = (
lambda tap, peer: self.ovs_int_ofports[tap])
self.mock_int_bridge.get_vif_ports.return_value = []
+ self.mock_int_bridge.db_list.return_value = []
self.mock_int_bridge.db_get_val.return_value = {}
self.mock_map_tun_bridge = self.ovs_bridges[self.MAP_TUN_BRIDGE]
]
self.mock_int_bridge_expected += [
mock.call.get_vif_ports(),
+ mock.call.db_list('Port', columns=['name', 'other_config', 'tag'])
]
self.mock_tun_bridge_expected += [
def _verify_mock_call(self, mock_obj, expected):
mock_obj.assert_has_calls(expected)
- self.assertEqual(len(mock_obj.mock_calls), len(expected))
+ self.assertEqual(expected, mock_obj.mock_calls)
def _verify_mock_calls(self):
self._verify_mock_call(self.mock_int_bridge_cls,
]
self.mock_int_bridge_expected += [
mock.call.get_vif_ports(),
+ mock.call.db_list('Port', columns=['name', 'other_config', 'tag'])
]
self.mock_tun_bridge_expected += [
mock.call.delete_flows(),