[lnst] SlavePool: parallel pool machine checks
by Jiří Pírko
commit f05fc59a56a42565aaea78608d13425abc1c01d4
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 21 12:52:28 2014 +0100
SlavePool: parallel pool machine checks
This patch implements parallel pool machine availability checking.
Availability is checked by creating a nonblocking TCP socket and
connecting to the slave. This way we can connect to all the slaves at
once instead of one-by-one.
This patch also introduces proper support for multiple pool directories.
Until now all slaves were put in a single pool, this could cause name
conflicts and network label conflicts, leading to confusion for users.
From now we clearly separate individual directories and machines from
two different pool directories can't be present in a single match.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/SlavePool.py | 152 +++++++++++++++++++++++++++++-------------
1 files changed, 105 insertions(+), 47 deletions(-)
---
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index 9c54629..82737a4 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -17,6 +17,8 @@ import logging
import os
import re
import copy
+import socket
+import select
from xml.dom import minidom
from lnst.Common.Config import lnst_config
from lnst.Common.NetUtils import normalize_hwaddr
@@ -35,6 +37,7 @@ class SlavePool:
"""
def __init__(self, pool_dirs, pool_checks=True):
self._map = {}
+ self._pool_dirs = {}
self._pool = {}
self._machine_matches = []
@@ -50,33 +53,73 @@ class SlavePool:
logging.info("Checking machine pool availability.")
for pool_dir in pool_dirs:
+ self._pool_dirs[pool_dir] = {}
self.add_dir(pool_dir)
+ if len(self._pool_dirs[pool_dir]) == 0:
+ del self._pool_dirs[pool_dir]
- self._mapper.set_pool(self._pool)
+ self._mapper.set_pool_dirs(self._pool_dirs)
- def add_dir(self, pool_dir):
- logging.info("Processing pool dir '%s'" % pool_dir)
- dentries = os.listdir(pool_dir)
+ def add_dir(self, dir_path):
+ logging.info("Processing pool dir '%s'" % dir_path)
- res = []
+ pool_dir = self._pool_dirs[dir_path]
+
+ dentries = os.listdir(dir_path)
for dirent in dentries:
- m_info = self.add_file("%s/%s" % (pool_dir, dirent))
- if m_info != None:
- res.append(m_info)
+ m_id, m = self.add_file(dir_path, dirent)
+ if m_id != None and m != None:
+ pool_dir[m_id] = m
- if len(res) == 0:
+ if len(pool_dir) == 0:
logging.warn("No machines found in this directory")
max_len = 0
- for m_id, _ in res:
+ for m_id in pool_dir.keys():
if len(m_id) > max_len:
max_len = len(m_id)
- for m_id, available in res:
- if available:
- machine_spec = self._pool[m_id]
- if 'libvirt_domain' in machine_spec['params']:
+
+ if self._pool_checks:
+ check_sockets = {}
+ for m_id, m in pool_dir.iteritems():
+ hostname = m["params"]["hostname"]
+ if "rpc_port" in m["params"]:
+ port = m["params"]["rpc_port"]
+ else:
+ port = lnst_config.get_option('environment', 'rpcport')
+
+ logging.debug("Querying machine '%s': %s:%s" %\
+ (m_id, hostname, port))
+
+ s = socket.socket()
+ s.settimeout(0)
+ try:
+ s.connect((hostname, port))
+ except:
+ pass
+ check_sockets[s] = m_id
+
+ while len(check_sockets) > 0:
+ rl, wl, el = select.select([], check_sockets.keys(), [])
+ for s in wl:
+ err = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ m_id = check_sockets[s]
+ if err == 0:
+ pool_dir[m_id]["available"] = True
+ del check_sockets[s]
+ else:
+ pool_dir[m_id]["available"] = False
+ del check_sockets[s]
+ else:
+ for m_id in pool_dir.keys():
+ pool_dir[m_id]["available"] = True
+
+ for m_id in list(pool_dir.keys()):
+ m = pool_dir[m_id]
+ if m["available"]:
+ if 'libvirt_domain' in m['params']:
libvirt_msg = " libvirt_domain: %s" %\
- machine_spec['params']['libvirt_domain']
+ m['params']['libvirt_domain']
else:
libvirt_msg = ""
msg = "%s%s [%s] %s" % (m_id, (max_len - len(m_id)) * " ",
@@ -85,10 +128,13 @@ class SlavePool:
else:
msg = "%s%s [%s]" % (m_id, (max_len - len(m_id)) * " ",
decorate_with_preset("DOWN", "fail"))
+ del pool_dir[m_id]
logging.info(msg)
- def add_file(self, filepath):
+ def add_file(self, dir_path, dirent):
+ filepath = dir_path + "/" + dirent
+ pool_dir = self._pool_dirs[dir_path]
if os.path.isfile(filepath) and re.search("\.xml$", filepath, re.I):
dirname, basename = os.path.split(filepath)
m_id = re.sub("\.[xX][mM][lL]$", "", basename)
@@ -97,9 +143,16 @@ class SlavePool:
xml_data = parser.parse()
machine_spec = self._process_machine_xml_data(m_id, xml_data)
+ if 'libvirt_domain' in machine_spec['params'] and \
+ not self._allow_virt:
+ logging.debug("libvirtd not running or allow_virtual "\
+ "disabled. Removing libvirt_domain from "\
+ "machine '%s'" % m_id)
+ del machine_spec['params']['libvirt_domain']
+
# Check if there isn't any machine with the same
# hostname or libvirt_domain already in the pool
- for pm_id, m in self._pool.iteritems():
+ for pm_id, m in pool_dir.iteritems():
pm = m["params"]
rm = machine_spec["params"]
if pm["hostname"] == rm["hostname"]:
@@ -113,32 +166,8 @@ class SlavePool:
"your pool ('%s' and '%s')." % (m_id, pm_id)
raise SlaveMachineError(msg)
- if self._pool_checks:
- available = False
-
- hostname = machine_spec["params"]["hostname"]
- if "rpc_port" in machine_spec["params"]:
- port = machine_spec["params"]["rpc_port"]
- else:
- port = lnst_config.get_option('environment', 'rpcport')
-
- logging.debug("Querying machine '%s': %s:%s" %\
- (m_id, hostname, port))
- if test_tcp_connection(hostname, port):
- available = True
-
- if 'libvirt_domain' in machine_spec['params'] and \
- not self._allow_virt:
- logging.debug("libvirtd not running or allow_virtual "\
- "disabled. Removing libvirt_domain from "\
- "machine '%s'" % m_id)
- del machine_spec['params']['libvirt_domain']
- else:
- available = True
-
- if available:
- self._pool[m_id] = machine_spec
- return (m_id, available)
+ return (m_id, machine_spec)
+ return (None, None)
def _process_machine_xml_data(self, m_id, machine_xml_data):
machine_spec = {"interfaces": {}, "params":{}}
@@ -239,7 +268,10 @@ class SlavePool:
self._map = {}
if self._map == {}:
+ self._pool = {}
return False
+ else:
+ self._pool = self._pool_dirs[self._map["pool_dir"]]
if self._map["virtual"]:
mreqs = self._mreqs
@@ -335,7 +367,10 @@ class MapperError(Exception):
class SetupMapper(object):
def __init__(self):
+ self._pool_dirs = {}
+ self._pool_dir_stack = []
self._pool = {}
+ self._pool_dir = None
self._mreqs = {}
self._unmatched_req_machines = []
self._matched_pool_machines = []
@@ -346,8 +381,8 @@ class SetupMapper(object):
def set_requirements(self, mreqs):
self._mreqs = mreqs
- def set_pool(self, pool):
- self._pool = pool
+ def set_pool_dirs(self, pool_dirs):
+ self._pool_dirs = pool_dirs
def set_virtual(self, virt_value):
self._virtual_matching = virt_value
@@ -372,6 +407,12 @@ class SetupMapper(object):
self._machine_stack = []
self._unmatched_req_machines = self._mreqs.keys()
+ self._pool_dir_stack = list(self._pool_dirs.keys())
+ if len(self._pool_dir_stack) > 0:
+ self._pool_dir = self._pool_dir_stack.pop()
+ self._pool = self._pool_dirs[self._pool_dir]
+ logging.info("Using pool dir: %s" % self._pool_dir)
+
self._unmatched_pool_machines = []
for p_id, p_machine in self._pool.iteritems():
if self._virtual_matching:
@@ -380,7 +421,7 @@ class SetupMapper(object):
else:
self._unmatched_pool_machines.append(p_id)
- if self._pool is not None or self._mreqs is not None:
+ if len(self._pool) > 0 and len(self._mreqs) > 0:
self._push_machine_stack()
def match(self):
@@ -427,6 +468,22 @@ class SetupMapper(object):
continue
else:
self._pop_machine_stack()
+ if len(self._machine_stack) == 0 and\
+ len(self._pool_dir_stack) > 0:
+ self._pool_dir = self._pool_dir_stack.pop()
+ self._pool = self._pool_dirs[self._pool_dir]
+ logging.info("Using pool dir: %s" % self._pool_dir)
+
+ self._unmatched_pool_machines = []
+ for p_id, p_machine in self._pool.iteritems():
+ if self._virtual_matching:
+ if "libvirt_domain" in p_machine["params"]:
+ self._unmatched_pool_machines.append(p_id)
+ else:
+ self._unmatched_pool_machines.append(p_id)
+
+ if len(self._pool) > 0 and len(self._mreqs) > 0:
+ self._push_machine_stack()
continue
return False
@@ -544,7 +601,8 @@ class SetupMapper(object):
return True
def get_mapping(self):
- mapping = {"machines": {}, "networks": {}, "virtual": False}
+ mapping = {"machines": {}, "networks": {}, "virtual": False,
+ "pool_dir": self._pool_dir}
for req_label, label_map in self._net_label_mapping.iteritems():
mapping["networks"][req_label] = label_map[0]
9 years, 5 months
[PATCH] SlavePool: parallel pool machine checks
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This patch implements parallel pool machine availability checking.
Availability is checked by creating a nonblocking TCP socket and
connecting to the slave. This way we can connect to all the slaves at
once instead of one-by-one.
This patch also introduces proper support for multiple pool directories.
Until now all slaves were put in a single pool, this could cause name
conflicts and network label conflicts, leading to confusion for users.
>From now we clearly separate individual directories and machines from
two different pool directories can't be present in a single match.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/SlavePool.py | 152 ++++++++++++++++++++++++++++++-------------
1 file changed, 105 insertions(+), 47 deletions(-)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index 9c54629..82737a4 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -17,6 +17,8 @@ import logging
import os
import re
import copy
+import socket
+import select
from xml.dom import minidom
from lnst.Common.Config import lnst_config
from lnst.Common.NetUtils import normalize_hwaddr
@@ -35,6 +37,7 @@ class SlavePool:
"""
def __init__(self, pool_dirs, pool_checks=True):
self._map = {}
+ self._pool_dirs = {}
self._pool = {}
self._machine_matches = []
@@ -50,33 +53,73 @@ class SlavePool:
logging.info("Checking machine pool availability.")
for pool_dir in pool_dirs:
+ self._pool_dirs[pool_dir] = {}
self.add_dir(pool_dir)
+ if len(self._pool_dirs[pool_dir]) == 0:
+ del self._pool_dirs[pool_dir]
- self._mapper.set_pool(self._pool)
+ self._mapper.set_pool_dirs(self._pool_dirs)
- def add_dir(self, pool_dir):
- logging.info("Processing pool dir '%s'" % pool_dir)
- dentries = os.listdir(pool_dir)
+ def add_dir(self, dir_path):
+ logging.info("Processing pool dir '%s'" % dir_path)
- res = []
+ pool_dir = self._pool_dirs[dir_path]
+
+ dentries = os.listdir(dir_path)
for dirent in dentries:
- m_info = self.add_file("%s/%s" % (pool_dir, dirent))
- if m_info != None:
- res.append(m_info)
+ m_id, m = self.add_file(dir_path, dirent)
+ if m_id != None and m != None:
+ pool_dir[m_id] = m
- if len(res) == 0:
+ if len(pool_dir) == 0:
logging.warn("No machines found in this directory")
max_len = 0
- for m_id, _ in res:
+ for m_id in pool_dir.keys():
if len(m_id) > max_len:
max_len = len(m_id)
- for m_id, available in res:
- if available:
- machine_spec = self._pool[m_id]
- if 'libvirt_domain' in machine_spec['params']:
+
+ if self._pool_checks:
+ check_sockets = {}
+ for m_id, m in pool_dir.iteritems():
+ hostname = m["params"]["hostname"]
+ if "rpc_port" in m["params"]:
+ port = m["params"]["rpc_port"]
+ else:
+ port = lnst_config.get_option('environment', 'rpcport')
+
+ logging.debug("Querying machine '%s': %s:%s" %\
+ (m_id, hostname, port))
+
+ s = socket.socket()
+ s.settimeout(0)
+ try:
+ s.connect((hostname, port))
+ except:
+ pass
+ check_sockets[s] = m_id
+
+ while len(check_sockets) > 0:
+ rl, wl, el = select.select([], check_sockets.keys(), [])
+ for s in wl:
+ err = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ m_id = check_sockets[s]
+ if err == 0:
+ pool_dir[m_id]["available"] = True
+ del check_sockets[s]
+ else:
+ pool_dir[m_id]["available"] = False
+ del check_sockets[s]
+ else:
+ for m_id in pool_dir.keys():
+ pool_dir[m_id]["available"] = True
+
+ for m_id in list(pool_dir.keys()):
+ m = pool_dir[m_id]
+ if m["available"]:
+ if 'libvirt_domain' in m['params']:
libvirt_msg = " libvirt_domain: %s" %\
- machine_spec['params']['libvirt_domain']
+ m['params']['libvirt_domain']
else:
libvirt_msg = ""
msg = "%s%s [%s] %s" % (m_id, (max_len - len(m_id)) * " ",
@@ -85,10 +128,13 @@ class SlavePool:
else:
msg = "%s%s [%s]" % (m_id, (max_len - len(m_id)) * " ",
decorate_with_preset("DOWN", "fail"))
+ del pool_dir[m_id]
logging.info(msg)
- def add_file(self, filepath):
+ def add_file(self, dir_path, dirent):
+ filepath = dir_path + "/" + dirent
+ pool_dir = self._pool_dirs[dir_path]
if os.path.isfile(filepath) and re.search("\.xml$", filepath, re.I):
dirname, basename = os.path.split(filepath)
m_id = re.sub("\.[xX][mM][lL]$", "", basename)
@@ -97,9 +143,16 @@ class SlavePool:
xml_data = parser.parse()
machine_spec = self._process_machine_xml_data(m_id, xml_data)
+ if 'libvirt_domain' in machine_spec['params'] and \
+ not self._allow_virt:
+ logging.debug("libvirtd not running or allow_virtual "\
+ "disabled. Removing libvirt_domain from "\
+ "machine '%s'" % m_id)
+ del machine_spec['params']['libvirt_domain']
+
# Check if there isn't any machine with the same
# hostname or libvirt_domain already in the pool
- for pm_id, m in self._pool.iteritems():
+ for pm_id, m in pool_dir.iteritems():
pm = m["params"]
rm = machine_spec["params"]
if pm["hostname"] == rm["hostname"]:
@@ -113,32 +166,8 @@ class SlavePool:
"your pool ('%s' and '%s')." % (m_id, pm_id)
raise SlaveMachineError(msg)
- if self._pool_checks:
- available = False
-
- hostname = machine_spec["params"]["hostname"]
- if "rpc_port" in machine_spec["params"]:
- port = machine_spec["params"]["rpc_port"]
- else:
- port = lnst_config.get_option('environment', 'rpcport')
-
- logging.debug("Querying machine '%s': %s:%s" %\
- (m_id, hostname, port))
- if test_tcp_connection(hostname, port):
- available = True
-
- if 'libvirt_domain' in machine_spec['params'] and \
- not self._allow_virt:
- logging.debug("libvirtd not running or allow_virtual "\
- "disabled. Removing libvirt_domain from "\
- "machine '%s'" % m_id)
- del machine_spec['params']['libvirt_domain']
- else:
- available = True
-
- if available:
- self._pool[m_id] = machine_spec
- return (m_id, available)
+ return (m_id, machine_spec)
+ return (None, None)
def _process_machine_xml_data(self, m_id, machine_xml_data):
machine_spec = {"interfaces": {}, "params":{}}
@@ -239,7 +268,10 @@ class SlavePool:
self._map = {}
if self._map == {}:
+ self._pool = {}
return False
+ else:
+ self._pool = self._pool_dirs[self._map["pool_dir"]]
if self._map["virtual"]:
mreqs = self._mreqs
@@ -335,7 +367,10 @@ class MapperError(Exception):
class SetupMapper(object):
def __init__(self):
+ self._pool_dirs = {}
+ self._pool_dir_stack = []
self._pool = {}
+ self._pool_dir = None
self._mreqs = {}
self._unmatched_req_machines = []
self._matched_pool_machines = []
@@ -346,8 +381,8 @@ class SetupMapper(object):
def set_requirements(self, mreqs):
self._mreqs = mreqs
- def set_pool(self, pool):
- self._pool = pool
+ def set_pool_dirs(self, pool_dirs):
+ self._pool_dirs = pool_dirs
def set_virtual(self, virt_value):
self._virtual_matching = virt_value
@@ -372,6 +407,12 @@ class SetupMapper(object):
self._machine_stack = []
self._unmatched_req_machines = self._mreqs.keys()
+ self._pool_dir_stack = list(self._pool_dirs.keys())
+ if len(self._pool_dir_stack) > 0:
+ self._pool_dir = self._pool_dir_stack.pop()
+ self._pool = self._pool_dirs[self._pool_dir]
+ logging.info("Using pool dir: %s" % self._pool_dir)
+
self._unmatched_pool_machines = []
for p_id, p_machine in self._pool.iteritems():
if self._virtual_matching:
@@ -380,7 +421,7 @@ class SetupMapper(object):
else:
self._unmatched_pool_machines.append(p_id)
- if self._pool is not None or self._mreqs is not None:
+ if len(self._pool) > 0 and len(self._mreqs) > 0:
self._push_machine_stack()
def match(self):
@@ -427,6 +468,22 @@ class SetupMapper(object):
continue
else:
self._pop_machine_stack()
+ if len(self._machine_stack) == 0 and\
+ len(self._pool_dir_stack) > 0:
+ self._pool_dir = self._pool_dir_stack.pop()
+ self._pool = self._pool_dirs[self._pool_dir]
+ logging.info("Using pool dir: %s" % self._pool_dir)
+
+ self._unmatched_pool_machines = []
+ for p_id, p_machine in self._pool.iteritems():
+ if self._virtual_matching:
+ if "libvirt_domain" in p_machine["params"]:
+ self._unmatched_pool_machines.append(p_id)
+ else:
+ self._unmatched_pool_machines.append(p_id)
+
+ if len(self._pool) > 0 and len(self._mreqs) > 0:
+ self._push_machine_stack()
continue
return False
@@ -544,7 +601,8 @@ class SetupMapper(object):
return True
def get_mapping(self):
- mapping = {"machines": {}, "networks": {}, "virtual": False}
+ mapping = {"machines": {}, "networks": {}, "virtual": False,
+ "pool_dir": self._pool_dir}
for req_label, label_map in self._net_label_mapping.iteritems():
mapping["networks"][req_label] = label_map[0]
--
1.9.3
9 years, 5 months
[lnst] NetTestResultSerializer: adjust xml_to_html transformation
by Jiří Pírko
commit 1dc6f97f18f1ef9250906f66b086bc9eadec2f8b
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Thu Nov 20 15:07:05 2014 +0100
NetTestResultSerializer: adjust xml_to_html transformation
This patch works around some issues with the old version of lxml library
available in RHEL6.
The first change is parsing the xslt document from a URL needs to
happen first otherwise the parser is unable to access URLs - know bug in
the old lxml library that is fixed in newer versions.
The second is that the tostring() method didn't support a doctype
parameter in older versions so instead we add the DOCTYPE manually.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/NetTestResultSerializer.py | 10 ++++------
1 files changed, 4 insertions(+), 6 deletions(-)
---
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index bf3da1e..62d4e43 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -18,7 +18,6 @@ from lnst.Common.NetTestCommand import str_command
from lnst.Common.Colours import decorate_string, decorate_with_preset
from lnst.Common.Config import lnst_config
from lxml import etree
-from lxml import html as lxml_h
def serialize_obj(obj, dom, el, upper_name="unnamed"):
if isinstance(obj, dict):
@@ -279,14 +278,13 @@ class NetTestResultSerializer:
return doc.toprettyxml()
def get_result_html(self):
+ xslt_url = lnst_config.get_option("environment", "xslt_url")
+ xslt = etree.parse(xslt_url)
+
xml = self._generate_xml().toprettyxml()
etree_xml = etree.fromstring(xml)
- xslt_url = lnst_config.get_option("environment", "xslt_url")
- xslt = etree.parse(xslt_url)
transform = etree.XSLT(xslt)
transformed_xml = transform(etree_xml)
- html = lxml_h.fromstring(etree.tostring(transformed_xml))
- return lxml_h.tostring(html, pretty_print=True,
- doctype="<!DOCTYPE html>")
+ return "<!DOCTYPE html>\n" + str(transformed_xml)
9 years, 5 months
[PATCH] NetTestResultSerializer: adjust xml_to_html transformation
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This patch works around some issues with the old version of lxml library
available in RHEL6.
The first change is parsing the xslt document from a URL needs to
happen first otherwise the parser is unable to access URLs - know bug in
the old lxml library that is fixed in newer versions.
The second is that the tostring() method didn't support a doctype
parameter in older versions so instead we add the DOCTYPE manually.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/NetTestResultSerializer.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index bf3da1e..62d4e43 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -18,7 +18,6 @@ from lnst.Common.NetTestCommand import str_command
from lnst.Common.Colours import decorate_string, decorate_with_preset
from lnst.Common.Config import lnst_config
from lxml import etree
-from lxml import html as lxml_h
def serialize_obj(obj, dom, el, upper_name="unnamed"):
if isinstance(obj, dict):
@@ -279,14 +278,13 @@ class NetTestResultSerializer:
return doc.toprettyxml()
def get_result_html(self):
+ xslt_url = lnst_config.get_option("environment", "xslt_url")
+ xslt = etree.parse(xslt_url)
+
xml = self._generate_xml().toprettyxml()
etree_xml = etree.fromstring(xml)
- xslt_url = lnst_config.get_option("environment", "xslt_url")
- xslt = etree.parse(xslt_url)
transform = etree.XSLT(xslt)
transformed_xml = transform(etree_xml)
- html = lxml_h.fromstring(etree.tostring(transformed_xml))
- return lxml_h.tostring(html, pretty_print=True,
- doctype="<!DOCTYPE html>")
+ return "<!DOCTYPE html>\n" + str(transformed_xml)
--
1.9.3
9 years, 5 months
[lnst] result_xslt: updated with new information from result xml
by Jiří Pírko
commit eecf03f3ac025237f97b9d579205434ff6f348f9
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 14 17:51:27 2014 +0100
result_xslt: updated with new information from result xml
This patch updates the XSLT transformation schme to reflect the new
information available in result xmls. This means that the HTML files now
also contain the information about the match number and what pool match
was used.
In addition to that the formatting and functionality was updated. To
make it easier to look at each command (command description and result
data) now has a border, the start of a new Task is highlighted.
There is now a button to show/hide result data of commands. Commands
that don't have any result data don't have this button and instead show
a note saying that no result data was returned. In case of background
commands that return their result data in a corresponding wait/intr/kill
command I added a highlight button that highlights this command, to make
it easier to find.
This patch breaks backwards compatibility and result html files
generated before this patch will now be displayed correctly. To fix this
you can download the old .css and .js files and fix the html file to
reference those instead.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
result_xslt/xml_to_html.css | 42 ++++
result_xslt/xml_to_html.js | 43 ++++-
result_xslt/xml_to_html.xsl | 442 +++++++++++++++++++++++--------------------
3 files changed, 313 insertions(+), 214 deletions(-)
---
diff --git a/result_xslt/xml_to_html.css b/result_xslt/xml_to_html.css
index 2f85bd7..d47c52b 100644
--- a/result_xslt/xml_to_html.css
+++ b/result_xslt/xml_to_html.css
@@ -1,3 +1,11 @@
+table{
+ border-collapse: collapse;
+}
+
+table.lnst_results {
+ border: 1px solid black;
+}
+
table.lnst_results td {
padding: 0px 10px 0px 10px;
}
@@ -14,6 +22,18 @@ table.result_data td{
vertical-align: top;
}
+tr.tr_top{
+ border-top: 1px solid black;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+}
+
+tr.tr_bottom{
+ border-bottom: 1px solid black;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+}
+
td.result_pass{
background-color: lime;
}
@@ -21,3 +41,25 @@ td.result_pass{
td.result_fail{
background-color: red;
}
+
+tr.task_header{
+ background-color: gray;
+}
+
+td.button{
+ vertical-align: top;
+}
+
+.match_description, .match_description th, .match_description td{
+ border: 1px solid black;
+ text-align: center;
+ border-collapse: collapse;
+ padding: 5px 10px 5px 10px;
+}
+
+.if_match_description{
+ border: 1px solid black;
+ text-align: center;
+ border-collapse: collapse;
+ padding: 0px 10px 0px 10px;
+}
diff --git a/result_xslt/xml_to_html.js b/result_xslt/xml_to_html.js
index 4c42b66..12176e4 100644
--- a/result_xslt/xml_to_html.js
+++ b/result_xslt/xml_to_html.js
@@ -1,21 +1,46 @@
window.toggleResultData = function (event, task_id, bg_id) {
switch_display = function(elem){
- if (elem.style.display == 'none')
+ if (elem.style.display == 'none'){
elem.style.display = '';
- else
+ }else{
elem.style.display = 'none';
+ }
+ }
+
+ cell = event.target.parentNode;
+ row = cell.parentNode;
+ result_cell_i = cell.cellIndex + 2;
+ result_cell = row.cells[result_cell_i];
+ switch_display(result_cell);
+
+ if (task_id !== undefined && bg_id !== undefined){
+ rows = row.parentNode.rows;
+ for (i = 0; i < rows.length; ++i){
+ if (rows[i].name == ("task_id="+task_id+"bg_id="+bg_id)){
+ switch_display(rows[i].cells[2]);
+ }
+ }
+ }
+}
+
+window.highlightResultData = function (event, task_id, bg_id) {
+ switch_background = function(elem){
+ if (elem.style.background == ''){
+ elem.style.background = 'lightblue';
+ }else{
+ elem.style.background = '';
+ }
}
- row = event.target.parentNode;
- result_row_i = row.rowIndex + 1;
- result_row = row.parentNode.rows[result_row_i];
- switch_display(result_row);
+ cell = event.target.parentNode;
+ row = cell.parentNode;
if (task_id !== undefined && bg_id !== undefined){
- rows = row.parentNode.childNodes;
+ rows = row.parentNode.rows;
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;
+ if (rows[i].getAttribute("name") == ("task_id="+task_id+"bg_id="+bg_id)){
+ switch_background(rows[i]);
+ }
}
}
}
diff --git a/result_xslt/xml_to_html.xsl b/result_xslt/xml_to_html.xsl
index 9a494ce..0e85d01 100644
--- a/result_xslt/xml_to_html.xsl
+++ b/result_xslt/xml_to_html.xsl
@@ -17,248 +17,201 @@
</xsl:template>
<xsl:template match="recipe">
- <h3><xsl:value-of select="@name"/></h3>
+ <h3><xsl:value-of select="@name"/> match <xsl:value-of select="@match_num"/></h3>
+ <xsl:apply-templates select="pool_match"/>
<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>
+ <tr><th colspan="5">Task</th></tr>
+ <tr><th>Host</th><th>Bg ID</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 match="pool_match">
+ <table class="match_description">
+ <tr><th colspan="3">Match description</th></tr>
+ <xsl:if test="@virtual = 'true'">
+ <tr><td colspan="3">Match is virtual</td></tr>
+ </xsl:if>
+ <tr><th>machine id</th><th>pool id</th><th>interface match description</th></tr>
+ <xsl:apply-templates select="m_match"/>
+ </table>
</xsl:template>
- <xsl:template match="command[@type='exec']">
- <xsl:param name="task_id"/>
+ <xsl:template match="m_match">
<tr>
+ <td>
+ <xsl:value-of select="@host_id"/>
+ </td>
+ <td>
+ <xsl:value-of select="@pool_id"/>
+ </td>
<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 test="if_match">
+ <td>
+ <table class="if_match_description">
+ <tr><th>interface id</th><th>pool interface id</th></tr>
+ <xsl:apply-templates select="if_match"/>
+ </table>
+ </td>
</xsl:when>
<xsl:otherwise>
- <xsl:attribute name="onclick">
- toggleResultData(event);
- </xsl:attribute>
+ <td>
+ no interface match
+ </td>
</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);">
+ <xsl:template match="if_match">
+ <tr>
<td>
- Controller
+ <xsl:value-of select="@if_id"/>
</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>
+ <xsl:value-of select="@pool_if_id"/>
</td>
</tr>
</xsl:template>
- <xsl:template match="command[@type='test']">
+ <xsl:template match="task">
+ <tr class="task_header"><th colspan="5">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">
<xsl:param name="task_id"/>
- <tr>
+ <tr class="tr_top">
+ <xsl:attribute name="name">task_id=<xsl:value-of select="$task_id"/>bg_id=<xsl:value-of select="@proc_id"/></xsl:attribute>
<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 test="@type='exec'">
+ <xsl:call-template name="cmd_exec"/>
+ </xsl:when>
+ <xsl:when test="@type='ctl_wait'">
+ <xsl:call-template name="cmd_ctl_wait"/>
+ </xsl:when>
+ <xsl:when test="@type='test'">
+ <xsl:call-template name="cmd_test"/>
+ </xsl:when>
+ <xsl:when test="@type='config'">
+ <xsl:call-template name="cmd_config"/>
+ </xsl:when>
+ <xsl:when test="@type='intr'">
+ <xsl:call-template name="cmd_intr"/>
+ </xsl:when>
+ <xsl:when test="@type='wait'">
+ <xsl:call-template name="cmd_wait"/>
+ </xsl:when>
+ <xsl:when test="@type='kill'">
+ <xsl:call-template name="cmd_kill"/>
</xsl:when>
<xsl:otherwise>
- <xsl:attribute name="onclick">
- toggleResultData(event);
- </xsl:attribute>
+ <td>
+ unknown command type
+ </td>
</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:call-template name="res_data">
+ <xsl:with-param name="task_id" select="$task_id"/>
+ </xsl:call-template>
</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 name="cmd_exec">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:if test="@bg_id">
+ <xsl:value-of select="@bg_id"/>
+ </xsl:if>
+ </td>
+ <td>
+ <xsl:value-of select="@command"/>
+ </td>
</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 name="cmd_ctl_wait">
+ <td>
+ Controller
+ </td>
+ <td>
+ </td>
+ <td>
+ wait <xsl:value-of select="@seconds"/>s
+ </td>
</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 name="cmd_test">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:if test="@bg_id">
+ <xsl:value-of select="@bg_id"/>
+ </xsl:if>
+ </td>
+ <td>
+ <xsl:value-of select="@module"/>
+ </td>
</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 name="cmd_config">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ </td>
+ <td>
+ config
+ </td>
+ </xsl:template>
+
+ <xsl:template name="cmd_intr">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:if test="@proc_id">
+ <xsl:value-of select="@proc_id"/>
+ </xsl:if>
+ </td>
+ <td>
+ interrupt
+ </td>
+ </xsl:template>
+
+ <xsl:template name="cmd_kill">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:if test="@proc_id">
+ <xsl:value-of select="@proc_id"/>
+ </xsl:if>
+ </td>
+ <td>
+ kill
+ </td>
+ </xsl:template>
+
+ <xsl:template name="cmd_wait">
+ <td>
+ <xsl:value-of select="@host"/>
+ </td>
+ <td>
+ <xsl:if test="@proc_id">
+ <xsl:value-of select="@proc_id"/>
+ </xsl:if>
+ </td>
+ <td>
+ wait
+ </td>
</xsl:template>
<xsl:template match="result">
@@ -305,7 +258,7 @@
<xsl:when test="@type = 'dict'">
<table class="result_data">
<xsl:for-each select="*">
- <xsl:call-template name="res_data_dict_item"/>
+ <xsl:call-template name="res_data_dict_item"/>
</xsl:for-each>
</table>
</xsl:when>
@@ -331,7 +284,7 @@
<xsl:when test="@type = 'dict'">
<table class="result_data">
<xsl:for-each select="*">
- <xsl:call-template name="res_data_dict_item"/>
+ <xsl:call-template name="res_data_dict_item"/>
</xsl:for-each>
</table>
</xsl:when>
@@ -340,4 +293,83 @@
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+
+ <xsl:template name="result_button">
+ <xsl:param name="task_id"/>
+ <button type="button">
+ <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>
+ Show result data
+ </button>
+ </xsl:template>
+
+ <xsl:template name="highlight_button">
+ <xsl:param name="task_id"/>
+ <button type="button">
+ <xsl:attribute name="onclick">
+ highlightResultData(event, '<xsl:value-of select="$task_id"/>', '<xsl:value-of select="@bg_id"/>');
+ </xsl:attribute>
+ Highlight result command
+ </button>
+ </xsl:template>
+
+ <xsl:template name="res_data">
+ <xsl:param name="task_id"/>
+ <xsl:choose>
+ <xsl:when test="result_data">
+ <tr class="tr_bottom">
+ <td class="button">
+ <xsl:call-template name="result_button">
+ <xsl:with-param name="task_id" select="$task_id"/>
+ </xsl:call-template>
+ </td>
+ <td>
+ </td>
+ <td colspan="3" style="display:none;">
+ <xsl:apply-templates select="result_data"/>
+ </td>
+ </tr>
+ </xsl:when>
+ <xsl:when test="@bg_id">
+ <tr class="tr_bottom">
+ <td class="button">
+ <xsl:call-template name="highlight_button">
+ <xsl:with-param name="task_id" select="$task_id"/>
+ </xsl:call-template>
+ </td>
+ <td>
+ </td>
+ <td colspan="3">
+ <div class="no_result_data">
+ Result data for background commands located in the corresponding wait/intr/kill command.
+ </div>
+ </td>
+ </tr>
+ </xsl:when>
+ <xsl:otherwise>
+ <tr class="tr_bottom">
+ <xsl:attribute name="name">task_id=<xsl:value-of select="$task_id"/>bg_id=<xsl:value-of select="@proc_id"/></xsl:attribute>
+ <td>
+ </td>
+ <td>
+ </td>
+ <td colspan="3">
+ <div class="no_result_data">
+ This command didn't provide any additional result data.
+ </div>
+ </td>
+ </tr>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
</xsl:stylesheet>
9 years, 5 months
[lnst] NetTestResultSerializer: extend xml generation
by Jiří Pírko
commit c761acc7d1e534e8cc33dbf54cff18f9ad404586
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 14 17:51:26 2014 +0100
NetTestResultSerializer: extend xml generation
This patch updates the result xml format by adding information about the
match number of the recipe and the associated pool match that was used.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/NetTestResultSerializer.py | 26 ++++++++++++++++++++++++++
1 files changed, 26 insertions(+), 0 deletions(-)
---
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index 622ba04..bf3da1e 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -204,9 +204,35 @@ class NetTestResultSerializer:
recipe_el = doc.createElement("recipe")
recipe_el.setAttribute("name", recipe["name"])
recipe_el.setAttribute("result", recipe["result"])
+ recipe_el.setAttribute("match_num", str(recipe["match_num"]))
top_el.appendChild(recipe_el)
+ match = recipe["pool_match"]
+ if match != {}:
+ match_el = doc.createElement("pool_match")
+
+ if "virtual" in match and match["virtual"]:
+ match_el.setAttribute("virtual", "true")
+ else:
+ match_el.setAttribute("virtual", "false")
+
+ for m_id, m in match["machines"].iteritems():
+ m_el = doc.createElement("m_match")
+ m_el.setAttribute("host_id", str(m_id))
+ m_el.setAttribute("pool_id", str(m["target"]))
+
+ for if_id, pool_id in m["interfaces"].iteritems():
+ if_el = doc.createElement("if_match")
+ if_el.setAttribute("if_id", str(if_id))
+ if_el.setAttribute("pool_if_id", str(pool_id))
+ m_el.appendChild(if_el)
+
+ match_el.appendChild(m_el)
+
+
+ recipe_el.appendChild(match_el)
+
if recipe["result"] == "FAIL" and \
"err_msg" in recipe and recipe["err_msg"] != "":
err_el = doc.createElement("error_message")
9 years, 5 months
[lnst] PoolWizard: don't ask for pool directory for every machine
by Jiří Pírko
commit 12d722564fd2deb6de979377c6d54ab44b6c98ec
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Fri Nov 14 16:10:26 2014 +0100
PoolWizard: don't ask for pool directory for every machine
This patch fixes an issue when user is being prompted for the pool
directory after every machine that is to be added to the pool. The fix
is to move the relevant prompt out of the machine query loop.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/Wizard.py | 12 +++++++-----
1 files changed, 7 insertions(+), 5 deletions(-)
---
diff --git a/lnst/Controller/Wizard.py b/lnst/Controller/Wizard.py
index f16c8fa..c38aae5 100644
--- a/lnst/Controller/Wizard.py
+++ b/lnst/Controller/Wizard.py
@@ -33,12 +33,14 @@ class Wizard:
named by user. User can choose which interfaces should be added to the
.xml file.
"""
+
+ pool_dir = raw_input("Enter path to pool directory "\
+ "(default: ~/.lnst/pool): ")
+ if pool_dir != "":
+ self._pool_dir = os.path.expanduser(pool_dir)
+ print "Pool directory set to %s" % self._pool_dir
+
while True:
- pool_dir = raw_input("Enter path to pool directory "\
- "(default: ~/.lnst/pool): ")
- if pool_dir != "":
- self._pool_dir = os.path.expanduser(pool_dir)
- print "Pool directory set to %s" % self._pool_dir
while True:
hostname = raw_input("Enter hostname: ")
try:
9 years, 5 months
[lnst] lnst-ctl.conf: set allow_virtual to true
by Jiří Pírko
commit 02d6b9128d71d2f77c1872f2f794931a2a7d557e
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 14 13:14:54 2014 +0100
lnst-ctl.conf: set allow_virtual to true
Since matching virtual machines is frequently used in beaker, I'm
setting the default value of allow_virtual to True. The user still needs
libvirtd to be running and machines whose slave pool xml contains the
parameter libvirt_domain in order for this to have any effect.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
install/lnst-ctl.conf.in | 2 +-
lnst-ctl.conf | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
---
diff --git a/install/lnst-ctl.conf.in b/install/lnst-ctl.conf.in
index 663a0c2..4d31483 100644
--- a/install/lnst-ctl.conf.in
+++ b/install/lnst-ctl.conf.in
@@ -54,4 +54,4 @@ log_dir = @ctl_logs_dir@
# The 'allow_virtual' option enables/disables the use of virtual machines with
# dynamic interface configuration. This feature is mostly used by LNST
# developers when testing new features.
-allow_virtual = False
+allow_virtual = True
diff --git a/lnst-ctl.conf b/lnst-ctl.conf
index e570598..d9a28e3 100644
--- a/lnst-ctl.conf
+++ b/lnst-ctl.conf
@@ -11,4 +11,4 @@ test_tool_dirs = ./test_tools
test_module_dirs = ./test_modules
log_dir = ./Logs
xslt_url = http://www.lnst-project.org/files/result_xslt/xml_to_html.xsl
-allow_virtual = False
+allow_virtual = True
9 years, 5 months
8166:产品战略与路标规划
by ������
lnst-developers:您好!
企业要的是什么?企业不是要追求优秀的产品,而是要寻求一种能持续产生优秀产品的机制和土壤!市场是产品开发的龙头,
决定了产品的方向,产品市场如何进行管理,困拢着许多成长中的企业。
本课程以实际咨询案例为基础,为学员提供一个交互式的研讨环境,在体验中学习市场管理的方法、工具,分享业界在产品市
场管理中的成功经验和失败教训。
产品市场管理――产品战略与路标规划
时・间・地・点: 2014年11月20-21日北京、11月27-28日上海、11月24-25日深圳
参・加・对・象: 企业CEO/总经理、运营总监、产品总监、产品经理、研发总监/副总、总工/技术总监、市场总监、行销总监等。
授・课・方・式: 讲师讲授 + 视频演绎 + 案例研讨 +角色扮演 + 讲师点评
学・习・费・用: 4300元/两人 ,单独一人收费2800元(含两天中餐、指定教材、证书、茶点)
报名咨询: 深圳:0755_612,880,35 北京:010_512,998,14 上海:021_310,010,70 陈 小 姐
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――
讲・师・介・绍:Charles(曹老师)
研发咨询资深顾问、国家发改委创新管理培训中心特邀讲师、清华大学国际工程项目管理研究院特邀讲师
专・业・背・景:
16年的高科技企业从业背景,具有丰富的产品策划、产品研发、产品中试、产品服务等领域的实践与管理经验。从事过产品设
计与开发、NPI、项目经理、产品经理、研发管理部经理、企业管理顾问等职务;
曾在国内某著名通信设备公司工作过7年(97~04),期间与国际顶尖咨询顾问一起工作,作为核心成员全程参与推动该公司
研发管理体系的变革工作,并作为产品经理主导了某产品线多个大型项目的产品设计、开发、中试、转产与上市工作。
研发管理咨询经验:
7年的研发管理咨询经验,主导了20多个研发管理咨询项目,项目范围涉及到市场需求、产品规划、产品开发、产品决策、技术评审
技术开发、研发组织、研发绩效、技术任职资格、项目管理、变更管理、知识管理、研发IT规划等领域。典型客户如下:
1)科达通信;2)OPPO;3)TCL家网事业部;4)苏州金龙;5)宇通重工;6)京信集团;7)福建敏讯;8)中电集团某军品研究所
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――
课・程・大・纲:
一、案例分析
1.市场细分2.目标市场3.产品定位
二、产品市场管理概述
1.产品管理的系统思维
1)企业在追求什么?2)产品成功的标准是什么?3)如何理解产品的市场成功和财务成功?
4)如何理解市场驱动产品开发?5)咨询案例分享:如何构建系统的产品管理体系
2.产品市场管理
1)什么是市场管理(Marketing Management)? 2)市场管理的定位、使命与责任
3)什么是产品/技术市场管理(Product/Technology Marketing Management)?
4)市场管理与市场需求管理的关系、市场管理与产品开发的关系
5)市场管理的整体流程框架、输入与输出6)产品市场管理组织运作模式分析(三种典型模式)
7)产品经理的责任与定位(三种典型定位分析)
三、产品市场管理业务----市场评估
1.概述:目的、主要活动、输入/输出
2.市场评估
1)环境分析2)竞争分析
3)市场分析:客户$APPEALS--APPEALS的维度及构成要素--APPEALS差距分析--客户访谈模板及要点--演练:应用$APPEALS讨论客户的购买标准
4)公司自身分析
3.市场评估输出
1)优势与劣势2)机会与威胁3)市场地图4)业务计划评估5)演练:市场地图及业务设计评估
四、产品市场管理业务----市场细分
1.概述:目的、主要活动、输入/输出
2.市场细分的定义
3.不同方式下的市场细分
4.市场细分主要活动
1)定义市场2)描述市场地图3)市场细分4)市场调研与验证5)选定细分市场
5.演练:如何对一个产品线的市场进行细分
五、产品市场管理业务----制定业务计划
1.概述:目的、主要活动、输入/输出
2.业务计划概览
3.确定细分市场目标及关键行动
4.审视营销目标
5.制定初步的方案
1)安索夫(Ansolf)矩阵和差距分析2)确定备选方案3)选择目标4)关键的前提假设5)预备行动指标6)技术生命周期
7)利润区8)价值准则9)以客户为中心业务设计
6.制定细分市场的业务战略及计划
1)快速回顾为制定战略完成的所有活动2)确定细分市场的战略目标3)确定细分市场的价值定位4)按照《业务计划要素》来制定战略
5)风险评估6)演练:制定选定细分市场的业务计划
六、产品战略与路标规划
1.产品路标是什么?
2.路标规划的输出是什么?(平台开发计划、产品开发计划、技术研究计划、资源缺口计划)
3.产品战略与路标规划流程
4.产品路标规划过程解析
1)技术、平台、产品线、产品、解决方案的关系2)产品平台的形成过程3)产品版本管理V/R/M(大版本、小版本、客户定制)
4)产品路标规划的形成(实际案例同步演练)
5.产品战略与路标规划决策评审
1)决策机制(决策团队、运作模式、支撑机制)2)决策标准(评审关键要素)
6.分享:业界产品路标规划的组织运作与支撑体系
1)市场策划部门、产品规划部门、产品开发部门2)决策组织及其支撑机构(参谋机构与秘书机构)
7.如何根据产品路标规划启动产品开发和技术研究
8.演练:选取客户公司的产品制定开发任务书
七、总结
9 years, 5 months
让繁琐的薪酬管理变得傻瓜化lnst-developers
by ������
lnst-developers(a)lists.fedorahosted.org
3E薪资设计与薪酬管理技巧 11月22-23日上海 准时开班
参・加・对・象:渴望规范和提升人力资源薪酬管理的董事长、总(副)经理、 HR总监、经理;薪资福利 经理、专员;高层经理等参加。
学・习・费・用:4500元/1人(含课-程-讲-义、午-餐、税-费、茶-点等,并含有价值2000元的3E薪资设计软件1套)
垂・询・热・线:深圳:0755-61.288.035 北京:010-51.299.814 上海:021-31.001.070
培・训・收・益:
1、全面掌握薪资、奖金体系设计的规范方法
2、掌握3E薪资体系设计的思路与操作步骤
3、科学进行岗位测评和进行薪资级别(含级差级幅度重叠度等)的现场设计
4、课程第一天提供学员“3E薪酬设计软件”进行练习,需要自带电脑,自带15个岗位的薪资数据,并提供测评要素两套、市场数据,现场
将指导学员设计出符合企业自身实际情况的新的薪酬体系
5、系统掌握薪酬设定、奖金、调薪等薪酬管理方法
讲・师・介・绍:[ 张・守・春 ]
背景:
1、1994年,毕业于清华大学电机系,随后进入联合利华(中国)从事人力资源工作;
2、1997年,赴美国新泽西州立大学学习,成为中国最早一届留美人力资源管理硕士;
3、在美国期间,担任朗讯集团人力资源总部(位于美国新泽西州)薪资管理经理;
4、回国后,历任通用国际、搜狐、ChinaRen等知名企业HR高阶,华泰保险人力资源总监;
5、先后担任新浪网、新华人寿、五矿集团等大型企业的HR顾问,设计二十余家企业薪酬咨询项目。
6、具有薪酬领域的专业海外研究功底,有丰富的外企、国企、民企的实战和咨询经验,深谙国内企业薪酬设计的误区和困惑,多次被国内
权威机构评为中国最具实战性培训师。
7、首创“3E薪资体系”,让复杂的薪酬管理变得简单化;开发“3E薪资软件”,让繁琐的薪酬管理变得傻瓜化。
张守春先生:3E薪资设计创始人,中国最早一届留美人力资源管理学硕士,高级讲师,“美国薪资协会”会员,毕业于美国新泽西州立大学
人力资源管理学硕士,被《财智》评为2005年十大杰出培训师、被“中国人力资源开发网”评为 2005年度最十佳人力资源培训师、2004年
最具实战的十大优秀培训师、先后担任世界著名跨国公司朗讯集团人力资源(美国总部)薪资管理经理,联合利华、通用、搜狐、ChinaRen
等国际知名企业人力资源高阶,华泰保险人力资源总监。前后担任新浪网、新华人寿、五矿集团有色金属公司等大型公司人事顾问,担任设
计二十余家大中小型企业薪酬咨询项目,丰富的实操经验和最新前沿理论。张先生具有薪酬领域的专业海外研究功底,有丰富的外企、国企、
民企的实战经验。张老师依据他在联合利华、朗讯等知名企业的薪酬管理经验,整合了现代流行的、规范的薪资设计技术,并且基于他的长
期专业咨询经验,在国内首倡“3E薪资设计”。该课程是薪酬领域最具实战、操作性和实用性的品牌课程,在国内风行已久。学员评价该课
程为“实际课程比期望的还好”,接受张老师3E薪酬培训的学员已累积四千人。
课・程・大・纲:
第一部 分付薪哲学
薪酬的本质是什么,如何看待它的激励性?
什么是全面薪酬管理,如何有效、长期地激励员工
薪酬工资和福利的本质区别是什么,设计目的有什么不同
薪酬体系设计的最大着力点是什么,公平还是激励?公平分哪三种公平,激励分哪两种激励。
企业文化和战略对薪酬福利设计的影响
第二部分 3E薪资设计理念
三种公平性对企业薪酬的戏剧化影响
什么是3E(薪酬的三种均衡),3E薪酬体系设计的思路是什么
如何理解外部竞争性,常见的误解和错误
公司的薪资水平在市场上的定位该如何科学确定,并且如何设定到这一定位
衡量内部均衡的公式是什么,它不平衡将导致什么问题
第三部分 岗位测评:内部均衡性
如何实现内部均衡,四种衡量岗位价值方法
内部均衡的世界知名的公式是什么
选择岗位测评要素的三个原则是什么
测评前,选择关键岗位的三个原则是什么
点值法(point-factor)岗位测评要素举例
提供常见的30个岗位测评要素
岗位测评的六个步骤是什么
如何用回归拟合的操作来校验岗位测评的结果是否准确
如何使用测评的数据结果,如何将岗位分级,什么是职级图(Grading Matrix)
岗位测评及职级图设计演练(半个小时,使用“张守春薪酬设计软件”进行岗位测评)
岗位测评该怎么组织才最合理,由谁来测,测评小组该如何构成,包括什么成员,常见错误
岗位测评注意事项和常见问题
职级图在招聘、晋升核定工资中的用途
第四部分 企业薪酬体系设计
a.工资级别设计
(用计算机演练一个小时,建议携带企业自身的实际工资数据,现场送给学员一个分十七步的《张守春薪酬
设计软件》,指导学员按步骤设计出符合企业实际的新的薪酬体系)
一个企业搞多少级工资好,级别数量如何确定
两级工资的级差的计算公式是什么
根据个体均衡性,各级工资的级幅度设定有什么规律和实际运用上的意义
两级工资之间的重叠度该如何设定,实际运用中的意义是什么
利用“张守春薪酬设计软件”,使用本企业事先需要准备出的20-30个岗位,涉及本公司的各个部门和高中
低各层级,以及这些岗位目前的工资,以及市场价格,指导学员利用本企业这些数据,使用软件,设计出企
业的各级别工资标准和市场曲线等等,包括级差、级幅度、重叠度等概念和计算设定。最后得到一个本企业
可用的薪酬标准和制度体系。并且比较现有人员工资与新体系的差异,找出其中需要调整的个体。
薪酬结构的构成的讲解,固定收入、变动收入的设计方法与常见错误
“薪点制”、“3P理论”等薪酬设计思路,与“3E”理念的差异和优势和共性各是什么
薪酬体系常见错误――割裂的内部不平衡的薪酬体系与虚假的岗位工资体系
企业的基本薪酬体系与岗位价值联系不大,工资体系更多是与行政级别和资历挂钩,该如何对它进行改造
岗位工资简单直接地与岗位内部价值挂钩(所谓岗位系数),而忽视外部均衡,该如何该改造
假的对岗付薪,实际是对资历付薪,该如何分辨和改正
职业生涯规划和薪酬幅度、调薪设计之间的配合关系
如何用“爵位”来回报和激励员工,在薪酬中的体现和意义
b.市场薪资调查
如何做市场薪资调查,市场薪资调查的边界该怎么确定
影响和决定一个岗位市场薪资价格高低的四个因素
如何阅读市场薪资调查报告,市场分位值是什么概念
如何把企业的薪资水平与市场曲线相比,并且画出企业薪资曲线与市场各分位曲线
领先、滞后政策分别是什么意思,在薪资设计中如何考虑使用领先、滞后政策
如何理解和解决新老员工工资的矛盾冲突
如何定期根据市场水平更新薪资曲线
第五部分 薪资管理
a.定薪
如何用所设计的薪资曲线结构为新来的人定薪
招聘时“薪资谈判”(侃价)的三招
如何为薪水期望低于标准的应聘者定薪水
如何为调动的人员定薪
如何通过薪酬体系地设计来解决新老员工工资冲突
解决薪酬水平低于标准的人员(绿圈)的科学合理方法
解决薪酬水平超标的人员(红圈)的十种方法
b.调薪
如何改变自己在调薪管理中被迫使用“拖、硬、卡”的尴尬被动局面
薪资调整的种类(晋升调薪、业绩调薪、结构化调薪)
如果老板问你,我们的实际薪资水平到哪儿了,你如何回答?如何利用CR来标识薪资水平。
如何使用CR来进行年度调薪
给张三调薪,李四也要求调薪,怎么办?不同的人各调多少薪,如何量化
如何根据企业总的薪资调幅和个人业绩,设计调薪矩阵,指导各部门和人员的调薪
如何从制度上避免部门经理为了争取调薪而拼命给下属打高分,或者把自己喜欢的下属打高
如何理解和确定年度调薪的百分比
调薪会增加成本吗?如何正确说服总经理理解调薪对成本的影响,并且批准调薪计划;
晋升的情况下如何调薪,降职呢
c.奖金
如何设定绩效工资(奖金)的比例
企业为员工发放绩效工资的计算公式
绩效考核系数矩阵的设定,以及系数的弹性设置
关于绩效工资或奖金的讨论
d.薪酬的管理和沟通
如何看待给予部门经理相应的合理的薪资管理权限
如何做好薪酬保密
如何加强在薪酬管理中的必要沟通,以及沟通的内容和技巧
如何用三种均衡性的原理(3E)去解释和沟通当下属薪酬高于上级,新来员工高于老员工的争议情况
怎么写企业的薪资政策,它包括哪些内容
第六部分 多种薪资体系分析
技能工资(skill-based)的设计和优点缺点是什么
宽幅薪资结构和传统岗位薪资结构的质的区别和使用方法
长期激励的设计方式
福利和股票期权
第七部分 案例演示
张守春老师所作过的一个电子制造企业薪酬体系咨询设计案例的实际过程展示、讲解
9 years, 5 months