[lnst] InterfaceManager: reconstruct upper/lower hierarchy
by Jiří Pírko
commit e7b3361f5dddb5ba865ecb74960abdacb79cf45a
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Mon May 12 15:43:33 2014 +0200
InterfaceManager: reconstruct upper/lower hierarchy
This patch makes the internal Device objects reconstruct the upper/lower
device hierarchy that can be found in the kernel. This is usefull when
we are deconfiguring devices without information from the kernel - the
upper and lower references will ensure the proper ordering of
deconfiguration.
Internal variables are named like on the controller:
upper == master
lower == slaves
master["primary"] is the primary master of an interface (IFLA_MASTER)
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Slave/InterfaceManager.py | 96 +++++++++++++++++++++++++++++++++-------
1 files changed, 79 insertions(+), 17 deletions(-)
---
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py
index 43c53eb..d00ee4d 100644
--- a/lnst/Slave/InterfaceManager.py
+++ b/lnst/Slave/InterfaceManager.py
@@ -91,6 +91,7 @@ class InterfaceManager(object):
self._devices[msg['index']] = dev
elif msg['header']['type'] == RTM_DELLINK:
if msg['index'] in self._devices:
+ self._devices[msg['index']].del_link()
del self._devices[msg['index']]
else:
return
@@ -186,8 +187,8 @@ class Device(object):
self._conf_dict = None
self._ip = None
self._state = None
- self._master = None
- self._parent = None
+ self._master = {"primary": None, "other": []}
+ self._slaves = []
self._if_manager = if_manager
@@ -197,7 +198,7 @@ class Device(object):
self._name = nl_msg.get_attr("IFLA_IFNAME")
self._state = nl_msg.get_attr("IFLA_OPERSTATE")
self._ip = None #TODO
- self._master = nl_msg.get_attr("IFLA_MASTER")
+ self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
self._initialized = True
@@ -206,7 +207,8 @@ class Device(object):
self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
self._name = nl_msg.get_attr("IFLA_IFNAME")
self._ip = None #TODO
- self._master = nl_msg.get_attr("IFLA_MASTER")
+ self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
+
link = nl_msg.get_attr("IFLA_LINK")
if link != None:
# IFLA_LINK is an index of device that's closer to physical
@@ -215,7 +217,12 @@ class Device(object):
# parent index in the child device; this is the opposite
# to IFLA_MASTER
link_dev = self._if_manager.get_device(link)
- link_dev.set_parent(self._if_index)
+ if link_dev != None:
+ link_dev.set_master(self._if_index, primary=False)
+ # This reference shouldn't change - you can't change the realdev
+ # of a vlan, you need to create a new vlan. Therefore the
+ # the following add_slave shouldn't be a problem.
+ self.add_slave(link)
if self._conf_dict:
self._conf_dict["name"] = self._name
@@ -228,6 +235,23 @@ class Device(object):
"hwaddr": self._hwaddr}
return None
+ def del_link(self):
+ if self._master["primary"]:
+ primary_id = self._master["primary"]
+ primary_dev = self._if_manager.get_device(primary_id)
+ if primary_dev:
+ primary_dev.del_slave(self._if_index)
+
+ for m_id in self._master["other"]:
+ m_dev = self._if_manager.get_device(m_id)
+ if m_dev:
+ m_dev.del_slave(self._if_index)
+
+ for dev_id in self._slaves:
+ dev = self._if_manager.get_device(dev_id)
+ if dev != None:
+ dev.del_master(self._if_index)
+
def get_if_index(self):
return self._if_index
@@ -260,11 +284,16 @@ class Device(object):
return self._conf
def clear_configuration(self):
- if self._master or self._parent:
- super_id = self._master if self._master else self._parent
- super_dev = self._if_manager.get_device(super_id)
- if super_dev:
- super_dev.clear_configuration()
+ if self._master["primary"]:
+ primary_id = self._master["primary"]
+ primary_dev = self._if_manager.get_device(primary_id)
+ if primary_dev:
+ primary_dev.clear_configuration()
+
+ for m_id in self._master["other"]:
+ m_dev = self._if_manager.get_device(m_id)
+ if m_dev:
+ m_dev.clear_configuration()
if self._conf != None:
self.down()
@@ -272,8 +301,36 @@ class Device(object):
self._conf = None
self._conf_dict = None
- def set_parent(self, if_index):
- self._parent = if_index
+ def set_master(self, if_index, primary=True):
+ if primary:
+ prev_master_id = self._master["primary"]
+ if prev_master_id != None and if_index != prev_master_id:
+ prev_master_dev = self._if_manager.get_device(prev_master_id)
+ if prev_master_dev != None:
+ prev_master_dev.del_slave(self._if_index)
+
+ self._master["primary"] = if_index
+ if self._master["primary"] != None:
+ master_id = self._master["primary"]
+ master_dev = self._if_manager.get_device(master_id)
+ if master_dev != None:
+ master_dev.add_slave(self._if_index)
+ elif if_index not in self._master["other"]:
+ self._master["other"].append(if_index)
+
+ def del_master(self, if_index):
+ if self._master["primary"] == if_index:
+ self._master["primary"] = None
+ elif if_index in self._master["other"]:
+ self._master["other"].remove(if_index)
+
+ def add_slave(self, if_index):
+ if if_index not in self._slaves:
+ self._slaves.append(if_index)
+
+ def del_slave(self, if_index):
+ if if_index in self._slaves:
+ self._slaves.remove(if_index)
def configure(self):
if self._conf != None and not self._configured:
@@ -281,11 +338,16 @@ class Device(object):
self._configured = True
def deconfigure(self):
- if self._master or self._parent:
- super_id = self._master if self._master else self._parent
- super_dev = self._if_manager.get_device(super_id)
- if super_dev:
- super_dev.deconfigure()
+ if self._master["primary"]:
+ primary_id = self._master["primary"]
+ primary_dev = self._if_manager.get_device(primary_id)
+ if primary_dev:
+ primary_dev.deconfigure()
+
+ for m_id in self._master["other"]:
+ m_dev = self._if_manager.get_device(m_id)
+ if m_dev:
+ m_dev.deconfigure()
if self._conf != None and self._configured:
self._conf.deconfigure()
9 years, 4 months
[lnst] InterfaceManager: use conf["name"] when Device not initialized
by Jiří Pírko
commit 5eeeefea4255ba773543787e8dd9a08d9af0335d
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Mon May 12 15:43:32 2014 +0200
InterfaceManager: use conf["name"] when Device not initialized
When using NetworkManager we don't create devices until we have created
connection objects for all the configured interfaces. The corresponding
Device objects therefore weren't initialized which caused crashes when
creating connection objects of devices that were higher in the
hierarchy. This patch fixes that.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Slave/InterfaceManager.py | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
---
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py
index 547662d..43c53eb 100644
--- a/lnst/Slave/InterfaceManager.py
+++ b/lnst/Slave/InterfaceManager.py
@@ -176,6 +176,7 @@ class InterfaceManager(object):
class Device(object):
def __init__(self, if_manager):
+ self._initialized = False
self._configured = False
self._if_index = None
@@ -198,6 +199,8 @@ class Device(object):
self._ip = None #TODO
self._master = nl_msg.get_attr("IFLA_MASTER")
+ self._initialized = True
+
def update_netlink(self, nl_msg):
if self._if_index == nl_msg['index']:
self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
@@ -217,6 +220,8 @@ class Device(object):
if self._conf_dict:
self._conf_dict["name"] = self._name
+ self._initialized = True
+
#return an update message that will be sent to the controller
return {"type": "if_update",
"devname": self._name,
@@ -248,6 +253,9 @@ class Device(object):
self._conf_dict = conf
self._conf = NetConfigDevice(conf, self._if_manager)
+ if not self._initialized:
+ self._name = conf["name"]
+
def get_configuration(self):
return self._conf
9 years, 4 months
[lnst] Machine: allow multiple non-primary interface masters
by Jiří Pírko
commit 94d1d272e99354523c720e4e0a5d79ecd7cb1f5f
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Mon May 12 15:43:31 2014 +0200
Machine: allow multiple non-primary interface masters
In the kernel a net_device uses two lists (upper and lower) to create
a hierarchy between the interfaces. This patch makes LNST reflect that
hierarchy - lower == Interface._slaves and upper == Interface._master
the patch also sepparates the master list into two parts - the _primary_
master (e.g. bridge, bond, team) and _other_ masters (e.g. vlan
interfaces). There can only be one primary master!
Since the other masters are not required by the slave configuration
(yet) the Slave modules don't have to be changed.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/Machine.py | 35 +++++++++++++++++++++++------------
1 files changed, 23 insertions(+), 12 deletions(-)
---
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index e2f1d3e..56566a5 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -126,7 +126,7 @@ class Machine(object):
ind1 = 0
ind2 = 0
for i in ordered_list:
- master = i.get_master()
+ master = i.get_primary_master()
if master != None:
ind1 = ordered_list.index(i)
ind2 = ordered_list.index(master)
@@ -391,7 +391,7 @@ class Interface(object):
self._addresses = []
self._options = []
- self._master = None
+ self._master = {"primary": None, "other": []}
self._ovs_conf = None
@@ -431,19 +431,26 @@ class Interface(object):
def set_option(self, name, value):
self._options.append((name, value))
- def set_master(self, master):
- if self._master != None:
- msg = "Interface %s already has a master." % self._master.get_id()
+ def add_master(self, master, primary=True):
+ if primary and self._master["primary"] != None:
+ msg = "Interface %s already has a primary master."\
+ % self._master.get_id()
raise MachineError(msg)
else:
- self._master = master
+ if primary:
+ self._master["primary"] = master
+ else:
+ self._master["other"].append(master)
- def get_master(self):
- return self._master
+ def get_primary_master(self):
+ return self._master["primary"]
def add_slave(self, iface):
self._slaves[iface.get_id()] = iface
- iface.set_master(self)
+ if self._type in ["vlan"]:
+ iface.add_master(self, primary=False)
+ else:
+ iface.add_master(self)
def set_slave_option(self, slave_id, name, value):
if slave_id not in self._slave_options:
@@ -473,10 +480,14 @@ class Interface(object):
"addresses": self._addresses, "slaves": self._slaves.keys(),
"options": self._options,
"slave_options": self._slave_options,
- "master": None, "ovs_conf": self._ovs_conf}
+ "master": None, "other_masters": [],
+ "ovs_conf": self._ovs_conf}
+
+ if self._master["primary"] != None:
+ config["master"] = self._master["primary"].get_id()
- if self._master != None:
- config["master"] = self._master.get_id()
+ for m in self._master["other"]:
+ config["other_masters"].append(m.get_id())
return config
9 years, 4 months
[lnst] RecipeParser: sepparate vlan parsing/validation
by Jiří Pírko
commit 7146d553c3fd54b48ea9c7cd41210b68ac4722f8
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Mon May 12 15:43:30 2014 +0200
RecipeParser: sepparate vlan parsing/validation
This commit separates the parsing/validation of <vlan> elements from the
group of virtual devices. The main reason is to check that the element
has _exactly one_ slave defined. Checking this in a different place
would be problematic...
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/RecipeParser.py | 23 ++++++++++++++++++++++-
1 files changed, 22 insertions(+), 1 deletions(-)
---
diff --git a/lnst/Controller/RecipeParser.py b/lnst/Controller/RecipeParser.py
index 046d6d9..df0b47a 100644
--- a/lnst/Controller/RecipeParser.py
+++ b/lnst/Controller/RecipeParser.py
@@ -105,7 +105,7 @@ class RecipeParser(XmlParser):
if iface["type"] == "eth":
iface["network"] = self._get_attribute(iface_tag, "label")
- elif iface["type"] in ["bond", "bridge", "vlan", "macvlan", "team"]:
+ elif iface["type"] in ["bond", "bridge", "macvlan", "team"]:
# slaves
slaves_tag = iface_tag.find("slaves")
if slaves_tag is not None and len(slaves_tag) > 0:
@@ -127,6 +127,27 @@ class RecipeParser(XmlParser):
opts = self._proces_options(opts_tag)
if len(opts) > 0:
iface["options"] = opts
+ elif iface["type"] in ["vlan"]:
+ # real_dev of the VLAN interface
+ slaves_tag = iface_tag.find("slaves")
+ if slaves_tag is None or len(slaves_tag) != 1:
+ msg = "VLAN '%s' need exactly one slave definition."\
+ % iface["id"]
+ raise RecipeError(msg, vlan)
+
+ iface["slaves"] = XmlCollection(slaves_tag)
+
+ slave_tag = slaves_tag[0]
+ slave = XmlData(slave_tag)
+ slave["id"] = self._get_attribute(slave_tag, "id")
+
+ iface["slaves"].append(slave)
+
+ # interface options
+ opts_tag = iface_tag.find("options")
+ opts = self._proces_options(opts_tag)
+ if len(opts) > 0:
+ iface["options"] = opts
elif iface["type"] == "ovs_bridge":
slaves_tag = iface_tag.find("slaves")
iface["slaves"] = XmlCollection(slaves_tag)
9 years, 4 months
[lnst] ResultSerializer: add type attribute to serialized objects
by Jiří Pírko
commit 4283b0a2dfcff5ee84b8d614ca627ad60f4c9766
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Tue May 6 10:01:57 2014 +0200
ResultSerializer: add type attribute to serialized objects
We serialize the result_data structure automatically into an arbitrary
xml file. This is not well suited for processing by an XSLT stylesheet.
This patch adds a 'type' attribute to created elements that the XSLT
stylesheet can use to determine the proper transformation.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/NetTestResultSerializer.py | 14 +++++++++-----
1 files changed, 9 insertions(+), 5 deletions(-)
---
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index 61f6c79..3689835 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -20,16 +20,20 @@ from lnst.Common.Config import lnst_config
def serialize_obj(obj, dom, el, upper_name="unnamed"):
if isinstance(obj, dict):
for key in obj:
- if upper_name == "options":
- new_el = dom.createElement("option")
- new_el.setAttribute("name", key)
- else:
- new_el = dom.createElement(key)
+ new_el = dom.createElement(key)
+ if isinstance(obj[key], dict):
+ new_el.setAttribute("type", "dict")
+ elif isinstance(obj[key], list):
+ new_el.setAttribute("type", "list")
el.appendChild(new_el)
serialize_obj(obj[key], dom, new_el, upper_name=key)
elif isinstance(obj, list):
for one in obj:
new_el = dom.createElement("%s_item" % upper_name)
+ if isinstance(one, dict):
+ new_el.setAttribute("type", "dict")
+ elif isinstance(one, list):
+ new_el.setAttribute("type", "list")
el.appendChild(new_el)
serialize_obj(one, dom, new_el)
else:
9 years, 4 months
[lnst] ResultSerializer: use the xslt_url option
by Jiří Pírko
commit 4a99839972db032682865cd78072d35ad6c10f11
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Tue May 6 10:01:56 2014 +0200
ResultSerializer: use the xslt_url option
This patch makes it so that the XSLT reference is automatically added
when creating a result xml file.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/NetTestResultSerializer.py | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
---
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index a7702fc..61f6c79 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -15,6 +15,7 @@ import logging
from xml.dom.minidom import getDOMImplementation
from lnst.Common.NetTestCommand import str_command
from lnst.Common.Colours import decorate_string, decorate_with_preset
+from lnst.Common.Config import lnst_config
def serialize_obj(obj, dom, el, upper_name="unnamed"):
if isinstance(obj, dict):
@@ -157,7 +158,12 @@ class NetTestResultSerializer:
def get_result_xml(self):
impl = getDOMImplementation()
doc = impl.createDocument(None, "results", None)
+
+ xslt_url = lnst_config.get_option("environment", "xslt_url")
+ proc_inst = doc.createProcessingInstruction('xml-stylesheet',
+ 'type="text/xsl" href="'+xslt_url+'"')
top_el = doc.documentElement
+ doc.insertBefore(proc_inst, top_el)
for recipe in self._results:
recipe_el = doc.createElement("recipe")
9 years, 4 months
[lnst] Config: add xslt_url option
by Jiří Pírko
commit 83e1735dc85c31f4240729421da299b8351c5b05
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Tue May 6 10:01:55 2014 +0200
Config: add xslt_url option
This is a Controller specific option, the value is a path or a URL to
the XSLT document that should be referenced in a result xml file if the
option -x is supplied.
The default value points to the xml_to_html.xsl file in this repository
but this should change shortly when we have the files uploaded
somewhere.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst-ctl | 12 ++++++++++--
lnst-ctl.conf | 1 +
lnst/Common/Config.py | 9 +++++++++
3 files changed, 20 insertions(+), 2 deletions(-)
---
diff --git a/lnst-ctl b/lnst-ctl
index 2dad6aa..ad7ecf7 100755
--- a/lnst-ctl
+++ b/lnst-ctl
@@ -48,6 +48,8 @@ def usage(retval=0):
print " -p, --packet-capture capture and log all ongoing " \
"network communication during the test"
print " -x, --result=FILE file to write xml_result"
+ print " -s, --xslt-url=URL URL to a XSLT document that the "\
+ "result xml will reference, only usefull when -x is used as well"
print " -r, --reduce-sync reduces resource synchronization "\
"for python tasks, see documentation"
sys.exit(retval)
@@ -123,10 +125,10 @@ def main():
try:
opts, args = getopt.getopt(
sys.argv[1:],
- "dhrc:x:poma:A:",
+ "dhrc:x:s:poma:A:",
["debug", "help", "config", "result=",
"packet-capture", "disable-pool-checks", "no-colours",
- "define_alias", "override_alias", "reduce-sync"]
+ "define_alias", "override_alias", "reduce-sync", "xslt-url"]
)
except getopt.GetoptError as err:
print str(err)
@@ -154,6 +156,7 @@ def main():
debug = 0
recipe_path = None
result_path = None
+ xslt_url = None
config_path = None
packet_capture = False
pool_checks = True
@@ -182,6 +185,8 @@ def main():
store_alias(arg, overriden_aliases)
elif opt in ("-r", "--reduce-sync"):
reduce_sync = True
+ elif opt in ("-s", "--xslt-url"):
+ xslt_url = arg
if config_path is not None:
if not os.path.isfile(config_path):
@@ -190,6 +195,9 @@ def main():
else:
lnst_config.load_config(config_path)
+ if xslt_url != None:
+ lnst_config.set_option("environment", "xslt_url", xslt_url)
+
if coloured_output:
coloured_output = not lnst_config.get_option("colours",
"disable_colours")
diff --git a/lnst-ctl.conf b/lnst-ctl.conf
index 584d340..3b32697 100644
--- a/lnst-ctl.conf
+++ b/lnst-ctl.conf
@@ -10,3 +10,4 @@ machine_pool_dirs =
test_tool_dirs = ./test_tools
test_module_dirs = ./test_modules
log_dir = ./Logs
+xslt_url = ./result_xslt/xml_to_html.xsl
diff --git a/lnst/Common/Config.py b/lnst/Common/Config.py
index ffecfb6..975df5a 100644
--- a/lnst/Common/Config.py
+++ b/lnst/Common/Config.py
@@ -69,6 +69,12 @@ class Config():
"additive" : False,
"action" : self.optionPath,
"name" : "resource_dir"}
+ self._options['environment']['xslt_url'] = {
+ "value" : "./result_xslt/xml_to_html.xsl",
+ "additive" : False,
+ "action" : self.optionPlain,
+ "name" : "xslt_url"
+ }
self.colours_scheme()
@@ -263,6 +269,9 @@ class Config():
def optionBool(self, option, cfg_path):
return bool_it(option)
+
+ def optionPlain(self, option, cfg_path):
+ return option
def dump_config(self):
string = ""
9 years, 4 months
[lnst] add result_xslt folder
by Jiří Pírko
commit b5887ca8b221f2bdb17250015fbe09d96fec5cfb
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Tue May 6 10:01:54 2014 +0200
add result_xslt folder
This folder contains 3 files:
xml_to_html.xsl - a XSLT stylesheet that transforms result xml files
into a HTML file viewable in a web browser
xml_to_html.js - a javascript file that is referenced by the created
HTML file, it defines a single function that shows/hides the result
data returned by a test
xml_to_html.css - a CSS file that is referenced by the created HTML
file, it contains some basic formatting
These files should be uploaded to a 'well known' location so that they
can be properly linked which will result in a very convenient use of the
result xml files - a user opens them in a web browser, and everything
just works.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
result_xslt/xml_to_html.css | 23 +++
result_xslt/xml_to_html.js | 21 +++
result_xslt/xml_to_html.xsl | 339 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 383 insertions(+), 0 deletions(-)
---
diff --git a/result_xslt/xml_to_html.css b/result_xslt/xml_to_html.css
new file mode 100644
index 0000000..2f85bd7
--- /dev/null
+++ b/result_xslt/xml_to_html.css
@@ -0,0 +1,23 @@
+table.lnst_results td {
+ padding: 0px 10px 0px 10px;
+}
+
+.result_data{
+ background-color: lightgrey;
+}
+
+table.result_data{
+ border: 1px solid black;
+}
+
+table.result_data td{
+ vertical-align: top;
+}
+
+td.result_pass{
+ background-color: lime;
+}
+
+td.result_fail{
+ background-color: red;
+}
diff --git a/result_xslt/xml_to_html.js b/result_xslt/xml_to_html.js
new file mode 100644
index 0000000..4c42b66
--- /dev/null
+++ b/result_xslt/xml_to_html.js
@@ -0,0 +1,21 @@
+window.toggleResultData = function (event, task_id, bg_id) {
+ switch_display = function(elem){
+ if (elem.style.display == 'none')
+ elem.style.display = '';
+ else
+ elem.style.display = 'none';
+ }
+
+ row = event.target.parentNode;
+ result_row_i = row.rowIndex + 1;
+ result_row = row.parentNode.rows[result_row_i];
+ switch_display(result_row);
+
+ if (task_id !== undefined && bg_id !== undefined){
+ rows = row.parentNode.childNodes;
+ for (i = 0; i < rows.length; ++i){
+ if (rows[i].id == ("task_id="+task_id+"bg_id="+bg_id))
+ rows[i].style.display = result_row.style.display;
+ }
+ }
+}
diff --git a/result_xslt/xml_to_html.xsl b/result_xslt/xml_to_html.xsl
new file mode 100644
index 0000000..7774bbb
--- /dev/null
+++ b/result_xslt/xml_to_html.xsl
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="html" indent="yes"/>
+ <xsl:template match="/">
+ <html>
+ <body>
+ <link rel="stylesheet" type="text/css" href="./xml_to_html.xsl"/>
+ <script type="text/javascript" src="./xml_to_html.js"/>
+ <h2>LNST results</h2>
+ <xsl:apply-templates select="results/recipe"/>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="recipe">
+ <h3><xsl:value-of select="@name"/></h3>
+ <table class="lnst_results">
+ <tr><th colspan="4">Task</th></tr>
+ <tr><th>Host</th><th>Command</th><th>Result</th><th>Result message</th></tr>
+ <xsl:apply-templates select="task"/>
+ </table>
+ </xsl:template>
+
+ <xsl:template match="task">
+ <tr><th colspan="4">Task <xsl:value-of select="position()"/></th></tr>
+ <xsl:apply-templates select="command">
+ <xsl:with-param name="task_id" select="position()"/>
+ </xsl:apply-templates>
+ </xsl:template>
+
+ <xsl:template match="command[@type='exec']">
+ <xsl:param name="task_id"/>
+ <tr>
+ <xsl:choose>
+ <xsl:when test="@bg_id">
+ <xsl:attribute name="onclick">
+ toggleResultData(event, <xsl:value-of select="$task_id"/>, <xsl:value-of select="@bg_id"/>);
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="onclick">
+ toggleResultData(event);
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:value-of select="@command"/>
+ <xsl:if test="@bg_id">
+ bg_id=<xsl:value-of select="@bg_id"/>
+ </xsl:if>
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="command[@type='ctl_wait']">
+ <xsl:param name="task_id"/>
+ <tr onclick="toggleResultData(event);">
+ <td>
+ Controller
+ </td>
+ <td>
+ wait <xsl:value-of select="@seconds"/>s
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="command[@type='test']">
+ <xsl:param name="task_id"/>
+ <tr>
+ <xsl:choose>
+ <xsl:when test="@bg_id">
+ <xsl:attribute name="onclick">
+ toggleResultData(event, <xsl:value-of select="$task_id"/>, <xsl:value-of select="@bg_id"/>);
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="onclick">
+ toggleResultData(event);
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:value-of select="@module"/>
+ <xsl:if test="@bg_id">
+ bg_id=<xsl:value-of select="@bg_id"/>
+ </xsl:if>
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="command[@type='config']">
+ <xsl:param name="task_id"/>
+ <tr onclick="toggleResultData(event);">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ config
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="command[@type='intr']">
+ <xsl:param name="task_id"/>
+ <tr onclick="toggleResultData(event);">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ interrupt bg_id=<xsl:value-of select="@proc_id"/>
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <xsl:attribute name="id">task_id=<xsl:value-of select="$task_id"/>bg_id=<xsl:value-of select="@proc_id"/></xsl:attribute>
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="command[@type='kill']">
+ <xsl:param name="task_id"/>
+ <tr onclick="toggleResultData(event);">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ kill bg_id=<xsl:value-of select="@proc_id"/>
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <xsl:attribute name="id">task_id=<xsl:value-of select="$task_id"/>bg_id=<xsl:value-of select="@proc_id"/></xsl:attribute>
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="command[@type='wait']">
+ <xsl:param name="task_id"/>
+ <tr onclick="toggleResultData(event);">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ wait for bg_id=<xsl:value-of select="@proc_id"/>
+ </td>
+ <xsl:apply-templates select="result"/>
+ </tr>
+ <tr style="display:none;">
+ <xsl:attribute name="id">task_id=<xsl:value-of select="$task_id"/>bg_id=<xsl:value-of select="@proc_id"/></xsl:attribute>
+ <td></td>
+ <td colspan="3">
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <xsl:apply-templates select="result_data"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div class="result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="result">
+ <xsl:choose>
+ <xsl:when test="@result='PASS'">
+ <td class="result_pass">PASSED</td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td class="result_fail">FAILED</td>
+ </xsl:otherwise>
+ </xsl:choose>
+ <td>
+ <xsl:if test="message">
+ <xsl:value-of select="message"/>
+ </xsl:if>
+ </td>
+ </xsl:template>
+
+ <xsl:template match="result_data">
+ <table class="result_data">
+ <th>Result Data:</th>
+ <xsl:for-each select="*">
+ <xsl:call-template name="res_data_dict_item"/>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+ <xsl:template name="res_data_dict_item">
+ <tr>
+ <td>
+ <xsl:value-of select="local-name()"/>
+ </td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="@type = 'list'">
+ <ul>
+ <xsl:for-each select="*">
+ <li>
+ <xsl:call-template name="res_data_list_item"/>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:when>
+ <xsl:when test="@type = 'dict'">
+ <table class="result_data">
+ <xsl:for-each select="*">
+ <xsl:call-template name="res_data_dict_item"/>
+ </xsl:for-each>
+ </table>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="text()"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template name="res_data_list_item">
+ <xsl:choose>
+ <xsl:when test="@type = 'list'">
+ <ul>
+ <xsl:for-each select="*">
+ <li>
+ <xsl:call-template name="res_data_list_item"/>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:when>
+ <xsl:when test="@type = 'dict'">
+ <table class="result_data">
+ <xsl:for-each select="*">
+ <xsl:call-template name="res_data_dict_item"/>
+ </xsl:for-each>
+ </table>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="text()"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+</xsl:stylesheet>
9 years, 4 months
[lnst] Controller: rename 'machine' to 'host'
by Jiří Pírko
commit f2275631ff1b64314257cf458be5a88bcd277fdd
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Tue May 6 10:01:53 2014 +0200
Controller: rename 'machine' to 'host'
This commit renames the internally used 'machine' keyword to 'host', and
'command_sequence' to 'task' also updating relevant output messages. The
reson for this is the automatic generation of result xmls, that use this
keyword and were inconsistent with the recipe xml format.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Common/NetTestCommand.py | 8 ++--
lnst/Controller/NetTestController.py | 44 ++++++++++++++--------------
lnst/Controller/NetTestResultSerializer.py | 14 ++++----
lnst/Controller/RecipeParser.py | 6 ++--
lnst/Controller/Task.py | 10 +++---
5 files changed, 41 insertions(+), 41 deletions(-)
---
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index 47a3e4a..7b45b33 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -25,7 +25,7 @@ def str_command(command):
attrs = ["type(%s)" % command["type"]]
if command["type"] == "test":
attrs.append("module(%s)" % command["module"])
- attrs.append("host(%s)" % command["machine"])
+ attrs.append("host(%s)" % command["host"])
if "bg_id" in command:
attrs.append("bg_id(%s)" % command["bg_id"])
@@ -33,7 +33,7 @@ def str_command(command):
attrs.append("timeout(%s)" % command["timeout"])
elif command["type"] == "exec":
attrs.append("command(%s)" % command["command"])
- attrs.append("machine(%s)" % command["machine"])
+ attrs.append("host(%s)" % command["host"])
if "from" in command:
attrs.append("from(%s)" % command["from"])
@@ -42,10 +42,10 @@ def str_command(command):
if "timeout" in command:
attrs.append("timeout(%s)" % command["timeout"])
elif command["type"] in ["wait", "intr", "kill"]:
- attrs.append("machine(%s)" % command["machine"])
+ attrs.append("host(%s)" % command["host"])
attrs.append("bg_id(%s)" % command["proc_id"])
elif command["type"] == "config":
- attrs.append("machine(%s)" % command["machine"])
+ attrs.append("host(%s)" % command["host"])
if "option" in command:
attrs.append("option(%s)" % command["option"])
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index 7324a7c..400cd19 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -105,8 +105,8 @@ class NetTestController:
# There must be some machines specified in the recipe
if "machines" not in recipe or \
("machines" in recipe and len(recipe["machines"]) == 0):
- msg = "No machines specified in the recipe. At least two " \
- "machines are required to perform a network test."
+ msg = "No hosts specified in the recipe. At least two " \
+ "hosts are required to perform a network test."
raise RecipeError(msg, recipe)
# machine requirements
@@ -122,7 +122,7 @@ class NetTestController:
if "params" in machine:
for p in machine["params"]:
if p["name"] in params:
- msg = "Parameter '%s' of machine %s was specified " \
+ msg = "Parameter '%s' of host %s was specified " \
"multiple times. Overriding the previous value." \
% (p["name"], m_id)
logging.warn(RecipeError(msg, p))
@@ -133,14 +133,14 @@ class NetTestController:
# Each machine must have at least one interface
if "interfaces" not in machine or \
("interfaces" in machine and len(machine["interfaces"]) == 0):
- msg = "Machine '%s' has no interfaces specified." % m_id
+ msg = "Hot '%s' has no interfaces specified." % m_id
raise RecipeError(msg, machine)
ifaces = {}
for iface in machine["interfaces"]:
if_id = iface["id"]
if if_id in ifaces:
- msg = "Interface with id='%s' already exists on machine " \
+ msg = "Interface with id='%s' already exists on host " \
"'%s'." % (if_id, m_id)
iface_type = iface["type"]
@@ -152,7 +152,7 @@ class NetTestController:
for i in iface["params"]:
if i["name"] in iface_params:
msg = "Parameter '%s' of interface %s of " \
- "machine %s was defined multiple times. " \
+ "host %s was defined multiple times. " \
"Overriding the previous value." \
% (i["name"], if_id, m_id)
logging.warn(RecipeError(msg, p))
@@ -197,14 +197,14 @@ class NetTestController:
if sp.is_setup_virtual() and os.geteuid() != 0:
msg = "Provisioning this setup requires additional configuration "\
- "of the virtual machines in the pool. LNST needs root "\
+ "of the virtual hosts in the pool. LNST needs root "\
"priviledges so it can connect to qemu."
raise NetTestError(msg)
logging.info("Provisioning initialized")
for m_id in machines.keys():
provisioner = sp.get_provisioner_id(m_id)
- logging.info(" machine %s uses %s" % (m_id, provisioner))
+ logging.info(" host %s uses %s" % (m_id, provisioner))
def _prepare_machine(self, m_id, resource_sync=True):
machine = self._machines[m_id]
@@ -236,7 +236,7 @@ class NetTestController:
else:
continue
for cmd in task['commands']:
- if 'machine' not in cmd or cmd['machine'] != m_id:
+ if 'host' not in cmd or cmd['host'] != m_id:
continue
if cmd['type'] == 'test':
mod = cmd['module']
@@ -318,10 +318,10 @@ class NetTestController:
for cmd_data in task["commands"]:
cmd = {"type": cmd_data["type"]}
- if "machine" in cmd_data:
- cmd["machine"] = cmd_data["machine"]
- if cmd["machine"] not in self._machines:
- msg = "Invalid machine id '%s'." % cmd["machine"]
+ if "host" in cmd_data:
+ cmd["host"] = cmd_data["host"]
+ if cmd["host"] not in self._machines:
+ msg = "Invalid host id '%s'." % cmd["host"]
raise RecipeError(msg, cmd_data)
if cmd["type"] in ["test", "exec"]:
@@ -339,10 +339,10 @@ class NetTestController:
def _prepare_command(self, cmd_data):
cmd = {"type": cmd_data["type"]}
- if "machine" in cmd_data:
- cmd["machine"] = cmd_data["machine"]
- if cmd["machine"] not in self._machines:
- msg = "Invalid machine id '%s'." % cmd["machine"]
+ if "host" in cmd_data:
+ cmd["host"] = cmd_data["host"]
+ if cmd["host"] not in self._machines:
+ msg = "Invalid host id '%s'." % cmd["host"]
raise RecipeError(msg, cmd_data)
if "expect" in cmd_data:
@@ -412,7 +412,7 @@ class NetTestController:
if command["type"] == "ctl_wait":
continue
- machine_id = command["machine"]
+ machine_id = command["host"]
if not machine_id in bg_ids:
bg_ids[machine_id] = set()
@@ -423,7 +423,7 @@ class NetTestController:
bg_ids[machine_id].remove(bg_id)
else:
logging.error("Found command \"%s\" for bg_id \"%s\" on "
- "machine \"%s\" which was not previously "
+ "host \"%s\" which was not previously "
"defined", cmd_type, bg_id, machine_id)
err = True
@@ -432,14 +432,14 @@ class NetTestController:
if not bg_id in bg_ids[machine_id]:
bg_ids[machine_id].add(bg_id)
else:
- logging.error("Command \"%d\" uses bg_id \"%s\" on machine "
+ logging.error("Command \"%d\" uses bg_id \"%s\" on host"
"\"%s\" which is already used",
i, bg_id, machine_id)
err = True
for machine_id in bg_ids:
for bg_id in bg_ids[machine_id]:
- logging.error("bg_id \"%s\" on machine \"%s\" has no kill/wait "
+ logging.error("bg_id \"%s\" on host \"%s\" has no kill/wait "
"command to it", bg_id, machine_id)
err = True
@@ -664,7 +664,7 @@ class NetTestController:
self._res_serializer.add_cmd_result(command, cmd_res)
return cmd_res
- machine_id = command["machine"]
+ machine_id = command["host"]
machine = self._machines[machine_id]
try:
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index 7f2e6d1..a7702fc 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -86,9 +86,9 @@ class NetTestResultSerializer:
m_id_max = 0
for cmd, cmd_res in task:
- if "machine" in cmd and\
- len(cmd["machine"]) > m_id_max:
- m_id_max = len(cmd["machine"])
+ if "host" in cmd and\
+ len(cmd["host"]) > m_id_max:
+ m_id_max = len(cmd["host"])
for cmd, cmd_res in task:
self._format_command(cmd, cmd_res, output_pairs, m_id_max)
@@ -100,9 +100,9 @@ class NetTestResultSerializer:
else:
res = "FAIL"
- if "machine" in command:
- m_id = "host %s: " % command["machine"]
- m_id += " " * (m_id_max - len(command["machine"]))
+ if "host" in command:
+ m_id = "host %s: " % command["host"]
+ m_id += " " * (m_id_max - len(command["host"]))
else:
#len("ctl") == 3; len("host ") == 5; 5-3 = 2
m_id = "ctl: " + " " * (m_id_max + 2)
@@ -174,7 +174,7 @@ class NetTestResultSerializer:
recipe_el.appendChild(err_el)
for task in recipe["tasks"]:
- cmd_seq_el = doc.createElement("command_sequence")
+ cmd_seq_el = doc.createElement("task")
recipe_el.appendChild(cmd_seq_el)
for cmd, cmd_res in task:
diff --git a/lnst/Controller/RecipeParser.py b/lnst/Controller/RecipeParser.py
index 2b554d7..046d6d9 100644
--- a/lnst/Controller/RecipeParser.py
+++ b/lnst/Controller/RecipeParser.py
@@ -260,7 +260,7 @@ class RecipeParser(XmlParser):
def _process_run_cmd(self, cmd_tag):
cmd = XmlData(cmd_tag)
- cmd["machine"] = self._get_attribute(cmd_tag, "host")
+ cmd["host"] = self._get_attribute(cmd_tag, "host")
has_module = self._has_attribute(cmd_tag, "module")
has_command = self._has_attribute(cmd_tag, "command")
@@ -300,7 +300,7 @@ class RecipeParser(XmlParser):
def _process_config_cmd(self, cmd_tag):
cmd = XmlData(cmd_tag)
cmd["type"] = "config"
- cmd["machine"] = self._get_attribute(cmd_tag, "host")
+ cmd["host"] = self._get_attribute(cmd_tag, "host")
if self._has_attribute(cmd_tag, "persistent"):
cmd["persistent"] = self._get_attribute(cmd_tag, "persistent")
@@ -335,6 +335,6 @@ class RecipeParser(XmlParser):
def _process_signal_cmd(self, cmd_tag):
cmd = XmlData(cmd_tag)
cmd["type"] = cmd_tag.tag
- cmd["machine"] = self._get_attribute(cmd_tag, "host")
+ cmd["host"] = self._get_attribute(cmd_tag, "host")
cmd["bg_id"] = self._get_attribute(cmd_tag, "bg_id")
return cmd
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py
index 10c9f02..59f17c5 100644
--- a/lnst/Controller/Task.py
+++ b/lnst/Controller/Task.py
@@ -114,7 +114,7 @@ class HostAPI(object):
:return: Command result.
:rtype: dict
"""
- cmd = {"machine": str(self._id), "type": "config"}
+ cmd = {"host": str(self._id), "type": "config"}
cmd["options"] = [{"name": option, "value": value}]
cmd["persistent"] = persistent
@@ -139,7 +139,7 @@ class HostAPI(object):
:return: A handle for process.
:rtype: ProcessAPI
"""
- cmd = {"machine": str(self._id)}
+ cmd = {"host": str(self._id)}
bg_id = None
for arg, argval in kwargs.iteritems():
@@ -307,7 +307,7 @@ class ProcessAPI(object):
def wait(self):
""" Blocking wait until the command returns. """
if self._bg_id:
- cmd = {"machine": self._host,
+ cmd = {"host": self._host,
"type": "wait",
"proc_id": self._bg_id}
self._res = self._ctl._run_command(cmd)
@@ -315,7 +315,7 @@ class ProcessAPI(object):
def intr(self):
""" Interrupt the command. """
if self._bg_id:
- cmd = {"machine": self._host,
+ cmd = {"host": self._host,
"type": "intr",
"proc_id": self._bg_id}
self._res = self._ctl._run_command(cmd)
@@ -329,7 +329,7 @@ class ProcessAPI(object):
to keep the results, use 'intr' instead.
"""
if self._bg_id:
- cmd = {"machine": self._host,
+ cmd = {"host": self._host,
"type": "kill",
"proc_id": self._bg_id}
self._res = self._ctl._run_command(cmd)
9 years, 4 months
[PATCH 1/4] RecipeParser: sepparate vlan parsing/validation
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This commit separates the parsing/validation of <vlan> elements from the
group of virtual devices. The main reason is to check that the element
has _exactly one_ slave defined. Checking this in a different place
would be problematic...
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/RecipeParser.py | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/lnst/Controller/RecipeParser.py b/lnst/Controller/RecipeParser.py
index 046d6d9..df0b47a 100644
--- a/lnst/Controller/RecipeParser.py
+++ b/lnst/Controller/RecipeParser.py
@@ -105,7 +105,7 @@ class RecipeParser(XmlParser):
if iface["type"] == "eth":
iface["network"] = self._get_attribute(iface_tag, "label")
- elif iface["type"] in ["bond", "bridge", "vlan", "macvlan", "team"]:
+ elif iface["type"] in ["bond", "bridge", "macvlan", "team"]:
# slaves
slaves_tag = iface_tag.find("slaves")
if slaves_tag is not None and len(slaves_tag) > 0:
@@ -127,6 +127,27 @@ class RecipeParser(XmlParser):
opts = self._proces_options(opts_tag)
if len(opts) > 0:
iface["options"] = opts
+ elif iface["type"] in ["vlan"]:
+ # real_dev of the VLAN interface
+ slaves_tag = iface_tag.find("slaves")
+ if slaves_tag is None or len(slaves_tag) != 1:
+ msg = "VLAN '%s' need exactly one slave definition."\
+ % iface["id"]
+ raise RecipeError(msg, vlan)
+
+ iface["slaves"] = XmlCollection(slaves_tag)
+
+ slave_tag = slaves_tag[0]
+ slave = XmlData(slave_tag)
+ slave["id"] = self._get_attribute(slave_tag, "id")
+
+ iface["slaves"].append(slave)
+
+ # interface options
+ opts_tag = iface_tag.find("options")
+ opts = self._proces_options(opts_tag)
+ if len(opts) > 0:
+ iface["options"] = opts
elif iface["type"] == "ovs_bridge":
slaves_tag = iface_tag.find("slaves")
iface["slaves"] = XmlCollection(slaves_tag)
--
1.9.0
9 years, 4 months