This is an additional patch that removes incorrect reference to
internal ports. The property internal_ports is removed and
ports with internal interfaces are now returned in property ports.
The patch also fixes an issue with OvS 2.13 that changed
the output format of 'ovs-vsctl show' that was used to get
information about the ovs bridge ports. To be more future proof
the json output of 'ovs-vsctl list ports' is used instead.
Since tunnels are also ports, the property tunnels reuses the ports
property and filters out the relevant ports.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
lnst/Devices/OvsBridgeDevice.py | 125 +++++++++++-----------
lnst/Recipes/ENRT/NoVirtOvsVxlanRecipe.py | 4 +-
2 files changed, 66 insertions(+), 63 deletions(-)
diff --git a/lnst/Devices/OvsBridgeDevice.py b/lnst/Devices/OvsBridgeDevice.py
index 28d786bb..fbe31b2c 100644
--- a/lnst/Devices/OvsBridgeDevice.py
+++ b/lnst/Devices/OvsBridgeDevice.py
@@ -12,6 +12,7 @@ olichtne(a)redhat.com (Ondrej Lichtner)
import re
import pprint
+import json
from lnst.Common.Utils import check_process_running
from lnst.Common.ExecCmd import exec_cmd
from lnst.Common.DeviceError import DeviceError
@@ -50,6 +51,51 @@ class OvsBridgeDevice(SoftDevice):
return cmd
+ def _format_ovs_json_value(self, value):
+ formatted_value = None
+ if type(value) == list:
+ value_type = value[0]
+
+ if value_type == 'map':
+ formatted_value = value[1]
+ elif value_type == 'set':
+ formatted_value = value[1]
+ elif value_type == 'uuid':
+ formatted_value = value[1]
+ else:
+ raise Exception("Unknown type in ovs json output: {}".format(
+ value_type))
+ else:
+ formatted_value = value
+
+ return formatted_value
+
+ def _format_ovs_json(self, ovs_json):
+ headings = ovs_json['headings']
+ data = ovs_json['data']
+
+ formatted_data = []
+
+ for data_entry in data:
+ formatted_fields = {}
+ for i, entry_value in enumerate(data_entry):
+ formatted_fields[headings[i]] = self._format_ovs_json_value(entry_value)
+ formatted_data.append(formatted_fields)
+
+ return formatted_data
+
+ def _list_ports(self):
+ out_json = exec_cmd("ovs-vsctl --format json list port",
+ log_outputs=False, json=True)[0]
+
+ return self._format_ovs_json(out_json)
+
+ def _list_interfaces(self):
+ out_json = exec_cmd("ovs-vsctl --format json list interface",
+ log_outputs=False, json=True)[0]
+
+ return self._format_ovs_json(out_json)
+
def port_add(self, device=None, port_options={}, interface_options={}):
if device is None:
dev_name = interface_options.get('name',
@@ -111,35 +157,31 @@ class OvsBridgeDevice(SoftDevice):
@property
def ports(self):
- numbered_ports, port_lines = self._get_port_info()
- ports = {}
-
- for line in port_lines:
- if not re.search('type=', line):
- self._line_to_port_number(line, numbered_ports, ports)
+ ports = self._list_ports()
+ interfaces = self._list_interfaces()
- return ports
+ filtered_ports = {}
- @property
- def internal_ports(self):
- numbered_ports, port_lines = self._get_port_info()
- int_ports = {}
+ for port in ports:
+ port_iface_uuid = port['interfaces']
+ port_ifaces = [ iface for iface in interfaces if iface['_uuid'] ==
port_iface_uuid ]
+ if len(port_ifaces):
+ port_iface = port_ifaces[0]
+ filtered_ports[port['name']] = {
+ 'interface': port_iface['name'],
+ 'type': port_iface['type'],
+ 'options': port_iface['options'],
+ }
- for line in port_lines:
- if re.search('type=internal', line):
- line = re.sub(r",?\stype=internal", "", line)
- self._line_to_port_number(line, numbered_ports, int_ports)
-
- return int_ports
+ return filtered_ports
@property
def tunnels(self):
- numbered_ports, port_lines = self._get_port_info()
- tunnels = {}
+ tunnels = self.ports.copy()
- for line in port_lines:
- if re.search('type=(?!internal)', line):
- self._line_to_port_number(line, numbered_ports, tunnels)
+ for port in self.ports.keys():
+ if tunnels[port]['type'] in ['', 'internal']:
+ del tunnels[port]
return tunnels
@@ -178,42 +220,3 @@ class OvsBridgeDevice(SoftDevice):
break
return pprint.pformat(flows[1:])
-
- def _get_port_info(self):
- numbered_ports = {}
- port_lines = []
-
- dumped_ports = exec_cmd("ovs-ofctl dump-ports-desc %s" %
- self.name, log_outputs=False)[0]
-
- for match in re.finditer(r'(\w+)\((\w*)\)',
- dumped_ports):
- numbered_ports[match.groups()[1]] = match.groups()[0]
-
- ovs_show = exec_cmd("ovs-vsctl show",
- log_outputs=False)[0]
- regex = r'(Port[\w\W]*?)(?=Port|ovs_version)'
-
- for match in re.finditer(regex, ovs_show):
- line = match.groups()[0].replace('\n', ' ')
- line = self._port_format(line)
- port_lines.append(line)
-
- return numbered_ports, port_lines
-
- def _port_format(self, line):
- res = re.sub(r":", "", line)
- res = re.sub(r"(\S[^,])\s(\S)", "\\1=\\2", res)
- res = re.sub(r"\s{2,}(?=\S)", ", ", res)
- res = re.sub(r"\s*$", "", res)
-
- return res
-
- def _line_to_port_number(self, line, ref, result):
- name = re.match(r"Port=\"(\w+)\"", line).groups()[0]
-
- try:
- number = ref[name]
- result[number] = line
- except KeyError:
- pass
diff --git a/lnst/Recipes/ENRT/NoVirtOvsVxlanRecipe.py
b/lnst/Recipes/ENRT/NoVirtOvsVxlanRecipe.py
index 0c7b7520..3fbb3845 100644
--- a/lnst/Recipes/ENRT/NoVirtOvsVxlanRecipe.py
+++ b/lnst/Recipes/ENRT/NoVirtOvsVxlanRecipe.py
@@ -67,8 +67,8 @@ class NoVirtOvsVxlanRecipe(CommonHWSubConfigMixin, BaseEnrtRecipe):
for dev in config.test_wide_devices
]),
"\n".join([
- "Configured {}.{}.internal_ports = {}".format(
- dev.host.hostid, dev.name, dev.internal_ports
+ "Configured {}.{}.ports = {}".format(
+ dev.host.hostid, dev.name, dev.ports
)
for dev in [host1.br0, host2.br0]
]),
--
2.21.1