Info about multi_match is now passed to NetTestController's init function where it's stored as attribute of the class.
This will be used in PyRecipes implementation as flag indicating whether multi_match is toggled on or off.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst-ctl | 3 ++- lnst/Controller/NetTestController.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index d61353d..b75b8a0 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -163,7 +163,8 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, defined_aliases=defined_aliases, overriden_aliases=overriden_aliases, reduce_sync=reduce_sync, - restrict_pools=pools) + restrict_pools=pools, + multi_match=multi_match) except XmlProcessingError as err: log_exc_traceback() logging.error(err) diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index 889dfa7..d3b4798 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -56,7 +56,8 @@ class NetTestController: res_serializer=None, pool_checks=True, packet_capture=False, defined_aliases=None, overriden_aliases=None, - reduce_sync=False, restrict_pools=[]): + reduce_sync=False, restrict_pools=[], + multi_match=False): self._res_serializer = res_serializer self._remote_capture_files = {} self._log_ctl = log_ctl @@ -65,6 +66,7 @@ class NetTestController: self._packet_capture = packet_capture self._reduce_sync = reduce_sync self._parser = RecipeParser(recipe_path) + self._multi_match = multi_match
self.remove_saved_machine_config()
Because XML recipes will be abandoned, we will only use aliases defined on command line. We can store them as attribute in NetTestController.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/NetTestController.py | 1 + 1 file changed, 1 insertion(+)
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index d3b4798..b2c4c97 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -66,6 +66,7 @@ class NetTestController: self._packet_capture = packet_capture self._reduce_sync = reduce_sync self._parser = RecipeParser(recipe_path) + self._defined_aliases = defined_aliases self._multi_match = multi_match
self.remove_saved_machine_config()
This attribute will help TaskAPI to detect actual run mode.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/NetTestController.py | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index b2c4c97..d1da36a 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -71,6 +71,8 @@ class NetTestController:
self.remove_saved_machine_config()
+ self.run_mode = "run" + self._machines = {} self._network_bridges = {} self._tasks = []
This method binds InterfaceAPI with corresponding Interface object and will be used in upcoming PyRecipes implementation.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/Task.py | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 178d774..aef9ebf 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -537,6 +537,9 @@ class InterfaceAPI(object): self._if = interface self._host = host
+ def init_iface(self, interface): + self._if = interface + def get_id(self): return self._if.get_id()
This patch adds these methods for HostAPI: init_host() - binds Machine and Interface objects to HostAPI and InterfaceAPI objects
init_ifaces() - binds Interface objects to InterfaceAPI (called in init_host() method)
add_interface() - creates InterfaceAPI object and also adds the interface to machine requirements dictionary
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/Task.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index aef9ebf..df2ecaa 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -195,9 +195,39 @@ class HostAPI(object): if interface.get_id() is None: continue self._ifaces[interface.get_id()] = InterfaceAPI(interface, self) - + self._if_id_seq = 0 self._bg_id_seq = 0
+ def _gen_if_id(self): + self._if_id_seq += 1 + return "if_id_%s" % self._if_id_seq + + def init_host(self, host): + self._m = host + self.init_ifaces() + + def init_ifaces(self): + for interface in self._m.get_interfaces(): + if interface.get_id() is None: + continue + self._ifaces[interface.get_id()].init_iface(interface) + + def add_interface(self, label, netns=None, params=None): + m_id = self.get_id() + if_id = self._gen_if_id() + + self._ctl.mreq[m_id]['interfaces'][if_id] = {} + self._ctl.mreq[m_id]['interfaces'][if_id]['network'] = label + self._ctl.mreq[m_id]['interfaces'][if_id]['netns'] = netns + + if params: + self._ctl.mreq[m_id]['interfaces'][if_id]['params'] = params + else: + self._ctl.mreq[m_id]['interfaces'][if_id]['params'] = {} + + self._ifaces[if_id] = InterfaceAPI(None, self) + return self._ifaces[if_id] + def get_id(self): return self._m.get_id()
Methods cleanup_slaves(), start_packet_capture() call the same named methods in NetTestController object.
packet_capture() is getter for a flag indicating whether packet capture is enabled or not.
add_host() adds newly created host (HostAPI object) to ControllerAPI's list of hosts.
init_hosts() will initialize all the hosts and its interfaces - meaning it will bind Machine objects to HostAPI objects and for each host all of its Interface objects to InterfaceAPI objects.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/Task.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index df2ecaa..c2284d8 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -39,7 +39,11 @@ class ControllerAPI(object):
def __init__(self, ctl, hosts): self._ctl = ctl + self.run_mode = ctl.run_mode self._result = True + self.first_run = True + self._m_id_seq = 0 + self.mreq = {}
self._perf_repo_api = PerfRepoAPI()
@@ -47,6 +51,29 @@ class ControllerAPI(object): for host_id, host in hosts.iteritems(): self._hosts[host_id] = HostAPI(self, host_id, host)
+ def get_mreq(self): + return self.mreq + + def cleanup_slaves(self): + self._ctl._cleanup_slaves() + + def packet_capture(self): + return self._ctl._packet_capture + + def start_packet_capture(self): + self._ctl._start_packet_capture() + + def gen_m_id(self): + self._m_id_seq += 1 + return "m_id_%s" % self._m_id_seq + + def add_host(self, host_id, handle): + self._hosts[host_id] = handle + + def init_hosts(self, hosts): + for host_id, host in hosts.iteritems(): + self._hosts[host_id].init_host(host) + def _run_command(self, command): """ An internal wrapper that allows keeping track of the
This will be used in PyRecipes for matching the topology.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/NetTestController.py | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index d1da36a..fb6ab75 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -232,6 +232,11 @@ class NetTestController:
m.wait_interface_init()
+ def set_machine_requirements(self): + mreq = Task.get_mreq() + sp = self._slave_pool + sp.set_machine_requirements(mreq) + def provision_machines(self): sp = self._slave_pool machines = self._machines
prepare_test_env methods purpose is provisioning of the setup and initializing of TaskAPI objects which represent hosts and interfaces. It will be used only in upcoming PyRecipes implementation.
init_taskapi just creates instance of TaskAPI class. This instance is now created in _run_python_task but in upcoming patches it will be removed from there.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/NetTestController.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index fb6ab75..0115d2f 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -678,6 +678,24 @@ class NetTestController:
return res
+ def prepare_test_env(self): + try: + self.provision_machines() + self.print_match_description() + if self.run_mode == "match_setup": + return True + self._prepare_network() + Task.ctl.init_hosts(self._machines) + return True + except (NoMatchError) as exc: + self._cleanup_slaves() + return False + except (KeyboardInterrupt, Exception) as exc: + msg = "Exception raised during configuration." + logging.error(msg) + self._cleanup_slaves() + raise + def _run_recipe(self): overall_res = {"passed": True}
@@ -703,6 +721,9 @@ class NetTestController:
return overall_res
+ def init_taskapi(self): + Task.ctl = Task.ControllerAPI(self) + def _run_python_task(self, task): #backup of resource table res_table_bkp = copy.deepcopy(self._resource_table)
These methods serve for calling same named NetTestController's methods by ControllerAPI object.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/Task.py | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index c2284d8..f1d26c5 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -57,12 +57,18 @@ class ControllerAPI(object): def cleanup_slaves(self): self._ctl._cleanup_slaves()
+ def set_machine_requirements(self): + self._ctl.set_machine_requirements() + def packet_capture(self): return self._ctl._packet_capture
def start_packet_capture(self): self._ctl._start_packet_capture()
+ def prepare_test_env(self): + return self._ctl.prepare_test_env() + def gen_m_id(self): self._m_id_seq += 1 return "m_id_%s" % self._m_id_seq
This patch adds several methods which will be used in upcoming PyRecipes implementation (I'll describe only those which are not self-explanatory):
breakpoint(): new functionality which will work as debugging breakpoints - it stops execution of the test and wait for user's signal to continue the test.
add_host(): prepares HostAPI object which will be initialized later (after the match).
match(): runs matching algorithm and returns True when the following code should be executed (first run of the test and/or multi match runs). It also calls the method for provisioning of the machines.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/Task.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index f1d26c5..e8a2538 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -32,6 +32,57 @@ except: # The handle to be imported from each task ctl = None
+def get_alias(alias, default=None): + return ctl.get_alias(alias, default) + +def get_mreq(): + return ctl.get_mreq() + +def wait(seconds): + return ctl.wait(seconds) + +def get_module(name, options={}): + return ctl.get_module(name, options) + +def breakpoint(): + if ctl.run_mode != "config_only": + return + raw_input("Breakpoint reached. Press enter to continue.") + +def add_host(params=None): + m_id = ctl.gen_m_id() + ctl.mreq[m_id] = {'interfaces' : {}, 'params' : {}} + handle = HostAPI(ctl, m_id) + ctl.add_host(m_id, handle) + return handle + +def match(): + ctl.cleanup_slaves() + + if ctl.first_run: + ctl.first_run = False + ctl.set_machine_requirements() + + if ctl.prepare_test_env(): + if ctl.run_mode == "match_setup": + return False + if ctl.packet_capture(): + ctl.start_packet_capture() + return True + else: + if ctl._ctl._multi_match: + if ctl.prepare_test_env(): + if ctl.run_mode == "match_setup": + return False + if ctl.packet_capture(): + ctl.start_packet_capture() + return True + else: + return False + else: + return False + + class TaskError(Exception): pass
class ControllerAPI(object):
On Thu, Jul 21, 2016 at 05:55:45PM +0200, Jiri Prochazka wrote:
This patch adds several methods which will be used in upcoming PyRecipes implementation (I'll describe only those which are not self-explanatory):
breakpoint(): new functionality which will work as debugging breakpoints - it stops execution of the test and wait for user's signal to continue the test.
add_host(): prepares HostAPI object which will be initialized later (after the match).
match(): runs matching algorithm and returns True when the following code should be executed (first run of the test and/or multi match runs). It also calls the method for provisioning of the machines.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com
lnst/Controller/Task.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index f1d26c5..e8a2538 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -32,6 +32,57 @@ except: # The handle to be imported from each task ctl = None
+def get_alias(alias, default=None):
- return ctl.get_alias(alias, default)
+def get_mreq():
- return ctl.get_mreq()
+def wait(seconds):
- return ctl.wait(seconds)
+def get_module(name, options={}):
- return ctl.get_module(name, options)
+def breakpoint():
- if ctl.run_mode != "config_only":
return
- raw_input("Breakpoint reached. Press enter to continue.")
+def add_host(params=None):
^^^^^^^^^^^
- m_id = ctl.gen_m_id()
- ctl.mreq[m_id] = {'interfaces' : {}, 'params' : {}}
^^^^^^^^^^^^^ I'm guessing the "params" argument is supposed to be used here?
- handle = HostAPI(ctl, m_id)
- ctl.add_host(m_id, handle)
- return handle
+def match():
- ctl.cleanup_slaves()
- if ctl.first_run:
ctl.first_run = False
ctl.set_machine_requirements()
if ctl.prepare_test_env():
if ctl.run_mode == "match_setup":
return False
if ctl.packet_capture():
ctl.start_packet_capture()
return True
- else:
if ctl._ctl._multi_match:
if ctl.prepare_test_env():
if ctl.run_mode == "match_setup":
return False
if ctl.packet_capture():
ctl.start_packet_capture()
return True
else:
return False
else:
return False
class TaskError(Exception): pass
class ControllerAPI(object):
2.4.11 _______________________________________________ LNST-developers mailing list lnst-developers@lists.fedorahosted.org https://lists.fedorahosted.org/admin/lists/lnst-developers@lists.fedorahoste...
This patch exports methods that will be accessible on module level.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/__init__.py | 1 + 1 file changed, 1 insertion(+)
diff --git a/lnst/__init__.py b/lnst/__init__.py index e69de29..924a8ff 100644 --- a/lnst/__init__.py +++ b/lnst/__init__.py @@ -0,0 +1 @@ +from lnst.Controller.Task import match, add_host, wait, get_alias, get_module, breakpoint
This patch implements PyRecipes and breaks existing XML recipes.
Python recipes, unlike XML recipes, do not need to be parsed before execution. They contain both network topology definition and test commands.
Network topology is defined in the beginning of PyRecipe, example:
import lnst
m1 = lnst.add_host() m2 = lnst.add_host()
m1_eth1 = m1.add_interface(label="tnet") m2_eth1 = m2.add_interface(label="tnet")
In the example, test will use 2 different hosts, each with 1 eth device.
To run match and network provision, use lnst.match() method in while loop, example:
while lnst.match(): m1_eth1.reset(ip="192.168.0.1/24") m2_eth1.reset(ip="192.168.0.2/24")
This piece of code will run the match, prepare the setup and sets IP addresses to both eth devices.
More info on how to use Pyrecipes will be in our documentation (github wiki).
After this patch, XML recipes will no longer work. It removes and modifies a lot of code in order to not break git bisect.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst-ctl | 13 +- lnst/Controller/NetTestController.py | 266 ++++++----------------------------- lnst/Controller/Task.py | 24 ++-- 3 files changed, 62 insertions(+), 241 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index b75b8a0..93b084b 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -178,9 +178,13 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, res = {} if matches == 1: try: - nettestctl.provision_machines() - nettestctl.print_match_description() + # init TaskAPI.Ctl + nettestctl.init_taskapi() res = exec_action(action, nettestctl) + except NoMatchError as err: + no_match = True + log_ctl.unset_recipe() + logging.warning("Match %d not possible." % matches) except Exception as err: no_match = True log_exc_traceback() @@ -190,12 +194,11 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, retval = RETVAL_ERR elif matches > 1: try: - nettestctl.provision_machines() + nettestctl.init_taskapi() log_ctl.set_recipe(file_path, prepend=True, expand="match_%d" % matches) log_dir = log_ctl.get_recipe_log_path() recipe_head_log_entry(file_path, log_dir, matches) res_serializer.add_recipe(file_path, matches) - nettestctl.print_match_description() res = exec_action(action, nettestctl) except NoMatchError as err: no_match = True @@ -400,7 +403,7 @@ def main(): all_files.sort() for f in all_files: recipe_file = os.path.join(recipe_path, f) - if re.match(r'^.*.xml$', recipe_file): + if re.match(r'^.*.py$', recipe_file): recipe_files.append(recipe_file) else: recipe_files.append(recipe_path) diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index 0115d2f..135bbf6 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -61,11 +61,10 @@ class NetTestController: self._res_serializer = res_serializer self._remote_capture_files = {} self._log_ctl = log_ctl - self._recipe_path = Path(None, recipe_path).abs_path() + self._recipe_path = Path(None, recipe_path) self._msg_dispatcher = MessageDispatcher(log_ctl) self._packet_capture = packet_capture self._reduce_sync = reduce_sync - self._parser = RecipeParser(recipe_path) self._defined_aliases = defined_aliases self._multi_match = multi_match
@@ -80,10 +79,6 @@ class NetTestController: mac_pool_range = lnst_config.get_option('environment', 'mac_pool_range') self._mac_pool = MacPool(mac_pool_range[0], mac_pool_range[1])
- self._parser.set_machines(self._machines) - self._parser.set_aliases(defined_aliases, overriden_aliases) - self._recipe = self._parser.parse() - conf_pools = lnst_config.get_pools() pools = {} if len(restrict_pools) > 0: @@ -100,9 +95,6 @@ class NetTestController: sp = SlavePool(pools, pool_checks) self._slave_pool = sp
- mreq = self._get_machine_requirements() - sp.set_machine_requirements(mreq) - modules_dirs = lnst_config.get_option('environment', 'module_dirs') tools_dirs = lnst_config.get_option('environment', 'tool_dirs')
@@ -196,21 +188,21 @@ class NetTestController: return mreq
def _prepare_network(self, resource_sync=True): - recipe = self._recipe + mreq = Task.get_mreq()
machines = self._machines for m_id in machines.keys(): self._prepare_machine(m_id, resource_sync)
- for machine_xml_data in recipe["machines"]: - m_id = machine_xml_data["id"] + for machine_id, machine_data in mreq.iteritems(): + m_id = machine_id m = machines[m_id] namespaces = set() - for iface_xml_data in machine_xml_data["interfaces"]: - self._prepare_interface(m_id, iface_xml_data) + for if_id, iface_data in machine_data["interfaces"].iteritems(): + self._prepare_interface(m_id, if_id, iface_data)
- if iface_xml_data["netns"] != None: - namespaces.add(iface_xml_data["netns"]) + if iface_data["netns"] != None: + namespaces.add(iface_data["netns"])
if len(namespaces) > 0: m.disable_nm() @@ -269,148 +261,16 @@ class NetTestController: machine.set_mac_pool(self._mac_pool) machine.set_network_bridges(self._network_bridges)
- recipe_name = os.path.basename(self._recipe_path) + recipe_name = os.path.basename(self._recipe_path.abs_path()) machine.init_connection(recipe_name)
- sync_table = {'module': {}, 'tools': {}} - if resource_sync: - res_table = copy.deepcopy(self._resource_table) - for task in self._recipe['tasks']: - if 'module_dir' in task: - modules = self._load_test_modules([task['module_dir']]) - res_table['module'].update(modules) - if 'tools_dir' in task: - tools = self._load_test_tools([task['tools_dir']]) - res_table['tools'].update(tools) - - if 'commands' not in task: - if not self._reduce_sync: - sync_table = res_table - break - else: - continue - for cmd in task['commands']: - if 'host' not in cmd or cmd['host'] != m_id: - continue - if cmd['type'] == 'test': - mod = cmd['module'] - if mod in res_table['module']: - sync_table['module'][mod] = res_table['module'][mod] - # check if test module uses some test tools - mod_path = res_table['module'][mod]["path"] - mod_tools = get_module_tools(mod_path) - for t in mod_tools: - if t in sync_table['tools']: - continue - logging.debug("Adding '%s' tool as "\ - "dependency of %s test module" % (t, mod)) - sync_table['tools'][t] = res_table['tools'][t] - else: - msg = "Module '%s' not found on the controller"\ - % mod - raise RecipeError(msg, cmd) - if cmd['type'] == 'exec' and 'from' in cmd: - tool = cmd['from'] - if tool in res_table['tools']: - sync_table['tools'][tool] = res_table['tools'][tool] - else: - msg = "Tool '%s' not found on the controller" % tool - raise RecipeError(msg, cmd) - machine.sync_resources(sync_table) - - def _prepare_interface(self, m_id, iface_xml_data): + def _prepare_interface(self, m_id, if_id, iface_data): machine = self._machines[m_id] - if_id = iface_xml_data["id"] - if_type = iface_xml_data["type"] - - try: - iface = machine.get_interface(if_id) - except MachineError: - if if_type == 'lo': - iface = machine.new_loopback_interface(if_id) - else: - iface = machine.new_soft_interface(if_id, if_type) - - if "slaves" in iface_xml_data: - for slave in iface_xml_data["slaves"]: - slave_id = slave["id"] - iface.add_slave(machine.get_interface(slave_id)) - - # Some soft devices (such as team) use per-slave options - if "options" in slave: - for opt in slave["options"]: - iface.set_slave_option(slave_id, opt["name"], - opt["value"]) - - if "addresses" in iface_xml_data: - for addr in iface_xml_data["addresses"]: - iface.add_address(addr) - - if "options" in iface_xml_data: - for opt in iface_xml_data["options"]: - iface.set_option(opt["name"], opt["value"]) - - if "netem" in iface_xml_data: - iface.set_netem(iface_xml_data["netem"].to_dict()) - - if "ovs_conf" in iface_xml_data: - iface.set_ovs_conf(iface_xml_data["ovs_conf"].to_dict()) - - if iface_xml_data["netns"] != None: - iface.set_netns(iface_xml_data["netns"]) - - if "peer" in iface_xml_data: - iface.set_peer(iface_xml_data["peer"]) - - def _prepare_tasks(self): - self._tasks = [] - for task_data in self._recipe["tasks"]: - task = {} - task["quit_on_fail"] = False - if "quit_on_fail" in task_data: - task["quit_on_fail"] = bool_it(task_data["quit_on_fail"]) - - if "module_dir" in task_data: - task["module_dir"] = task_data["module_dir"] - - if "tools_dir" in task_data: - task["tools_dir"] = task_data["tools_dir"] - - if "python" in task_data: - root = Path(None, self._recipe_path).get_root() - path = Path(root, task_data["python"]) - - task["python"] = path - if not path.exists(): - msg = "Task file '%s' not found." % path.to_str() - raise RecipeError(msg, task_data) - - self._tasks.append(task) - continue - - task["commands"] = task_data["commands"] - task["skeleton"] = [] - for cmd_data in task["commands"]: - cmd = {"type": cmd_data["type"]} - - 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"]: - if "bg_id" in cmd_data: - cmd["bg_id"] = cmd_data["bg_id"] - elif cmd["type"] in ["wait", "intr", "kill"]: - cmd["proc_id"] = cmd_data["bg_id"] - - task["skeleton"].append(cmd)
- if self._check_task(task): - raise RecipeError("Incorrect command sequence.", task_data) + iface = machine.get_interface(if_id)
- self._tasks.append(task) + if iface_data["netns"] != None: + iface.set_netns(iface_data["netns"])
def _prepare_command(self, cmd_data): cmd = {"type": cmd_data["type"]} @@ -635,36 +495,23 @@ class NetTestController: os.remove("/tmp/.lnst_machine_conf")
def match_setup(self): + self.run_mode = "match_setup" + res = self._run_python_task() return {"passed": True}
def config_only_recipe(self): + self.run_mode = "config_only" try: - self._prepare_network(resource_sync=False) - except (KeyboardInterrupt, Exception) as exc: - msg = "Exception raised during configuration." - logging.error(msg) - self._cleanup_slaves() + res = self._run_recipe() + except Exception as exc: + logging.error("Recipe execution terminated by unexpected exception") raise - - self._save_machine_config() - - self._cleanup_slaves(deconfigure=False) - return {"passed": True} - - def run_recipe(self): - try: - self._prepare_tasks() - self._prepare_network() - except (KeyboardInterrupt, Exception) as exc: - msg = "Exception raised during configuration." - logging.error(msg) + finally: self._cleanup_slaves() - raise
- if self._packet_capture: - self._start_packet_capture() + return res
- err = None + def run_recipe(self): try: res = self._run_recipe() except Exception as exc: @@ -699,70 +546,44 @@ class NetTestController: def _run_recipe(self): overall_res = {"passed": True}
- for task in self._tasks: + try: self._res_serializer.add_task() - try: - res = self._run_task(task) - except CommandException as exc: - logging.debug(exc) - overall_res["passed"] = False - overall_res["err_msg"] = "Command exception raised." - break - - for machine in self._machines.itervalues(): - machine.restore_system_config() - - # task failed, check if we should quit_on_fail - if not res: - overall_res["passed"] = False - overall_res["err_msg"] = "At least one command failed." - if task["quit_on_fail"]: - break + res = self._run_python_task() + except CommandException as exc: + logging.debug(exc) + overall_res["passed"] = False + overall_res["err_msg"] = "Command exception raised." + + for machine in self._machines.itervalues(): + machine.restore_system_config() + + # task failed + if not res: + overall_res["passed"] = False + overall_res["err_msg"] = "At least one command failed."
return overall_res
def init_taskapi(self): Task.ctl = Task.ControllerAPI(self)
- def _run_python_task(self, task): + def _run_python_task(self): #backup of resource table res_table_bkp = copy.deepcopy(self._resource_table) - if 'module_dir' in task: - modules = self._load_test_modules([task['module_dir']]) - self._resource_table['module'].update(modules) - if 'tools_dir' in task: - tools = self._load_test_tools([task['tools_dir']]) - self._resource_table['tools'].update(tools) - - # Initialize the API handle - Task.ctl = Task.ControllerAPI(self, self._machines)
cwd = os.getcwd() - task_path = task["python"] + task_path = self._recipe_path name = os.path.basename(task_path.abs_path()).split(".")[0] sys.path.append(os.path.dirname(task_path.resolve())) os.chdir(os.path.dirname(task_path.resolve())) - module = imp.load_source(name, task_path.resolve()) + imp.load_source(name, task_path.resolve()) os.chdir(cwd) sys.path.remove(os.path.dirname(task_path.resolve()))
#restore resource table self._resource_table = res_table_bkp
- return module.ctl._result - - def _run_task(self, task): - if "python" in task: - return self._run_python_task(task) - - seq_passed = True - for cmd_data in task["commands"]: - cmd = self._prepare_command(cmd_data) - cmd_res = self._run_command(cmd) - if not cmd_res["passed"]: - seq_passed = False - - return seq_passed + return Task.ctl._result
def _run_command(self, command): logging.info("Executing command: [%s]", str_command(command)) @@ -891,12 +712,11 @@ class NetTestController: return packages
def _get_alias(self, alias): - templates = self._parser._template_proc - return templates._find_definition(alias) + if alias in self._defined_aliases: + return self._defined_aliases[alias]
def _get_aliases(self): - templates = self._parser._template_proc - return templates._dump_definitions() + return self._defined_aliases
class MessageDispatcher(ConnectionHandler): def __init__(self, log_ctl): diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index e8a2538..a607765 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -88,7 +88,7 @@ class TaskError(Exception): pass class ControllerAPI(object): """ An API class representing the controller. """
- def __init__(self, ctl, hosts): + def __init__(self, ctl): self._ctl = ctl self.run_mode = ctl.run_mode self._result = True @@ -99,8 +99,6 @@ class ControllerAPI(object): self._perf_repo_api = PerfRepoAPI()
self._hosts = {} - for host_id, host in hosts.iteritems(): - self._hosts[host_id] = HostAPI(self, host_id, host)
def get_mreq(self): return self.mreq @@ -188,7 +186,7 @@ class ControllerAPI(object): cmd = {"type": "ctl_wait", "seconds": int(seconds)} return self._ctl._run_command(cmd)
- def get_alias(self, alias): + def get_alias(self, alias, default=None): """ Get the value of user defined alias.
@@ -199,9 +197,13 @@ class ControllerAPI(object): :rtype: string """ try: - return self._ctl._get_alias(alias) + val = self._ctl._get_alias(alias) + if val is None: + return default + else: + return val except XmlTemplateError: - return None + return default
def get_aliases(self): """ @@ -269,16 +271,12 @@ class ControllerAPI(object): class HostAPI(object): """ An API class representing a host machine. """
- def __init__(self, ctl, host_id, host): + def __init__(self, ctl, host_id): self._ctl = ctl self._id = host_id - self._m = host + self._m = None
self._ifaces = {} - for interface in self._m.get_interfaces(): - if interface.get_id() is None: - continue - self._ifaces[interface.get_id()] = InterfaceAPI(interface, self) self._if_id_seq = 0 self._bg_id_seq = 0
@@ -313,7 +311,7 @@ class HostAPI(object): return self._ifaces[if_id]
def get_id(self): - return self._m.get_id() + return self._id
def get_configuration(self): return self._m.get_configuration()
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- pyrecipes/3_vlans.py | 34 ++++++++++++++++++++++++++++++++++ pyrecipes/example.py | 33 +++++++++++++++++++++++++++++++++ pyrecipes/ping_flood.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 pyrecipes/3_vlans.py create mode 100644 pyrecipes/example.py create mode 100644 pyrecipes/ping_flood.py
diff --git a/pyrecipes/3_vlans.py b/pyrecipes/3_vlans.py new file mode 100644 index 0000000..067fd33 --- /dev/null +++ b/pyrecipes/3_vlans.py @@ -0,0 +1,34 @@ +import lnst + +def ping_mod_init(if1, if2): + ping_mod = lnst.get_module("IcmpPing", + options={ + "addr": if2.get_ip(0), + "count": 10, + "interval": 1, + "iface" : if1.get_devname()}) + return ping_mod + +m1 = lnst.add_host() +m2 = lnst.add_host() + +m1_eth1 = m1.add_interface(label="tnet") +m2_eth1 = m2.add_interface(label="tnet") + +while lnst.match(): + m1.sync_resources(modules=["IcmpPing"]) + m2.sync_resources(modules=["IcmpPing"]) + + m1_vlan10 = m1.create_vlan(realdev_iface=m1_eth1, vlan_tci="10", ip="192.168.10.1/24") + m1_vlan20 = m1.create_vlan(realdev_iface=m1_eth1, vlan_tci="20", ip="192.168.20.1/24") + m1_vlan30 = m1.create_vlan(realdev_iface=m1_eth1, vlan_tci="30", ip="192.168.30.1/24") + + m2_vlan10 = m2.create_vlan(realdev_iface=m2_eth1, vlan_tci="10", ip="192.168.10.2/24") + m2_vlan20 = m2.create_vlan(realdev_iface=m2_eth1, vlan_tci="20", ip="192.168.20.2/24") + m2_vlan30 = m2.create_vlan(realdev_iface=m2_eth1, vlan_tci="30", ip="192.168.30.2/24") + + ping_mod = ping_mod_init(m1_vlan10, m2_vlan10) + ping_mod_bad = ping_mod_init(m1_vlan10, m2_vlan20) + + m1.run(ping_mod) + #m1.run(ping_mod_bad) diff --git a/pyrecipes/example.py b/pyrecipes/example.py new file mode 100644 index 0000000..32c1b08 --- /dev/null +++ b/pyrecipes/example.py @@ -0,0 +1,33 @@ +import lnst + +# if1 ... src +# if2 ... dst +def ping_mod_init(if1, if2): + ping_mod = lnst.get_module("IcmpPing", + options={ + "addr": if2.get_ip(0), + "count": 10, + "interval": 1, + "iface" : if1.get_devname()}) + return ping_mod + +m1 = lnst.add_host() +m2 = lnst.add_host() + +m1_eth1 = m1.add_interface(label="tnet") +m1_eth2 = m1.add_interface(label="tnet") +m2_eth1 = m2.add_interface(label="tnet") + +while lnst.match(): + m1.sync_resources(modules=["IcmpPing"]) + m2.sync_resources(modules=["IcmpPing"]) + m2_eth1.reset(ip="192.168.0.2/24") + ping = ping_mod_init(m1_eth1, m2_eth1) + #lnst.breakpoint() + team_if = m1.create_team(slaves=[m1_eth1, m1_eth2]) + #lnst.breakpoint() + team_if.reset(ip="192.168.0.1/24") + #lnst.breakpoint() + ping = ping_mod_init(team_if, m2_eth1) + #lnst.breakpoint() + m1.run(ping) diff --git a/pyrecipes/ping_flood.py b/pyrecipes/ping_flood.py new file mode 100644 index 0000000..35d4741 --- /dev/null +++ b/pyrecipes/ping_flood.py @@ -0,0 +1,48 @@ +import lnst + +m1 = lnst.add_host() +m2 = lnst.add_host() + +m1_eth1 = m1.add_interface(label="tnet") +m2_eth1 = m2.add_interface(label="tnet") + +while lnst.match(): + m1.sync_resources(modules=["Icmp6Ping", "IcmpPing"]) + m2.sync_resources(modules=["Icmp6Ping", "IcmpPing"]) + + lnst.wait(15) + + ipv = lnst.get_alias("ipv", default="ipv4") + print "ipv" + print ipv + mtu = lnst.get_alias("mtu", default="1500") + print "mtu" + print mtu + + m1_eth1.reset(ip=["192.168.101.10/24", "fc00:0:0:0::1/64"]) + m2_eth1.reset(ip=["192.168.101.11/24", "fc00:0:0:0::2/64"]) + + ping_mod = lnst.get_module("IcmpPing", + options={ + "addr": m2_eth1.get_ip(0), + "count": 10, + "interval": 0.1, + "iface" : m1_eth1.get_devname(), + "limit_rate": 90}) + + ping_mod6 = lnst.get_module("Icmp6Ping", + options={ + "addr": m2_eth1.get_ip(1), + "count": 10, + "interval": 0.1, + "iface" : m1_eth1.get_devname(), + "limit_rate": 90}) + + m1_eth1.set_mtu(mtu) + m2_eth1.set_mtu(mtu) + + if ipv in [ 'ipv6', 'both' ]: + m1.run(ping_mod6) + + if ipv in ['ipv4', 'both' ]: + m1.run(ping_mod)
Since aliases can now only be specified via command line option, we can use only defined_aliases and thus remove overriden_aliases completely.
This patch removes overriden_aliases from lnst-ctl and NetTestController's __init__ function.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst-ctl | 5 ++--- lnst/Controller/NetTestController.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index 93b084b..c8a229f 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -142,7 +142,7 @@ def exec_action(action, nettestctl):
def get_recipe_result(action, file_path, log_ctl, res_serializer, pool_checks, packet_capture, - defined_aliases, overriden_aliases, + defined_aliases, reduce_sync, multi_match, pools): retval = RETVAL_PASS
@@ -161,7 +161,6 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, pool_checks=pool_checks, packet_capture=packet_capture, defined_aliases=defined_aliases, - overriden_aliases=overriden_aliases, reduce_sync=reduce_sync, restrict_pools=pools, multi_match=multi_match) @@ -413,7 +412,7 @@ def main(): for recipe_file in recipe_files: rv = get_recipe_result(action, recipe_file, log_ctl, res_serializer, pool_checks, packet_capture, - defined_aliases, overriden_aliases, + defined_aliases, reduce_sync, multi_match, pools) if rv > retval: retval = rv diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index 135bbf6..5de40ac 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -55,9 +55,8 @@ class NetTestController: def __init__(self, recipe_path, log_ctl, res_serializer=None, pool_checks=True, packet_capture=False, - defined_aliases=None, overriden_aliases=None, - reduce_sync=False, restrict_pools=[], - multi_match=False): + defined_aliases=None, reduce_sync=False, + restrict_pools=[], multi_match=False): self._res_serializer = res_serializer self._remote_capture_files = {} self._log_ctl = log_ctl
Import of RecipeParser and _get_machine_requirements() method can be deleted as they are no longer used.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/NetTestController.py | 72 +----------------------------------- 1 file changed, 1 insertion(+), 71 deletions(-)
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index 5de40ac..05368c8 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -24,7 +24,7 @@ from lnst.Common.NetUtils import MacPool from lnst.Common.Utils import md5sum, dir_md5sum from lnst.Common.Utils import check_process_running, bool_it, get_module_tools from lnst.Common.NetTestCommand import str_command, CommandException -from lnst.Controller.RecipeParser import RecipeParser, RecipeError +from lnst.Controller.RecipeParser import RecipeError from lnst.Controller.SlavePool import SlavePool from lnst.Controller.Machine import MachineError, VirtualInterface from lnst.Controller.Machine import StaticInterface @@ -116,76 +116,6 @@ class NetTestController: msg = "SSH session terminated with status %s" % status raise NetTestError(msg)
- def _get_machine_requirements(self): - recipe = self._recipe - - # 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 hosts specified in the recipe. At least two " \ - "hosts are required to perform a network test." - raise RecipeError(msg, recipe) - - # machine requirements - mreq = {} - for machine in recipe["machines"]: - m_id = machine["id"] - - if m_id in mreq: - msg = "Machine with id='%s' already exists." % m_id - raise RecipeError(msg, machine) - - params = {} - if "params" in machine: - for p in machine["params"]: - if p["name"] in params: - msg = "Parameter '%s' of host %s was specified " \ - "multiple times. Overriding the previous value." \ - % (p["name"], m_id) - logging.warn(RecipeError(msg, p)) - name = p["name"] - val = p["value"] - params[name] = val - - # Each machine must have at least one interface - if "interfaces" not in machine or \ - ("interfaces" in machine and len(machine["interfaces"]) == 0): - msg = "Host '%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 host " \ - "'%s'." % (if_id, m_id) - - iface_type = iface["type"] - if iface_type != "eth": - continue - - iface_params = {} - if "params" in iface: - for i in iface["params"]: - if i["name"] in iface_params: - msg = "Parameter '%s' of interface %s of " \ - "host %s was defined multiple times. " \ - "Overriding the previous value." \ - % (i["name"], if_id, m_id) - logging.warn(RecipeError(msg, p)) - name = i["name"] - val = i["value"] - iface_params[name] = val - - ifaces[if_id] = { - "network": iface["network"], - "params": iface_params - } - - mreq[m_id] = {"params": params, "interfaces": ifaces} - - return mreq - def _prepare_network(self, resource_sync=True): mreq = Task.get_mreq()
This patch removes methods that has already been marked as obsolete. It also removes a few methods that no longer make sense in current state of PyRecipes, such as get_host() or get_interface() because everything is created in PyRecipe itself.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst/Controller/Task.py | 109 ------------------------------------------------ 1 file changed, 109 deletions(-)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index a607765..08fe664 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -140,27 +140,6 @@ class ControllerAPI(object): self._result = self._result and res["passed"] return res
- def get_host(self, host_id): - """ - Get an API handle for the host from the recipe spec with - a specific id. - - :param host_id: id of the host as defined in the recipe - :type host_id: string - - :return: The host handle. - :rtype: HostAPI - - :raises TaskError: If there is no host with such id. - """ - if host_id not in self._hosts: - raise TaskError("Host '%s' not found." % host_id) - - return self._hosts[host_id] - - def get_hosts(self): - return self._hosts - def get_module(self, name, options={}): """ Initialize a module to be run on a host. @@ -412,82 +391,6 @@ class HostAPI(object): cmd_res = self._ctl._run_command(cmd) return ProcessAPI(self._ctl, self._id, cmd_res, bg_id, cmd["netns"])
- def get_interfaces(self): - return self._ifaces - - def get_interface(self, if_id): - return self._ifaces[if_id] - - def get_device(self, name): - dev = self._m.dev_db_get_name(name) - if dev: - return DeviceAPI(self._m.dev_db_get_name(name), self) - else: - raise TaskError("No device with name '%s' found." % str(name)) - - @deprecated - def get_devname(self, if_id): - """ - Returns devname of the interface. - - :param if_id: which interface - :type if_id: string - - :return: Device name (e.g., eth0). - :rtype: str - """ - iface = self._ifaces[if_id] - return iface.get_devname() - - @deprecated - def get_hwaddr(self, if_id): - """ - Returns hwaddr of the interface. - - :param if_id: which interface - :type if_id: string - - :return: HW address (e.g., 00:11:22:33:44:55:FF). - :rtype: str - """ - iface = self._ifaces[if_id] - return iface.get_hwaddr() - - @deprecated - def get_ip(self, if_id, addr_number=0): - """ - Returns an IP address of the interface. - - :param if_id: which interface - :type if_id: string - - :param addr_number: which address - :type addr_number: int - - :return: IP address (e.g., 192.168.1.10). - :rtype: str - """ - iface = self._ifaces[if_id] - return iface.get_ip_addr(addr_number) - - @deprecated - def get_prefix(self, if_id, addr_number=0): - """ - Returns an IP address prefix (netmask) - of the interface. - - :param if_id: which interface - :type if_id: string - - :param addr_number: which address - :type addr_number: int - - :return: netmask (e.g., 24). - :rtype: str - """ - iface = self._ifaces[if_id] - return iface.get_ip_prefix(addr_number) - def sync_resources(self, modules=[], tools=[]): res_table = self._ctl._ctl._resource_table sync_table = {'module': {}, 'tools': {}} @@ -676,21 +579,9 @@ class InterfaceAPI(object): def get_ips(self, selector={}): return VolatileValue(self._if.get_addresses)
- @deprecated - def get_ip_addr(self, ip_index=0): - return self.get_ip(ip_index) - - @deprecated - def get_ip_addrs(self): - return self.get_ips() - def get_prefix(self, ip_index=0): return VolatileValue(self._if.get_prefix, ip_index)
- @deprecated - def get_ip_prefix(self, ip_index=0): - return self.get_prefix(ip_index) - def get_mtu(self): return VolatileValue(self._if.get_mtu)
This patch adds new command line option for enabling breakpoints in the task. Short option is -b, long option --breakpoints.
Also the message informing about reaching the breakpoint has been moved to logging to level INFO.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst-ctl | 14 ++++++++++---- lnst/Controller/NetTestController.py | 4 +++- lnst/Controller/Task.py | 6 ++++-- 3 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index c8a229f..0a3a870 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -43,6 +43,7 @@ def usage(retval=0): print " -A, --override-alias name=value define top-level alias that " \ "will override any other definitions in the recipe" print " -a, --define-alias name=value define top-level alias" + print " -b, --breakpoints enable breakpoint feature" print " -c, --config=FILE load additional config file" print " -C, --config-override=FILE reset config defaults and load " \ "the following config file" @@ -142,7 +143,7 @@ def exec_action(action, nettestctl):
def get_recipe_result(action, file_path, log_ctl, res_serializer, pool_checks, packet_capture, - defined_aliases, + defined_aliases, breakpoints, reduce_sync, multi_match, pools): retval = RETVAL_PASS
@@ -163,7 +164,8 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, defined_aliases=defined_aliases, reduce_sync=reduce_sync, restrict_pools=pools, - multi_match=multi_match) + multi_match=multi_match, + breakpoints=breakpoints) except XmlProcessingError as err: log_exc_traceback() logging.error(err) @@ -244,8 +246,9 @@ def main(): try: opts, args = getopt.getopt( sys.argv[1:], - "A:a:c:C:dhmoprs:t:ux:v", + "A:a:bc:C:dhmoprs:t:ux:v", [ + "breakpoints", "override_alias=", "define_alias=", "config=", @@ -290,6 +293,7 @@ def main(): with open(usr_cfg, 'w') as f: f.write(lnst_config.dump_config())
+ breakpoints = False debug = 0 result_path = None html_result_path = None @@ -309,6 +313,8 @@ def main(): debug += 1 elif opt in ("-h", "--help"): usage(RETVAL_PASS) + elif opt in ("-b", "--breakpoints"): + breakpoints = True elif opt in ("-c", "--config"): if not os.path.isfile(arg): print "File '%s' doesn't exist!" % arg @@ -412,7 +418,7 @@ def main(): for recipe_file in recipe_files: rv = get_recipe_result(action, recipe_file, log_ctl, res_serializer, pool_checks, packet_capture, - defined_aliases, + defined_aliases, breakpoints, reduce_sync, multi_match, pools) if rv > retval: retval = rv diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index 05368c8..4b47d1f 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -56,7 +56,8 @@ class NetTestController: res_serializer=None, pool_checks=True, packet_capture=False, defined_aliases=None, reduce_sync=False, - restrict_pools=[], multi_match=False): + restrict_pools=[], multi_match=False, + breakpoints=False): self._res_serializer = res_serializer self._remote_capture_files = {} self._log_ctl = log_ctl @@ -70,6 +71,7 @@ class NetTestController: self.remove_saved_machine_config()
self.run_mode = "run" + self.breakpoints = breakpoints
self._machines = {} self._network_bridges = {} diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 08fe664..56c8379 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -45,9 +45,10 @@ def get_module(name, options={}): return ctl.get_module(name, options)
def breakpoint(): - if ctl.run_mode != "config_only": + if not ctl.breakpoints: return - raw_input("Breakpoint reached. Press enter to continue.") + logging.info("Breakpoint reached. Press enter to continue.") + raw_input("")
def add_host(params=None): m_id = ctl.gen_m_id() @@ -91,6 +92,7 @@ class ControllerAPI(object): def __init__(self, ctl): self._ctl = ctl self.run_mode = ctl.run_mode + self.breakpoints = ctl.breakpoints self._result = True self.first_run = True self._m_id_seq = 0
With introduction of PyRecipes and breakpoints, the config_only option is no longer needed so we can remove it.
This patch removes also methods that were used by config_only mode for storing and deleting of the configuration on the machine.
Signed-off-by: Jiri Prochazka jprochaz@redhat.com --- lnst-ctl | 8 +-- lnst/Controller/Machine.py | 15 +++-- lnst/Controller/NetTestController.py | 108 ++--------------------------------- 3 files changed, 12 insertions(+), 119 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index 0a3a870..29d133c 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -136,8 +136,6 @@ def store_alias(alias_def, aliases_dict): def exec_action(action, nettestctl): if action == "run": return nettestctl.run_recipe() - elif action == "config_only": - return nettestctl.config_only_recipe() elif action == "match_setup": return nettestctl.match_setup()
@@ -380,13 +378,9 @@ def main():
action = args[0] recipes = args[1:] - if not action in ['run', 'config_only', 'deconfigure', 'match_setup', - 'list_pools']: + if not action in ['run', 'match_setup', 'list_pools']: logging.error("Action '%s' not recognised" % action) usage(RETVAL_ERR) - if action == 'deconfigure': - NetTestController.remove_saved_machine_config() - return 0 elif action == 'list_pools': log_ctl.unset_formatter() logging.disable(logging.INFO) diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 3e730bd..e17e6b2 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -286,7 +286,7 @@ class Machine(object):
return self._configured
- def cleanup(self, deconfigure=True): + def cleanup(self): """ Clean the machine up
This is the counterpart of the configure() method. It will @@ -322,14 +322,13 @@ class Machine(object):
self.restore_system_config()
- if deconfigure: - ordered_ifaces.reverse() - for iface in ordered_ifaces: - iface.deconfigure() - for iface in ordered_ifaces: - iface.cleanup() + ordered_ifaces.reverse() + for iface in ordered_ifaces: + iface.deconfigure() + for iface in ordered_ifaces: + iface.cleanup()
- self.del_namespaces() + self.del_namespaces()
self.restore_nm_option() self._rpc_call("bye") diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py index 4b47d1f..64e3e4a 100644 --- a/lnst/Controller/NetTestController.py +++ b/lnst/Controller/NetTestController.py @@ -68,8 +68,6 @@ class NetTestController: self._defined_aliases = defined_aliases self._multi_match = multi_match
- self.remove_saved_machine_config() - self.run_mode = "run" self.breakpoints = breakpoints
@@ -317,14 +315,14 @@ class NetTestController:
return err
- def _cleanup_slaves(self, deconfigure=True): + def _cleanup_slaves(self): if self._machines == None: return
for machine_id, machine in self._machines.iteritems(): if machine.is_configured(): try: - machine.cleanup(deconfigure) + machine.cleanup() except: pass
@@ -335,113 +333,15 @@ class NetTestController: del self._machines[m_id]
# remove dynamically created bridges - if deconfigure: - for bridge in self._network_bridges.itervalues(): - bridge.cleanup() - self._network_bridges = {} - - def _save_machine_config(self): - #saves current virtual configuration to a file, after pickling it - config_data = dict() - machines = config_data["machines"] = {} - for m in self._machines.itervalues(): - machine = machines[m.get_hostname()] = dict() - - if m.get_libvirt_domain() != None: - machine["libvirt_dom"] = m.get_libvirt_domain() - machine["interfaces"] = [] - - machine["security"] = m.get_security() - - for i in m._interfaces: - if isinstance(i, VirtualInterface): - hwaddr = i.get_orig_hwaddr() - - machine["interfaces"].append(hwaddr) - - config_data["bridges"] = bridges = [] for bridge in self._network_bridges.itervalues(): - bridges.append(bridge.get_name()) - - with open("/tmp/.lnst_machine_conf", "wb") as f: - os.fchmod(f.fileno(), 0o600) - pickled_data = cPickle.dump(config_data, f) - - @classmethod - def remove_saved_machine_config(cls): - #removes previously saved configuration - cfg = None - try: - with open("/tmp/.lnst_machine_conf", "rb") as f: - cfg = cPickle.load(f) - except: - logging.info("No previous configuration found.") - return - - if cfg: - logging.info("Cleaning up leftover configuration from previous "\ - "config_only run.") - for hostname, machine in cfg["machines"].iteritems(): - port = lnst_config.get_option("environment", "rpcport") - if test_tcp_connection(hostname, port): - s = socket.create_connection((hostname, port)) - rpc_con = CtlSecSocket(s) - try: - rpc_con.handshake(machine["security"]) - except SecSocketException: - logging.error("Failed authentication for machine %s" %\ - hostname) - continue - - rpc_msg= {"type": "command", - "method_name": "machine_cleanup", - "args": []} - - logging.debug("Calling cleanup on slave '%s'" % hostname) - rpc_con.send_msg(rpc_msg) - while True: - msg = rpc_con.recv_msg() - if msg['type'] == 'result': - break - rpc_con.close() - - if "libvirt_dom" in machine: - libvirt_dom = machine["libvirt_dom"] - domain_ctl = VirtDomainCtl(libvirt_dom) - logging.info("Detaching dynamically created interfaces.") - for i in machine["interfaces"]: - try: - domain_ctl.detach_interface(i) - except: - pass - - logging.info("Removing dynamically created bridges.") - for br in cfg["bridges"]: - try: - net_ctl = VirtNetCtl(br) - net_ctl.cleanup() - except: - pass - - os.remove("/tmp/.lnst_machine_conf") + bridge.cleanup() + self._network_bridges = {}
def match_setup(self): self.run_mode = "match_setup" res = self._run_python_task() return {"passed": True}
- def config_only_recipe(self): - self.run_mode = "config_only" - try: - res = self._run_recipe() - except Exception as exc: - logging.error("Recipe execution terminated by unexpected exception") - raise - finally: - self._cleanup_slaves() - - return res - def run_recipe(self): try: res = self._run_recipe()
Thu, Jul 21, 2016 at 05:55:36PM CEST, jprochaz@redhat.com wrote:
Info about multi_match is now passed to NetTestController's init function where it's stored as attribute of the class.
This will be used in PyRecipes implementation as flag indicating whether multi_match is toggled on or off.
Could you please provide a short changelog from the version 2? That would help the review process a lot. Please add it next time by default.
On Thu, Jul 21, 2016 at 05:55:36PM +0200, Jiri Prochazka wrote:
Info about multi_match is now passed to NetTestController's init function where it's stored as attribute of the class.
This will be used in PyRecipes implementation as flag indicating whether multi_match is toggled on or off.
Finished going through the v3 recipes, except for the one issue I found they look ok to me.
I'll wait for the summary email for "diff v2 v3" that jpirko requested and check again based on that...
-Ondrej
lnst-developers@lists.fedorahosted.org