From: Ondrej Lichtner <olichtne(a)redhat.com>
This extends the LNST supported XML configuration of Open vSwitch
bridges. It now supports internal interfaces, tunnels and flow entries.
An example XML configuration now looks like this:
<ovs_bridge id="bridge1">
<internal id="int0">
<addresses>
<address value="192.168.100.1/24"/>
</addresses>
<options>
<option name="ofport_request" value="5"/>
<option name="name" value="int0"/>
</options>
</internal>
<tunnel id="vxlan1" type="vxlan">
<options>
<option name="option:remote_ip"
value="192.168.1.2"/>
<option name="option:key" value="flow"/>
<option name="ofport_request" value="10"/>
</options>
</tunnel>
<flow_entries>
<entry>table=0,in_port=5,actions=set_field:100->tun_id,output:10</entry>
<entry>table=0,in_port=10,tun_id=100,actions=output:5</entry>
<entry>table=0,priority=100,actions=drop</entry>
</flow_entries>
</ovs_bridge>
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/RecipeParser.py | 123 +++++++++++++++++++++++++++++++++-------
schema-recipe.rng | 76 ++++++++++++++++++++-----
2 files changed, 166 insertions(+), 33 deletions(-)
diff --git a/lnst/Controller/RecipeParser.py b/lnst/Controller/RecipeParser.py
index 827279f..09233a7 100644
--- a/lnst/Controller/RecipeParser.py
+++ b/lnst/Controller/RecipeParser.py
@@ -70,13 +70,23 @@ class RecipeParser(XmlParser):
else:
unique_ids.append(interface['id'])
- if interface['type'] != 'lo':
- continue
- elif interface['netns'] in lo_netns:
- msg = "Only one loopback device per netns is allowed."
- raise RecipeError(msg, interface_tag)
- else:
- lo_netns.append(interface['netns'])
+ if interface['type'] == 'lo':
+ if interface['netns'] in lo_netns:
+ msg = "Only one loopback device per netns "\
+ "is allowed."
+ raise RecipeError(msg, interface_tag)
+ else:
+ lo_netns.append(interface['netns'])
+ elif interface['type'] == "ovs_bridge":
+ ovs_conf = interface["ovs_conf"]
+ for i in ovs_conf["tunnels"] +
ovs_conf["internals"]:
+ if i['id'] in unique_ids:
+ msg = "Interface with ID \"%s\" has
already "\
+ "been defined for this machine." %\
+ i['id']
+ raise RecipeError(msg, i)
+ else:
+ unique_ids.append(i['id'])
machine["interfaces"].extend(interfaces)
@@ -125,14 +135,8 @@ class RecipeParser(XmlParser):
# addresses
addresses_tag = iface_tag.find("addresses")
- if addresses_tag is not None and len(addresses_tag) > 0:
- iface["addresses"] = XmlCollection(addresses_tag)
- for addr_tag in addresses_tag:
- if self._has_attribute(addr_tag, "value"):
- addr = self._get_attribute(addr_tag, "value")
- else:
- addr = self._get_content(addr_tag)
- iface["addresses"].append(addr)
+ addrs = self._process_addresses(addresses_tag)
+ iface["addresses"] = addrs
if iface["type"] == "eth":
iface["network"] = self._get_attribute(iface_tag,
"label")
@@ -210,12 +214,17 @@ class RecipeParser(XmlParser):
ovsb_slaves = []
iface["ovs_conf"] = XmlData(slaves_tag)
- for slave_tag in slaves_tag:
- slave = XmlData(slave_tag)
- slave["id"] = str(self._get_attribute(slave_tag,
"id"))
- ovsb_slaves.append(slave["id"])
+ if slaves_tag is not None:
+ for slave_tag in slaves_tag:
+ slave = XmlData(slave_tag)
+ slave["id"] = str(self._get_attribute(slave_tag,
"id"))
+ ovsb_slaves.append(slave["id"])
- iface["slaves"].append(slave)
+ opts_tag = slave_tag.find("options")
+ opts = self._process_options(opts_tag)
+ slave["options"] = opts
+
+ iface["slaves"].append(slave)
vlan_elems = iface_tag.findall("vlan")
vlans = iface["ovs_conf"]["vlans"] = XmlData(slaves_tag)
@@ -279,8 +288,82 @@ class RecipeParser(XmlParser):
if len(opts) > 0:
bonds[bond_id]["options"] = opts
+ unique_ids = []
+ tunnels = iface["ovs_conf"]["tunnels"] =
XmlCollection(slaves_tag)
+ tunnel_elems = iface_tag.findall("tunnel")
+ for tunnel_elem in tunnel_elems:
+ tunnels.append(XmlData(tunnel_elem))
+ tunnel = tunnels[-1]
+ tunnel["id"] = str(self._get_attribute(tunnel_elem,
"id"))
+ if tunnel["id"] in unique_ids:
+ msg = "Tunnel with id '%s' already defined for "\
+ "this ovs_bridge." % tunnel["id"]
+ raise RecipeError(msg, tunnel_elem)
+ else:
+ unique_ids.append(tunnel["id"])
+
+ t = str(self._get_attribute(tunnel_elem, "type"))
+ tunnel["type"] = t
+
+ opts_elem = tunnel_elem.find("options")
+ opts = self._process_options(opts_elem)
+ if len(opts) > 0:
+ tunnel["options"] = opts
+
+ # addresses
+ addresses_tag = tunnel_elem.find("addresses")
+ addrs = self._process_addresses(addresses_tag)
+ tunnel["addresses"] = addrs
+
+ iface["ovs_conf"]["internals"] =
XmlCollection(slaves_tag)
+ internals = iface["ovs_conf"]["internals"]
+ internal_elems = iface_tag.findall("internal")
+ for internal_elem in internal_elems:
+ internals.append(XmlData(internal_elem))
+ internal = internals[-1]
+ internal["id"] = str(self._get_attribute(internal_elem,
"id"))
+ if internal["id"] in unique_ids:
+ msg = "Internal id '%s' already defined for "\
+ "this ovs_bridge." % internal["id"]
+ raise RecipeError(msg, internal_elem)
+ else:
+ unique_ids.append(internal["id"])
+
+ opts_elem = internal_elem.find("options")
+ opts = self._process_options(opts_elem)
+ if len(opts) > 0:
+ internal["options"] = opts
+
+ # addresses
+ addresses_tag = internal_elem.find("addresses")
+ addrs = self._process_addresses(addresses_tag)
+ internal["addresses"] = addrs
+
+ iface["ovs_conf"]["flow_entries"] =
XmlCollection(slaves_tag)
+ flow_entries = iface["ovs_conf"]["flow_entries"]
+ flow_elems = iface_tag.findall("flow_entries")
+ if len(flow_elems) == 1:
+ entries = flow_elems[0].findall("entry")
+ for entry in entries:
+ if self._has_attribute(entry, "value"):
+ flow_entries.append(self._get_attribute(entry,
+ "value"))
+ else:
+ flow_entries.append(self._get_content(entry))
+
return [iface]
+ def _process_addresses(self, addresses_tag):
+ addresses = XmlCollection(addresses_tag)
+ if addresses_tag is not None and len(addresses_tag) > 0:
+ for addr_tag in addresses_tag:
+ if self._has_attribute(addr_tag, "value"):
+ addr = self._get_attribute(addr_tag, "value")
+ else:
+ addr = self._get_content(addr_tag)
+ addresses.append(addr)
+ return addresses
+
def _process_options(self, opts_tag):
options = XmlCollection(opts_tag)
if opts_tag is not None:
diff --git a/schema-recipe.rng b/schema-recipe.rng
index ad841bb..ca06eb8 100644
--- a/schema-recipe.rng
+++ b/schema-recipe.rng
@@ -200,19 +200,24 @@
<ref name="define"/>
</optional>
- <element name="slaves">
- <interleave>
- <optional>
- <ref name="define"/>
- </optional>
+ <optional>
+ <element name="slaves">
+ <interleave>
+ <optional>
+ <ref name="define"/>
+ </optional>
- <oneOrMore>
- <element name="slave">
- <attribute name="id"/>
- </element>
- </oneOrMore>
- </interleave>
- </element>
+ <oneOrMore>
+ <element name="slave">
+ <attribute name="id"/>
+ <optional>
+ <ref name="options"/>
+ </optional>
+ </element>
+ </oneOrMore>
+ </interleave>
+ </element>
+ </optional>
<zeroOrMore>
<element name="bond">
@@ -224,7 +229,7 @@
</optional>
<optional>
- <ref name="options"/>
+ <ref name="options"/>
</optional>
<element name="slaves">
@@ -268,6 +273,51 @@
</interleave>
</element>
</zeroOrMore>
+ <zeroOrMore>
+ <element name="tunnel">
+ <attribute name="id"/>
+ <attribute name="type">
+ <choice>
+ <value>vxlan</value>
+ <value>gre</value>
+ <value>geneve</value>
+ </choice>
+ </attribute>
+ <interleave>
+ <optional>
+ <ref name="options"/>
+ </optional>
+ <optional>
+ <ref name="addresses"/>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="internal">
+ <attribute name="id"/>
+ <interleave>
+ <optional>
+ <ref name="options"/>
+ </optional>
+ <optional>
+ <ref name="addresses"/>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <element name="flow_entries">
+ <oneOrMore>
+ <element name="entry">
+ <choice>
+ <attribute name="value"/>
+ <text/>
+ </choice>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
</interleave>
</element>
</define>
--
2.8.2