From: Radek Pazdera rpazdera@redhat.com
This patchset integrates LNST with libvirt and add support for dynamically adding arbitrary netdevices to the test machines in case they are libvirt virtual domains.
To take advantage of this, you need to have libvirt (including virsh) installed on the controller machine.
To indicate that your machine is virtualized on controller, specify 'libvirt_domain=<your_domain_name_here>' attribute to <info> tag in netmachine config.
After that you can mark your dynamic devices by enclosing them in <create> tag.
Some other changes were added along with this patchset. Netdevices are now groupped in <netdevices> tag (it's necessary to fix this in your older recipes to use them from now on).
Each netdevice now has a required attribute called 'network'. This can be later used to determine the topology within lnst. You can chose any network name you want as long as its unique within your recipe. The semantics of this attribute is to indicate that all the devices tagged with the same network name are available on a single link segment.
Hope this patchset doesn't break anything else :). In case it does, please let me know.
Radek
Radek Pazdera (12): NetTest: Adding 'network' attribute to netdevice Common: New module NetUtils Utils: Moved get_corespond_local_ip() to NetUtils NetConfigDevNames: Separating scan_netdevs method NetTestController: Adding new exception NetUtils: Moving scan_netdevs from NetConfig NetTestSlave: Adding RPC method for dev querying NetTestParse: Adding support for libvirt-integration NetConfig: Adding device rescan to add methods NetUtils: Adding AddressPool class Common: Adding VirtUtils module NetTestController: Implementing libvirt-integration
Common/NetUtils.py | 108 ++++++++++++++++++ Common/Utils.py | 20 +--- Common/VirtUtils.py | 242 ++++++++++++++++++++++++++++++++++++++++ NetConfig/NetConfig.py | 4 + NetConfig/NetConfigDevNames.py | 27 +---- NetTest/NetTestController.py | 102 +++++++++++++++++- NetTest/NetTestParse.py | 38 ++++++- NetTest/NetTestSlave.py | 11 ++ 8 files changed, 504 insertions(+), 48 deletions(-) create mode 100644 Common/NetUtils.py create mode 100644 Common/VirtUtils.py
From: Radek Pazdera rpazdera@redhat.com
This commit introduces networks into LNST netdevices. Up to this point we were unaware of how the devices are connected to each other. The network attribute adds this information directly into the recipe.
So if you later receive a recipe, you can configure your underlying physical network accordingly. This will be also useful with the upcomming virtual networks and interfaces, because LNST will be able to set up the virtual network automatically.
See the following example:
Machine 1: <netdevice hwaddr="52:54:00:11:D5:A8" network="channel-1" phys_id="1" type="eth"/> <netdevice hwaddr="52:54:00:2E:CD:A8" network="channel-2" phys_id="1" type="eth"/>
Machine 2: <netdevice hwaddr="52:54:00:2E:D5:A8" network="channel-1" phys_id="1" type="eth"/> <netdevice hwaddr="52:54:00:2E:D5:A8" network="channel-2" phys_id="1" type="eth"/>
Machine 1 is connected to machine 2 with two networks, channel-1 and channel-2.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestController.py | 16 +++++++++++++++- NetTest/NetTestParse.py | 1 + 2 files changed, 16 insertions(+), 1 deletions(-)
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py index 4e34c90..8e23152 100644 --- a/NetTest/NetTestController.py +++ b/NetTest/NetTestController.py @@ -39,11 +39,14 @@ class NetTestController: self._recipe = {} definitions = {"recipe": self._recipe}
+ self._recipe["networks"] = {} + ntparse = NetTestParse(recipe_path) ntparse.set_recipe(self._recipe) ntparse.set_definitions(definitions)
- ntparse.register_event_handler("netdevice_ready", ignore_event) + ntparse.register_event_handler("netdevice_ready", + self._prepare_device) ntparse.register_event_handler("machine_info_ready", self._prepare_slave) ntparse.register_event_handler("interface_config_ready", @@ -75,6 +78,17 @@ class NetTestController: session.command, status) raise Exception("Session Die.")
+ def _prepare_device(self, machine_id, dev_id): + info = self._get_machineinfo(machine_id) + dev = self._recipe["machines"][machine_id]["netdevices"][dev_id] + + dev_net = dev["network"] + networks = self._recipe["networks"] + if not dev_net in networks: + networks[dev_net] = {"members": []} + + networks[dev_net]["members"].append((machine_id, dev_id)) + def _prepare_interface(self, machine_id, netdev_config_id): rpc = self._get_machinerpc(machine_id) info = self._get_machineinfo(machine_id) diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index e52f0f3..32d75e1 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -145,6 +145,7 @@ class NetMachineConfigParse(RecipeParser):
dev = machine["netdevices"][phys_id] = {} dev["type"] = self._get_attribute(node, "type") + dev["network"] = self._get_attribute(node, "network") dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
if self._has_attribute(node, "name"):
From: Radek Pazdera rpazdera@redhat.com
Created a new module for networking related code and utilities in Common package.
The normalize_hwaddr() function was moved there.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- Common/NetUtils.py | 16 ++++++++++++++++ NetConfig/NetConfigDevNames.py | 4 +--- NetTest/NetTestParse.py | 3 +-- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 Common/NetUtils.py
diff --git a/Common/NetUtils.py b/Common/NetUtils.py new file mode 100644 index 0000000..059ce6e --- /dev/null +++ b/Common/NetUtils.py @@ -0,0 +1,16 @@ +""" +Networking related utilities and common code + +Copyright 2012 Red Hat, Inc. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +rpazdera@redhat.com (Radek Pazdera) +""" + +import logging + +def normalize_hwaddr(hwaddr): + return hwaddr.upper().rstrip("\n") diff --git a/NetConfig/NetConfigDevNames.py b/NetConfig/NetConfigDevNames.py index 1220480..7c7f862 100644 --- a/NetConfig/NetConfigDevNames.py +++ b/NetConfig/NetConfigDevNames.py @@ -13,9 +13,7 @@ jpirko@redhat.com (Jiri Pirko) import logging import os from NetConfigCommon import get_option - -def normalize_hwaddr(hwaddr): - return hwaddr.upper().rstrip("\n") +from Common.NetUtils import normalize_hwaddr
class NetConfigDevNames: def __init__(self): diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 32d75e1..746342c 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -16,8 +16,7 @@ import re from Common.XmlProcessing import RecipeParser from Common.XmlProcessing import XmlDomTreeInit from Common.XmlProcessing import XmlProcessingError -from NetConfig.NetConfigDevNames import normalize_hwaddr - +from Common.NetUtils import normalize_hwaddr
class NetTestParse(RecipeParser): def __init__(self, recipe_filepath):
From: Radek Pazdera rpazdera@redhat.com
This commit moves get_corespond_local_ip function from the general utility module to the new network specific one.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- Common/NetUtils.py | 20 ++++++++++++++++++++ Common/Utils.py | 20 ++------------------ NetTest/NetTestController.py | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/Common/NetUtils.py b/Common/NetUtils.py index 059ce6e..7e642b6 100644 --- a/Common/NetUtils.py +++ b/Common/NetUtils.py @@ -11,6 +11,26 @@ rpazdera@redhat.com (Radek Pazdera) """
import logging +import re +import socket +import subprocess
def normalize_hwaddr(hwaddr): return hwaddr.upper().rstrip("\n") + +def get_corespond_local_ip(query_ip): + """ + Get ip address in local system which can communicate with query_ip. + + @param query_ip: IP of client which want communicate with autotest machine. + @return: IP address which can communicate with query_ip + """ + query_ip = socket.gethostbyname(query_ip) + ip = subprocess.Popen("ip route get %s" % (query_ip), + shell=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + ip = ip.communicate()[0] + ip = re.search(r"src ([0-9.]*)",ip) + if ip is None: + return ip + return ip.group(1) diff --git a/Common/Utils.py b/Common/Utils.py index 8f39729..f6ba201 100644 --- a/Common/Utils.py +++ b/Common/Utils.py @@ -9,7 +9,8 @@ published by the Free Software Foundation; see COPYING for details. __autor__ = """ jzupka@redhat.com (Jiri Zupka) """ -import logging, time, subprocess, re, socket +import logging +import time
def die_when_parent_die(): try: @@ -51,20 +52,3 @@ def wait_for(func, timeout, first=0.0, step=1.0, text=None):
logging.debug("Timeout elapsed") return None - - -def get_corespond_local_ip(query_ip): - """ - Get ip address in local system which can communicate with quert_ip. - - @param query_ip: IP of client which want communicate with autotest machine. - @return: IP address which can communicate with query_ip - """ - query_ip = socket.gethostbyname(query_ip) - ip = subprocess.Popen("ip route get %s" % (query_ip), - shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - ip = ip.communicate()[0] - ip = re.search(r"src ([0-9.]*)",ip) - if ip is None: - return ip - return ip.group(1) diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py index 8e23152..a997e37 100644 --- a/NetTest/NetTestController.py +++ b/NetTest/NetTestController.py @@ -20,7 +20,7 @@ from pprint import pprint, pformat from Common.XmlRpc import ServerProxy from NetTest.NetTestParse import NetTestParse from Common.SlaveUtils import prepare_client_session -from Common.Utils import get_corespond_local_ip +from Common.NetUtils import get_corespond_local_ip from NetTest.NetTestSlave import DefaultRPCPort from NetTest.NetTestCommand import NetTestCommand, str_command from Common.LoggingServer import LoggingServer
From: Radek Pazdera rpazdera@redhat.com
I separated scan_netdevs method from the class and changed it to regular function, because it will be useful elsewhere in the code, but not in the context of the class (so using the class directly would not be pretty).
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetConfig/NetConfigDevNames.py | 40 ++++++++++++++++++++-------------------- 1 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/NetConfig/NetConfigDevNames.py b/NetConfig/NetConfigDevNames.py index 7c7f862..3f3e0fd 100644 --- a/NetConfig/NetConfigDevNames.py +++ b/NetConfig/NetConfigDevNames.py @@ -15,30 +15,30 @@ import os from NetConfigCommon import get_option from Common.NetUtils import normalize_hwaddr
+def scan_netdevs(): + sys_dir = "/sys/class/net" + scan = [] + for root, dirs, files in os.walk(sys_dir): + if "lo" in dirs: + dirs.remove("lo") + for d in dirs: + dev_path = os.path.join(sys_dir, d) + addr_path = os.path.join(dev_path, "address") + if not os.path.isfile(addr_path): + continue + handle = open(addr_path, "rb") + addr = handle.read() + handle.close() + addr = normalize_hwaddr(addr) + scan.append({"name": d, "hwaddr": addr}) + return scan + class NetConfigDevNames: def __init__(self): - self._scan = self._scan_netdevs() - - def _scan_netdevs(self): - sys_dir = "/sys/class/net" - scan = [] - for root, dirs, files in os.walk(sys_dir): - if "lo" in dirs: - dirs.remove("lo") - for d in dirs: - dev_path = os.path.join(sys_dir, d) - addr_path = os.path.join(dev_path, "address") - if not os.path.isfile(addr_path): - continue - handle = open(addr_path, "rb") - addr = handle.read() - handle.close() - addr = normalize_hwaddr(addr) - scan.append({"name": d, "hwaddr": addr}) - return scan + self._scan = scan_netdevs()
def rescan_netdevs(self): - self._scan = self._scan_netdevs() + self._scan = scan_netdevs()
def assign_name_by_scan(self, dev_id, netdev): if (not "hwaddr" in netdev or
From: Radek Pazdera rpazdera@redhat.com
This commit adds new exception to NetTestController that will be used to indicate errors in recipe execution.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestController.py | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py index a997e37..1dcfc69 100644 --- a/NetTest/NetTestController.py +++ b/NetTest/NetTestController.py @@ -25,6 +25,9 @@ from NetTest.NetTestSlave import DefaultRPCPort from NetTest.NetTestCommand import NetTestCommand, str_command from Common.LoggingServer import LoggingServer
+class NetTestError(Exception): + pass + def ignore_event(**kwarg): pass
From: Radek Pazdera rpazdera@redhat.com
scan_netdevs function was moved from NetConfigDevNames to NetUtils, because it will be needed by code outside of the NetConfig module as well to detect devices.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- Common/NetUtils.py | 19 +++++++++++++++++++ NetConfig/NetConfigDevNames.py | 19 +------------------ 2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/Common/NetUtils.py b/Common/NetUtils.py index 7e642b6..42ee6f0 100644 --- a/Common/NetUtils.py +++ b/Common/NetUtils.py @@ -11,6 +11,7 @@ rpazdera@redhat.com (Radek Pazdera) """
import logging +import os import re import socket import subprocess @@ -18,6 +19,24 @@ import subprocess def normalize_hwaddr(hwaddr): return hwaddr.upper().rstrip("\n")
+def scan_netdevs(): + sys_dir = "/sys/class/net" + scan = [] + for root, dirs, files in os.walk(sys_dir): + if "lo" in dirs: + dirs.remove("lo") + for d in dirs: + dev_path = os.path.join(sys_dir, d) + addr_path = os.path.join(dev_path, "address") + if not os.path.isfile(addr_path): + continue + handle = open(addr_path, "rb") + addr = handle.read() + handle.close() + addr = normalize_hwaddr(addr) + scan.append({"name": d, "hwaddr": addr}) + return scan + def get_corespond_local_ip(query_ip): """ Get ip address in local system which can communicate with query_ip. diff --git a/NetConfig/NetConfigDevNames.py b/NetConfig/NetConfigDevNames.py index 3f3e0fd..0f246d9 100644 --- a/NetConfig/NetConfigDevNames.py +++ b/NetConfig/NetConfigDevNames.py @@ -14,24 +14,7 @@ import logging import os from NetConfigCommon import get_option from Common.NetUtils import normalize_hwaddr - -def scan_netdevs(): - sys_dir = "/sys/class/net" - scan = [] - for root, dirs, files in os.walk(sys_dir): - if "lo" in dirs: - dirs.remove("lo") - for d in dirs: - dev_path = os.path.join(sys_dir, d) - addr_path = os.path.join(dev_path, "address") - if not os.path.isfile(addr_path): - continue - handle = open(addr_path, "rb") - addr = handle.read() - handle.close() - addr = normalize_hwaddr(addr) - scan.append({"name": d, "hwaddr": addr}) - return scan +from Common.NetUtils import scan_netdevs
class NetConfigDevNames: def __init__(self):
Fri, Aug 10, 2012 at 12:49:09PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
scan_netdevs function was moved from NetConfigDevNames to NetUtils, because it will be needed by code outside of the NetConfig module as well to detect devices.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
Common/NetUtils.py | 19 +++++++++++++++++++ NetConfig/NetConfigDevNames.py | 19 +------------------ 2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/Common/NetUtils.py b/Common/NetUtils.py index 7e642b6..42ee6f0 100644 --- a/Common/NetUtils.py +++ b/Common/NetUtils.py @@ -11,6 +11,7 @@ rpazdera@redhat.com (Radek Pazdera) """
import logging +import os import re import socket import subprocess @@ -18,6 +19,24 @@ import subprocess def normalize_hwaddr(hwaddr): return hwaddr.upper().rstrip("\n")
+def scan_netdevs():
- sys_dir = "/sys/class/net"
- scan = []
- for root, dirs, files in os.walk(sys_dir):
if "lo" in dirs:
dirs.remove("lo")
for d in dirs:
dev_path = os.path.join(sys_dir, d)
addr_path = os.path.join(dev_path, "address")
if not os.path.isfile(addr_path):
continue
handle = open(addr_path, "rb")
addr = handle.read()
handle.close()
addr = normalize_hwaddr(addr)
scan.append({"name": d, "hwaddr": addr})
- return scan
def get_corespond_local_ip(query_ip): """ Get ip address in local system which can communicate with query_ip. diff --git a/NetConfig/NetConfigDevNames.py b/NetConfig/NetConfigDevNames.py index 3f3e0fd..0f246d9 100644 --- a/NetConfig/NetConfigDevNames.py +++ b/NetConfig/NetConfigDevNames.py @@ -14,24 +14,7 @@ import logging import os from NetConfigCommon import get_option from Common.NetUtils import normalize_hwaddr
-def scan_netdevs():
- sys_dir = "/sys/class/net"
- scan = []
- for root, dirs, files in os.walk(sys_dir):
if "lo" in dirs:
dirs.remove("lo")
for d in dirs:
dev_path = os.path.join(sys_dir, d)
addr_path = os.path.join(dev_path, "address")
if not os.path.isfile(addr_path):
continue
handle = open(addr_path, "rb")
addr = handle.read()
handle.close()
addr = normalize_hwaddr(addr)
scan.append({"name": d, "hwaddr": addr})
- return scan
+from Common.NetUtils import scan_netdevs
class NetConfigDevNames: def __init__(self): -- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
You could probably do this in: [PATCH 04/12] NetConfigDevNames: Separating scan_netdevs method
...
Other than this and my naming comment, the set looks good to me.
Jiri
On 08/10/2012 01:14 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 12:49:09PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
scan_netdevs function was moved from NetConfigDevNames to NetUtils, because it will be needed by code outside of the NetConfig module as well to detect devices.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
Common/NetUtils.py | 19 +++++++++++++++++++ NetConfig/NetConfigDevNames.py | 19 +------------------ 2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/Common/NetUtils.py b/Common/NetUtils.py index 7e642b6..42ee6f0 100644 --- a/Common/NetUtils.py +++ b/Common/NetUtils.py @@ -11,6 +11,7 @@ rpazdera@redhat.com (Radek Pazdera) """
import logging +import os import re import socket import subprocess @@ -18,6 +19,24 @@ import subprocess def normalize_hwaddr(hwaddr): return hwaddr.upper().rstrip("\n")
+def scan_netdevs():
- sys_dir = "/sys/class/net"
- scan = []
- for root, dirs, files in os.walk(sys_dir):
if "lo" in dirs:
dirs.remove("lo")
for d in dirs:
dev_path = os.path.join(sys_dir, d)
addr_path = os.path.join(dev_path, "address")
if not os.path.isfile(addr_path):
continue
handle = open(addr_path, "rb")
addr = handle.read()
handle.close()
addr = normalize_hwaddr(addr)
scan.append({"name": d, "hwaddr": addr})
- return scan
def get_corespond_local_ip(query_ip): """ Get ip address in local system which can communicate with query_ip. diff --git a/NetConfig/NetConfigDevNames.py b/NetConfig/NetConfigDevNames.py index 3f3e0fd..0f246d9 100644 --- a/NetConfig/NetConfigDevNames.py +++ b/NetConfig/NetConfigDevNames.py @@ -14,24 +14,7 @@ import logging import os from NetConfigCommon import get_option from Common.NetUtils import normalize_hwaddr
-def scan_netdevs():
- sys_dir = "/sys/class/net"
- scan = []
- for root, dirs, files in os.walk(sys_dir):
if "lo" in dirs:
dirs.remove("lo")
for d in dirs:
dev_path = os.path.join(sys_dir, d)
addr_path = os.path.join(dev_path, "address")
if not os.path.isfile(addr_path):
continue
handle = open(addr_path, "rb")
addr = handle.read()
handle.close()
addr = normalize_hwaddr(addr)
scan.append({"name": d, "hwaddr": addr})
- return scan
+from Common.NetUtils import scan_netdevs
class NetConfigDevNames: def __init__(self): -- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
You could probably do this in: [PATCH 04/12] NetConfigDevNames: Separating scan_netdevs method
Ok, I will merge them.
...
Other than this and my naming comment, the set looks good to me.
Jiri
From: Radek Pazdera rpazdera@redhat.com
This commit adds simple RPC method to the slave that allows the controler to issue a device queries and obtain information about available devices by hwaddr.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestSlave.py | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/NetTest/NetTestSlave.py b/NetTest/NetTestSlave.py index 12440f7..a7722e4 100644 --- a/NetTest/NetTestSlave.py +++ b/NetTest/NetTestSlave.py @@ -22,6 +22,7 @@ from NetConfig.NetConfig import NetConfig from NetConfig.NetConfigDevice import NetConfigDeviceAllCleanup from NetTest.NetTestCommand import NetTestCommand, CommandException from Common.Utils import die_when_parent_die +from Common.NetUtils import scan_netdevs
DefaultRPCPort = 9999
@@ -46,6 +47,16 @@ class NetTestSlaveXMLRPC: Logs.append_network_hadler(logger_address, port) return True
+ def get_devices_by_hwaddr(self, hwaddr): + name_scan = scan_netdevs() + netdevs = [] + + for entry in name_scan: + if entry["hwaddr"] == hwaddr: + netdevs.append(entry) + + return netdevs + def get_interface_info(self, if_id): if_config = self._netconfig.get_interface_config(if_id) info = {}
From: Radek Pazdera rpazdera@redhat.com
This commit adds necessary changes to the parser in LNST in order to support the libvirt device creation.
Netdevice tags are now grouped under a <netdevices> parent tag within <netmachineconfig>.
On top of that a special mode for defining netdevices (called create) was added. Devices defined in this mode are not present in the machine and will be created on test execution.
This mode is available only on virtual test machines created using libvirt. Here's an exmaple config of such a machine:
<machine id="1"> <netmachineconfig> <info hostname="192.168.122.11" libvirt_domain="RHEL6" rootpass="redhat"/> <netdevices> <create> <netdevice network="tnet" phys_id="1" type="eth"/> <netdevice network="tnet" phys_id="2" type="eth"/> </create> </netdevices> </netmachineconfig> <netconfig> <interface id="1" phys_id="1" type="eth"> <addresses> <address value="192.168.100.226/24"/> </addresses> </interface> <interface id="2" phys_id="2" type="eth"> <addresses> <address value="192.168.100.240/24"/> </addresses> </interface> </netconfig> </machine>
The dynamic devices are enclosed within the <create> tag. Their hwaddr doesn't need to be specified (it will be generated if missing).
Additionally, 'target_bridge' attribute can be added to these devices to override the default LNST internal bridge, that these devices are connected to.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestParse.py | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 746342c..d1edf6b 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -118,7 +118,7 @@ class NetMachineConfigParse(RecipeParser):
def parse(self, node): scheme = {"info": self._info, - "netdevice": self._netdevice} + "netdevices": self._netdevices} self._process_child_nodes(node, scheme)
def _info(self, node, params): @@ -127,6 +127,10 @@ class NetMachineConfigParse(RecipeParser):
info["hostname"] = self._get_attribute(node, "hostname")
+ if self._has_attribute(node, "libvirt_domain"): + info["libvirt_domain"] = self._get_attribute(node, + "libvirt_domain") + if self._has_attribute(node, "rootpass"): info["rootpass"] = self._get_attribute(node, "rootpass")
@@ -138,18 +142,42 @@ class NetMachineConfigParse(RecipeParser): self._trigger_event("machine_info_ready", {"machine_id": self._machine_id})
+ def _netdevices(self, node, params): + scheme = {"netdevice": self._netdevice, + "create": self._create} + + new_params = {"dynamic": False} + self._process_child_nodes(node, scheme, new_params) + + def _create(self, node, params): + scheme = {"netdevice": self._netdevice} + + new_params = {"dynamic": True} + self._process_child_nodes(node, scheme, new_params) + def _netdevice(self, node, params): machine = self._machine phys_id = self._get_attribute(node, "phys_id", int)
dev = machine["netdevices"][phys_id] = {} + dev["dynamic"] = params["dynamic"] dev["type"] = self._get_attribute(node, "type") dev["network"] = self._get_attribute(node, "network") - dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
- if self._has_attribute(node, "name"): + # hwaddr attribute is optional for dynamic devices, + # but it is required by non-dynamic devices + if not dev["dynamic"] or self._has_attribute(node, "hwaddr"): + hwaddr = self._get_attribute(node, "hwaddr") + dev["hwaddr"] = normalize_hwaddr(hwaddr) + + # name attribute is only valid when the device is not dynamic + if not dev["dynamic"] and self._has_attribute(node, "name"): dev["name"] = self._get_attribute(node, "name")
+ # bridge attribute is valid only when the device is dynamic + if dev["dynamic"] and self._has_attribute(node, "target_bridge"): + dev["target_bridge"] = self._get_attribute(node, "target_bridge") + self._trigger_event("netdevice_ready", {"machine_id": self._machine_id, "dev_id": phys_id})
Fri, Aug 10, 2012 at 12:49:11PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This commit adds necessary changes to the parser in LNST in order to support the libvirt device creation.
Netdevice tags are now grouped under a <netdevices> parent tag within <netmachineconfig>.
On top of that a special mode for defining netdevices (called create) was added. Devices defined in this mode are not present in the machine and will be created on test execution.
This mode is available only on virtual test machines created using libvirt. Here's an exmaple config of such a machine:
<machine id="1"> <netmachineconfig> <info hostname="192.168.122.11" libvirt_domain="RHEL6" rootpass="redhat"/> <netdevices> <create> <netdevice network="tnet" phys_id="1" type="eth"/> <netdevice network="tnet" phys_id="2" type="eth"/> </create>
This is ok and I love your work! But still I have some concerns about naming. I think that "create" and "tagret_bridge" might be misleading for users.
How about to change it this way: "create" -> "libvirt_create" "target_bridge" -> "libvirt_bridge"
This naming would tell everyone on a first look that this xml element and attribute are only valid for libvirt.
Also, I think that there should be check for <info libvirt_domain=""> attribute in case "libvirt_create" is detected.
Also, I think that dev["dynamic"] = True/False should be uniform to "libvirt_create" somehow. How about:
dev["create"] = None dev["create"] = "libvirt" dev["create"] = "whateverisneededinfuture"
?
</netdevices>
</netmachineconfig> <netconfig> <interface id="1" phys_id="1" type="eth"> <addresses> <address value="192.168.100.226/24"/> </addresses> </interface> <interface id="2" phys_id="2" type="eth"> <addresses> <address value="192.168.100.240/24"/> </addresses> </interface> </netconfig> </machine>
The dynamic devices are enclosed within the <create> tag. Their hwaddr doesn't need to be specified (it will be generated if missing).
Additionally, 'target_bridge' attribute can be added to these devices to override the default LNST internal bridge, that these devices are connected to.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
NetTest/NetTestParse.py | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 746342c..d1edf6b 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -118,7 +118,7 @@ class NetMachineConfigParse(RecipeParser):
def parse(self, node): scheme = {"info": self._info,
"netdevice": self._netdevice}
"netdevices": self._netdevices} self._process_child_nodes(node, scheme)
def _info(self, node, params):
@@ -127,6 +127,10 @@ class NetMachineConfigParse(RecipeParser):
info["hostname"] = self._get_attribute(node, "hostname")
if self._has_attribute(node, "libvirt_domain"):
info["libvirt_domain"] = self._get_attribute(node,
"libvirt_domain")
if self._has_attribute(node, "rootpass"): info["rootpass"] = self._get_attribute(node, "rootpass")
@@ -138,18 +142,42 @@ class NetMachineConfigParse(RecipeParser): self._trigger_event("machine_info_ready", {"machine_id": self._machine_id})
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
"create": self._create}
new_params = {"dynamic": False}
self._process_child_nodes(node, scheme, new_params)
def _create(self, node, params):
scheme = {"netdevice": self._netdevice}
new_params = {"dynamic": True}
self._process_child_nodes(node, scheme, new_params)
def _netdevice(self, node, params): machine = self._machine phys_id = self._get_attribute(node, "phys_id", int)
dev = machine["netdevices"][phys_id] = {}
dev["dynamic"] = params["dynamic"] dev["type"] = self._get_attribute(node, "type") dev["network"] = self._get_attribute(node, "network")
dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
if self._has_attribute(node, "name"):
# hwaddr attribute is optional for dynamic devices,
# but it is required by non-dynamic devices
if not dev["dynamic"] or self._has_attribute(node, "hwaddr"):
hwaddr = self._get_attribute(node, "hwaddr")
dev["hwaddr"] = normalize_hwaddr(hwaddr)
# name attribute is only valid when the device is not dynamic
if not dev["dynamic"] and self._has_attribute(node, "name"): dev["name"] = self._get_attribute(node, "name")
# bridge attribute is valid only when the device is dynamic
if dev["dynamic"] and self._has_attribute(node, "target_bridge"):
dev["target_bridge"] = self._get_attribute(node, "target_bridge")
self._trigger_event("netdevice_ready", {"machine_id": self._machine_id, "dev_id": phys_id})
-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
On 08/10/2012 01:11 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 12:49:11PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This commit adds necessary changes to the parser in LNST in order to support the libvirt device creation.
Netdevice tags are now grouped under a <netdevices> parent tag within <netmachineconfig>.
On top of that a special mode for defining netdevices (called create) was added. Devices defined in this mode are not present in the machine and will be created on test execution.
This mode is available only on virtual test machines created using libvirt. Here's an exmaple config of such a machine:
<machine id="1"> <netmachineconfig> <info hostname="192.168.122.11" libvirt_domain="RHEL6" rootpass="redhat"/> <netdevices> <create> <netdevice network="tnet" phys_id="1" type="eth"/> <netdevice network="tnet" phys_id="2" type="eth"/> </create>
This is ok and I love your work! But still I have some concerns about naming. I think that "create" and "tagret_bridge" might be misleading for users.
How about to change it this way: "create" -> "libvirt_create"
Fair point, we discussed it yesterday and settled on create, because it was shorter, but with libvirt_create it's more clear.
"target_bridge" -> "libvirt_bridge"
On the other hand, I think, that this would be misleading. This attribute is there as a possibility to bypass the default bridge, that is created with the network and connect it to a different (possibly hand-configured one). This option is useful only in cases when you want to mix manually and dynamically configured devices (which should happen only in several corner cases).
I don't know it target_bridge is the best name, but I wouldn't use libvirt_bridge.
This naming would tell everyone on a first look that this xml element and attribute are only valid for libvirt.
Also, I think that there should be check for <info libvirt_domain=""> attribute in case "libvirt_create" is detected.
Also, I think that dev["dynamic"] = True/False should be uniform to "libvirt_create" somehow. How about:
dev["create"] = None dev["create"] = "libvirt" dev["create"] = "whateverisneededinfuture"
?
</netdevices> </netmachineconfig> <netconfig> <interface id="1" phys_id="1" type="eth"> <addresses> <address value="192.168.100.226/24"/> </addresses> </interface> <interface id="2" phys_id="2" type="eth"> <addresses> <address value="192.168.100.240/24"/> </addresses> </interface> </netconfig>
</machine>
The dynamic devices are enclosed within the <create> tag. Their hwaddr doesn't need to be specified (it will be generated if missing).
Additionally, 'target_bridge' attribute can be added to these devices to override the default LNST internal bridge, that these devices are connected to.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
NetTest/NetTestParse.py | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 746342c..d1edf6b 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -118,7 +118,7 @@ class NetMachineConfigParse(RecipeParser):
def parse(self, node): scheme = {"info": self._info,
"netdevice": self._netdevice}
"netdevices": self._netdevices} self._process_child_nodes(node, scheme)
def _info(self, node, params):
@@ -127,6 +127,10 @@ class NetMachineConfigParse(RecipeParser):
info["hostname"] = self._get_attribute(node, "hostname")
if self._has_attribute(node, "libvirt_domain"):
info["libvirt_domain"] = self._get_attribute(node,
"libvirt_domain")
if self._has_attribute(node, "rootpass"): info["rootpass"] = self._get_attribute(node, "rootpass")
@@ -138,18 +142,42 @@ class NetMachineConfigParse(RecipeParser): self._trigger_event("machine_info_ready", {"machine_id": self._machine_id})
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
"create": self._create}
new_params = {"dynamic": False}
self._process_child_nodes(node, scheme, new_params)
def _create(self, node, params):
scheme = {"netdevice": self._netdevice}
new_params = {"dynamic": True}
self._process_child_nodes(node, scheme, new_params)
def _netdevice(self, node, params): machine = self._machine phys_id = self._get_attribute(node, "phys_id", int)
dev = machine["netdevices"][phys_id] = {}
dev["dynamic"] = params["dynamic"] dev["type"] = self._get_attribute(node, "type") dev["network"] = self._get_attribute(node, "network")
dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
if self._has_attribute(node, "name"):
# hwaddr attribute is optional for dynamic devices,
# but it is required by non-dynamic devices
if not dev["dynamic"] or self._has_attribute(node, "hwaddr"):
hwaddr = self._get_attribute(node, "hwaddr")
dev["hwaddr"] = normalize_hwaddr(hwaddr)
# name attribute is only valid when the device is not dynamic
if not dev["dynamic"] and self._has_attribute(node, "name"): dev["name"] = self._get_attribute(node, "name")
# bridge attribute is valid only when the device is dynamic
if dev["dynamic"] and self._has_attribute(node, "target_bridge"):
dev["target_bridge"] = self._get_attribute(node, "target_bridge")
self._trigger_event("netdevice_ready", {"machine_id": self._machine_id, "dev_id": phys_id})
-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
Fri, Aug 10, 2012 at 01:22:56PM CEST, rpazdera@redhat.com wrote:
On 08/10/2012 01:11 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 12:49:11PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This commit adds necessary changes to the parser in LNST in order to support the libvirt device creation.
Netdevice tags are now grouped under a <netdevices> parent tag within <netmachineconfig>.
On top of that a special mode for defining netdevices (called create) was added. Devices defined in this mode are not present in the machine and will be created on test execution.
This mode is available only on virtual test machines created using libvirt. Here's an exmaple config of such a machine:
<machine id="1"> <netmachineconfig> <info hostname="192.168.122.11" libvirt_domain="RHEL6" rootpass="redhat"/> <netdevices> <create> <netdevice network="tnet" phys_id="1" type="eth"/> <netdevice network="tnet" phys_id="2" type="eth"/> </create>
This is ok and I love your work! But still I have some concerns about naming. I think that "create" and "tagret_bridge" might be misleading for users.
How about to change it this way: "create" -> "libvirt_create"
Fair point, we discussed it yesterday and settled on create, because it was shorter, but with libvirt_create it's more clear.
"target_bridge" -> "libvirt_bridge"
On the other hand, I think, that this would be misleading. This attribute is there as a possibility to bypass the default bridge, that is created with the network and connect it to a different (possibly hand-configured one). This option is useful only in cases when you want to mix manually and dynamically configured devices (which should happen only in several corner cases).
Anyway, you specify here that you want "libvirt" to connect this device to some "bridge". So the name seems to be appropriate.
Or do you see some broader use for this attribute that would not be related to "libvirt" ?
I don't know it target_bridge is the best name, but I wouldn't use libvirt_bridge.
This naming would tell everyone on a first look that this xml element and attribute are only valid for libvirt.
Also, I think that there should be check for <info libvirt_domain=""> attribute in case "libvirt_create" is detected.
Also, I think that dev["dynamic"] = True/False should be uniform to "libvirt_create" somehow. How about:
dev["create"] = None dev["create"] = "libvirt" dev["create"] = "whateverisneededinfuture"
?
</netdevices>
</netmachineconfig> <netconfig> <interface id="1" phys_id="1" type="eth"> <addresses> <address value="192.168.100.226/24"/> </addresses> </interface> <interface id="2" phys_id="2" type="eth"> <addresses> <address value="192.168.100.240/24"/> </addresses> </interface> </netconfig> </machine>
The dynamic devices are enclosed within the <create> tag. Their hwaddr doesn't need to be specified (it will be generated if missing).
Additionally, 'target_bridge' attribute can be added to these devices to override the default LNST internal bridge, that these devices are connected to.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
NetTest/NetTestParse.py | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 746342c..d1edf6b 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -118,7 +118,7 @@ class NetMachineConfigParse(RecipeParser):
def parse(self, node): scheme = {"info": self._info,
"netdevice": self._netdevice}
"netdevices": self._netdevices} self._process_child_nodes(node, scheme)
def _info(self, node, params):
@@ -127,6 +127,10 @@ class NetMachineConfigParse(RecipeParser):
info["hostname"] = self._get_attribute(node, "hostname")
if self._has_attribute(node, "libvirt_domain"):
info["libvirt_domain"] = self._get_attribute(node,
"libvirt_domain")
if self._has_attribute(node, "rootpass"): info["rootpass"] = self._get_attribute(node, "rootpass")
@@ -138,18 +142,42 @@ class NetMachineConfigParse(RecipeParser): self._trigger_event("machine_info_ready", {"machine_id": self._machine_id})
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
"create": self._create}
new_params = {"dynamic": False}
self._process_child_nodes(node, scheme, new_params)
def _create(self, node, params):
scheme = {"netdevice": self._netdevice}
new_params = {"dynamic": True}
self._process_child_nodes(node, scheme, new_params)
def _netdevice(self, node, params): machine = self._machine phys_id = self._get_attribute(node, "phys_id", int)
dev = machine["netdevices"][phys_id] = {}
dev["dynamic"] = params["dynamic"] dev["type"] = self._get_attribute(node, "type") dev["network"] = self._get_attribute(node, "network")
dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
if self._has_attribute(node, "name"):
# hwaddr attribute is optional for dynamic devices,
# but it is required by non-dynamic devices
if not dev["dynamic"] or self._has_attribute(node, "hwaddr"):
hwaddr = self._get_attribute(node, "hwaddr")
dev["hwaddr"] = normalize_hwaddr(hwaddr)
# name attribute is only valid when the device is not dynamic
if not dev["dynamic"] and self._has_attribute(node, "name"): dev["name"] = self._get_attribute(node, "name")
# bridge attribute is valid only when the device is dynamic
if dev["dynamic"] and self._has_attribute(node, "target_bridge"):
dev["target_bridge"] = self._get_attribute(node, "target_bridge")
self._trigger_event("netdevice_ready", {"machine_id": self._machine_id, "dev_id": phys_id})
-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
On 08/10/2012 01:42 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 01:22:56PM CEST, rpazdera@redhat.com wrote:
On 08/10/2012 01:11 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 12:49:11PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This commit adds necessary changes to the parser in LNST in order to support the libvirt device creation.
Netdevice tags are now grouped under a <netdevices> parent tag within <netmachineconfig>.
On top of that a special mode for defining netdevices (called create) was added. Devices defined in this mode are not present in the machine and will be created on test execution.
This mode is available only on virtual test machines created using libvirt. Here's an exmaple config of such a machine:
<machine id="1"> <netmachineconfig> <info hostname="192.168.122.11" libvirt_domain="RHEL6" rootpass="redhat"/> <netdevices> <create> <netdevice network="tnet" phys_id="1" type="eth"/> <netdevice network="tnet" phys_id="2" type="eth"/> </create>
This is ok and I love your work! But still I have some concerns about naming. I think that "create" and "tagret_bridge" might be misleading for users.
How about to change it this way: "create" -> "libvirt_create"
Fair point, we discussed it yesterday and settled on create, because it was shorter, but with libvirt_create it's more clear.
"target_bridge" -> "libvirt_bridge"
On the other hand, I think, that this would be misleading. This attribute is there as a possibility to bypass the default bridge, that is created with the network and connect it to a different (possibly hand-configured one). This option is useful only in cases when you want to mix manually and dynamically configured devices (which should happen only in several corner cases).
Anyway, you specify here that you want "libvirt" to connect this device to some "bridge". So the name seems to be appropriate.
Right, I see your point now. From this perspective it makes sense.
It seemed to me as if libvirt would somehow create the bridge (which is possible too, but we don't want that).
Or do you see some broader use for this attribute that would not be related to "libvirt" ?
It could be used in broader terms (to add physical interfaces to these virtual bridges, so the virtual hosts can communicate with them - the "single bare-metal server to many virtual clients test scenario" I talked about at the meeting. But I think we agreed that LNST shouldn't do that. That it could be confused with the bridges for libvirt.
You have to setup the wires yourself anyway, so bridging the physical interface is a part of the manual setup you have to do (in case you chose to perform such complicated tests).
I'm thinking, what about making it "virt_create" and "virt_bridge"? It's a little shorter and it will hide the dependency on libvirt from the recipes. Maybe even change "libvirt_domain" to something else.
Now if libvirt team decides to change the project name, we will have to change all the recipes. But it's just a thought, it's not very likely to happen anytime soon ...
I don't know it target_bridge is the best name, but I wouldn't use libvirt_bridge.
This naming would tell everyone on a first look that this xml element and attribute are only valid for libvirt.
Also, I think that there should be check for <info libvirt_domain=""> attribute in case "libvirt_create" is detected.
Also, I think that dev["dynamic"] = True/False should be uniform to "libvirt_create" somehow. How about:
dev["create"] = None dev["create"] = "libvirt" dev["create"] = "whateverisneededinfuture"
?
</netdevices> </netmachineconfig> <netconfig> <interface id="1" phys_id="1" type="eth"> <addresses> <address value="192.168.100.226/24"/> </addresses> </interface> <interface id="2" phys_id="2" type="eth"> <addresses> <address value="192.168.100.240/24"/> </addresses> </interface> </netconfig>
</machine>
The dynamic devices are enclosed within the <create> tag. Their hwaddr doesn't need to be specified (it will be generated if missing).
Additionally, 'target_bridge' attribute can be added to these devices to override the default LNST internal bridge, that these devices are connected to.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
NetTest/NetTestParse.py | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 746342c..d1edf6b 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -118,7 +118,7 @@ class NetMachineConfigParse(RecipeParser):
def parse(self, node): scheme = {"info": self._info,
"netdevice": self._netdevice}
"netdevices": self._netdevices} self._process_child_nodes(node, scheme)
def _info(self, node, params):
@@ -127,6 +127,10 @@ class NetMachineConfigParse(RecipeParser):
info["hostname"] = self._get_attribute(node, "hostname")
if self._has_attribute(node, "libvirt_domain"):
info["libvirt_domain"] = self._get_attribute(node,
"libvirt_domain")
if self._has_attribute(node, "rootpass"): info["rootpass"] = self._get_attribute(node, "rootpass")
@@ -138,18 +142,42 @@ class NetMachineConfigParse(RecipeParser): self._trigger_event("machine_info_ready", {"machine_id": self._machine_id})
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
"create": self._create}
new_params = {"dynamic": False}
self._process_child_nodes(node, scheme, new_params)
def _create(self, node, params):
scheme = {"netdevice": self._netdevice}
new_params = {"dynamic": True}
self._process_child_nodes(node, scheme, new_params)
def _netdevice(self, node, params): machine = self._machine phys_id = self._get_attribute(node, "phys_id", int)
dev = machine["netdevices"][phys_id] = {}
dev["dynamic"] = params["dynamic"] dev["type"] = self._get_attribute(node, "type") dev["network"] = self._get_attribute(node, "network")
dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
if self._has_attribute(node, "name"):
# hwaddr attribute is optional for dynamic devices,
# but it is required by non-dynamic devices
if not dev["dynamic"] or self._has_attribute(node, "hwaddr"):
hwaddr = self._get_attribute(node, "hwaddr")
dev["hwaddr"] = normalize_hwaddr(hwaddr)
# name attribute is only valid when the device is not dynamic
if not dev["dynamic"] and self._has_attribute(node, "name"): dev["name"] = self._get_attribute(node, "name")
# bridge attribute is valid only when the device is dynamic
if dev["dynamic"] and self._has_attribute(node, "target_bridge"):
dev["target_bridge"] = self._get_attribute(node, "target_bridge")
self._trigger_event("netdevice_ready", {"machine_id": self._machine_id, "dev_id": phys_id})
-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
Mon, Aug 13, 2012 at 10:32:46AM CEST, rpazdera@redhat.com wrote:
On 08/10/2012 01:42 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 01:22:56PM CEST, rpazdera@redhat.com wrote:
On 08/10/2012 01:11 PM, Jiri Pirko wrote:
Fri, Aug 10, 2012 at 12:49:11PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This commit adds necessary changes to the parser in LNST in order to support the libvirt device creation.
Netdevice tags are now grouped under a <netdevices> parent tag within <netmachineconfig>.
On top of that a special mode for defining netdevices (called create) was added. Devices defined in this mode are not present in the machine and will be created on test execution.
This mode is available only on virtual test machines created using libvirt. Here's an exmaple config of such a machine:
<machine id="1"> <netmachineconfig> <info hostname="192.168.122.11" libvirt_domain="RHEL6" rootpass="redhat"/> <netdevices> <create> <netdevice network="tnet" phys_id="1" type="eth"/> <netdevice network="tnet" phys_id="2" type="eth"/> </create>
This is ok and I love your work! But still I have some concerns about naming. I think that "create" and "tagret_bridge" might be misleading for users.
How about to change it this way: "create" -> "libvirt_create"
Fair point, we discussed it yesterday and settled on create, because it was shorter, but with libvirt_create it's more clear.
"target_bridge" -> "libvirt_bridge"
On the other hand, I think, that this would be misleading. This attribute is there as a possibility to bypass the default bridge, that is created with the network and connect it to a different (possibly hand-configured one). This option is useful only in cases when you want to mix manually and dynamically configured devices (which should happen only in several corner cases).
Anyway, you specify here that you want "libvirt" to connect this device to some "bridge". So the name seems to be appropriate.
Right, I see your point now. From this perspective it makes sense.
It seemed to me as if libvirt would somehow create the bridge (which is possible too, but we don't want that).
Or do you see some broader use for this attribute that would not be related to "libvirt" ?
It could be used in broader terms (to add physical interfaces to these virtual bridges, so the virtual hosts can communicate with them - the "single bare-metal server to many virtual clients test scenario" I talked about at the meeting. But I think we agreed that LNST shouldn't do that. That it could be confused with the bridges for libvirt.
You have to setup the wires yourself anyway, so bridging the physical interface is a part of the manual setup you have to do (in case you chose to perform such complicated tests).
I'm thinking, what about making it "virt_create" and "virt_bridge"?
Again, would seem misleading.
It's a little shorter and it will hide the dependency on libvirt from the recipes. Maybe even change "libvirt_domain" to something else.
Now if libvirt team decides to change the project name, we will have to change all the recipes. But it's just a thought, it's not very likely to happen anytime soon ...
Nope.
I don't know it target_bridge is the best name, but I wouldn't use libvirt_bridge.
This naming would tell everyone on a first look that this xml element and attribute are only valid for libvirt.
Also, I think that there should be check for <info libvirt_domain=""> attribute in case "libvirt_create" is detected.
Also, I think that dev["dynamic"] = True/False should be uniform to "libvirt_create" somehow. How about:
dev["create"] = None dev["create"] = "libvirt" dev["create"] = "whateverisneededinfuture"
?
</netdevices>
</netmachineconfig> <netconfig> <interface id="1" phys_id="1" type="eth"> <addresses> <address value="192.168.100.226/24"/> </addresses> </interface> <interface id="2" phys_id="2" type="eth"> <addresses> <address value="192.168.100.240/24"/> </addresses> </interface> </netconfig> </machine>
The dynamic devices are enclosed within the <create> tag. Their hwaddr doesn't need to be specified (it will be generated if missing).
Additionally, 'target_bridge' attribute can be added to these devices to override the default LNST internal bridge, that these devices are connected to.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
NetTest/NetTestParse.py | 34 +++++++++++++++++++++++++++++++--- 1 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index 746342c..d1edf6b 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -118,7 +118,7 @@ class NetMachineConfigParse(RecipeParser):
def parse(self, node): scheme = {"info": self._info,
"netdevice": self._netdevice}
"netdevices": self._netdevices} self._process_child_nodes(node, scheme)
def _info(self, node, params):
@@ -127,6 +127,10 @@ class NetMachineConfigParse(RecipeParser):
info["hostname"] = self._get_attribute(node, "hostname")
if self._has_attribute(node, "libvirt_domain"):
info["libvirt_domain"] = self._get_attribute(node,
"libvirt_domain")
if self._has_attribute(node, "rootpass"): info["rootpass"] = self._get_attribute(node, "rootpass")
@@ -138,18 +142,42 @@ class NetMachineConfigParse(RecipeParser): self._trigger_event("machine_info_ready", {"machine_id": self._machine_id})
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
"create": self._create}
new_params = {"dynamic": False}
self._process_child_nodes(node, scheme, new_params)
def _create(self, node, params):
scheme = {"netdevice": self._netdevice}
new_params = {"dynamic": True}
self._process_child_nodes(node, scheme, new_params)
def _netdevice(self, node, params): machine = self._machine phys_id = self._get_attribute(node, "phys_id", int)
dev = machine["netdevices"][phys_id] = {}
dev["dynamic"] = params["dynamic"] dev["type"] = self._get_attribute(node, "type") dev["network"] = self._get_attribute(node, "network")
dev["hwaddr"] = normalize_hwaddr(self._get_attribute(node, "hwaddr"))
if self._has_attribute(node, "name"):
# hwaddr attribute is optional for dynamic devices,
# but it is required by non-dynamic devices
if not dev["dynamic"] or self._has_attribute(node, "hwaddr"):
hwaddr = self._get_attribute(node, "hwaddr")
dev["hwaddr"] = normalize_hwaddr(hwaddr)
# name attribute is only valid when the device is not dynamic
if not dev["dynamic"] and self._has_attribute(node, "name"): dev["name"] = self._get_attribute(node, "name")
# bridge attribute is valid only when the device is dynamic
if dev["dynamic"] and self._has_attribute(node, "target_bridge"):
dev["target_bridge"] = self._get_attribute(node, "target_bridge")
self._trigger_event("netdevice_ready", {"machine_id": self._machine_id, "dev_id": phys_id})
-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
From: Radek Pazdera rpazdera@redhat.com
Now, when devices can be added dynamically to the slaves, NetConfig is obligated to rescan the existing devices each time a new device is configured.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetConfig/NetConfig.py | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/NetConfig/NetConfig.py b/NetConfig/NetConfig.py index 41a99d8..75d5129 100644 --- a/NetConfig/NetConfig.py +++ b/NetConfig/NetConfig.py @@ -65,6 +65,8 @@ class NetConfig: NetConfigDeviceType(dev_type).type_init()
self._config[if_id] = config + + self._devnames.rescan_netdevs() self._devnames.assign_name(if_id, self._config)
def remove_interface_config(self, if_id): @@ -125,6 +127,8 @@ class NetConfig: if "slaves" in params: netdev["slaves"] = params["slaves"] self._config[dev_id] = netdev + + self._devnames.rescan_netdevs() self._devnames.assign_name(dev_id, self._config) return dev_id
From: Radek Pazdera rpazdera@redhat.com
This commit introduces AddressPool class (and its two children - MacPool and IpPool). This class can be used to generate (and keep a track of) different types of network addresses within a defined range.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- Common/NetUtils.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 53 insertions(+), 0 deletions(-)
diff --git a/Common/NetUtils.py b/Common/NetUtils.py index 42ee6f0..7fe5250 100644 --- a/Common/NetUtils.py +++ b/Common/NetUtils.py @@ -53,3 +53,56 @@ def get_corespond_local_ip(query_ip): if ip is None: return ip return ip.group(1) + +class AddressPool: + def __init__(self, start, end): + self._next = self._addr_to_byte_string(start) + self._final = self._addr_to_byte_string(end) + + def _inc_byte_string(self, byte_string): + if len(byte_string): + byte_string[-1] += 1 + if byte_string[-1] > 255: + del byte_string[-1] + self._inc_byte_string(byte_string) + byte_string.append(0) + + def _addr_to_byte_string(self, addr): + pass + + def _byte_string_to_addr(self, byte_string): + pass + + def get_addr(self): + if self._next > self._final: + msg = "Pool exhausted, no free addresses available" + raise Exception(msg) + + addr_str = self._byte_string_to_addr(self._next) + self._inc_byte_string(self._next) + + return addr_str + +class MacPool(AddressPool): + def _addr_to_byte_string(self, addr): + bs = [int(byte, 16) for byte in addr.split(":")] + + if len(bs) != 6: + raise Exception("Malformed MAC address") + + return bs + + def _byte_string_to_addr(self, byte_string): + return ':'.join(map(lambda x: "%02x" % x, byte_string)) + +class IpPool(AddressPool): + def _addr_to_byte_string(self, addr): + bs = [int(byte) for byte in addr.split(".")] + + if len(bs) != 4: + raise Exception("Malformed MAC address") + + return bs + + def _byte_string_to_addr(self, byte_string): + return '.'.join(map(str, byte_string))
From: Radek Pazdera rpazdera@redhat.com
This commit adds VirtUtils module to Common.
VirtUtils is a set of classes that can be used to manage virtual domains and networks.
VirtDomainCtl can control libvirt domains, VirtNetCtl can control libvirt networks, BridgeCtl can control network bridges.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- Common/VirtUtils.py | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 242 insertions(+), 0 deletions(-) create mode 100644 Common/VirtUtils.py
diff --git a/Common/VirtUtils.py b/Common/VirtUtils.py new file mode 100644 index 0000000..28a3d7e --- /dev/null +++ b/Common/VirtUtils.py @@ -0,0 +1,242 @@ +""" +Utilities for manipulating virtualization host, its guests and +connections between them + +Copyright 2012 Red Hat, Inc. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +rpazdera@redhat.com (Radek Pazdera) +""" + +import os +import re +import copy +import time +import logging +from tempfile import NamedTemporaryFile +from xml.dom import minidom +from ExecCmd import exec_cmd, ExecCmdFail +from Common.NetUtils import normalize_hwaddr, scan_netdevs + +class VirtUtilsError(Exception): + pass + +def _ip(cmd): + try: + exec_cmd("ip %s" % cmd) + except ExecCmdFail as err: + raise VirtUtilsError("ip command error: %s" % err) + +def _brctl(cmd): + try: + exec_cmd("brctl %s" % cmd) + except ExecCmdFail as err: + raise VirtUtilsError("brctl error: %s" % err) + +def _virsh(cmd): + try: + exec_cmd("virsh %s" % cmd) + except ExecCmdFail as err: + raise VirtUtilsError("virsh error: %s" % err) + + +class VirtDomainCtl: + _name = None + + def __init__(self, domain_name): + self._name = domain_name + + def start(self): + _virsh("start %s" % self._name) + + def stop(self): + _virsh("destroy %s" % self._name) + + def restart(self): + _virsh("reboot %s" % self._name) + + def attach_interface(self, hwaddr, net_name, net_type="bridge"): + _virsh("attach-interface %s %s %s --mac %s" % \ + (self._name, net_type, net_name, hwaddr)) + self.ifup(hwaddr) + + def detach_interface(self, hwaddr, net_type="bridge"): + _virsh("detach-interface %s %s %s" % (self._name, net_type, hwaddr)) + + def ifup(self, hwaddr): + _virsh("domif-setlink %s %s up" % (self._name, hwaddr)) + + def ifdown(self, hwaddr): + _virsh("domif-setlink %s %s down" % (self._name, hwaddr)) + +class NetCtl(object): + _name = None + + def __init__(self, name): + self._name = name + + def get_name(self): + return self._name + + def init(self): + pass + + def cleanup(self): + pass + +class VirtNetCtl(NetCtl): + _addr = None + _mask = None + + _dhcp_from = None + _dhcp_to = None + _static_mappings = [] + _lease_file = None + + def setup_dhcp_server(self, addr, mask, range_start, range_end, + static_map=None): + self._addr = addr + self._mask = mask + self._dhcp_from = range_start + self._dhcp_to = range_end + self._lease_file = "/var/lib/libvirt/dnsmasq/%s.leases" % self._name + if static_map: + self._static_mappings = static_map + + def get_dhcp_mapping(self, hwaddr, timeout=30): + wait = timeout + while True: + addr = self._get_map(hwaddr) + if addr or not wait: + break + + time.sleep(1) + wait -= 1 + + return addr + + def _get_map(self, hwaddr): + + try: + leases_file = open(self._lease_file, "r") + except IOError as err: + raise VirtUtilsError("Unable to resolve IP mapping (%s)" % err) + + addr = None + normalized_hwaddr = normalize_hwaddr(hwaddr) + for entry in leases_file: + match = re.match(r"\d+\s+([0-9a-f:]+)\s+([0-9.]+)", entry) + if match: + entry_hwaddr = normalize_hwaddr(match.group(1)) + entry_addr = match.group(2) + if entry_hwaddr == normalized_hwaddr: + addr = entry_addr + break + + leases_file.close() + return addr + + + def init(self): + tmp_file = NamedTemporaryFile(delete=False) + doc = self._get_net_xml_dom() + + doc.writexml(tmp_file) + tmp_file.close() + + _virsh("net-create %s" % tmp_file.name) + os.unlink(tmp_file.name) + + def cleanup(self): + _virsh("net-destroy %s" % self._name) + exec_cmd("rm -f %s" % self._lease_file) + + def _get_net_xml_dom(self): + doc = minidom.Document() + + net = doc.createElement("network") + doc.appendChild(net) + + name = doc.createElement("name") + name_text = doc.createTextNode(self._name) + name.appendChild(name_text) + net.appendChild(name) + + if self._addr: + ip = doc.createElement("ip") + ip.setAttribute("address", self._addr) + ip.setAttribute("netmask", self._mask) + net.appendChild(ip) + + dhcp = doc.createElement("dhcp") + ip.appendChild(dhcp) + + dhcp_range = doc.createElement("range") + dhcp_range.setAttribute("start", self._dhcp_from) + dhcp_range.setAttribute("end", self._dhcp_to) + dhcp.appendChild(dhcp_range) + + for mapping in self._static_mappings: + hwaddr, addr = mapping + host = doc.createElement("host") + host.setAttribute("mac", hwaddr) + host.setAttribute("ip", addr) + dhcp.appendChild(host) + + return doc + +class BridgeCtl(NetCtl): + _remove = False + + def __init__(self, name=None): + if not name: + name = self._generate_name() + + self._check_name(name) + self._name = name + + @staticmethod + def _check_name(name): + if len(name) > 16: + msg = "Bridge name '%s' longer than 16 characters" % name + raise VirtUtilsError(msg) + + @staticmethod + def _generate_name(): + devs = scan_netdevs() + + index = 0 + while True: + name = "lnstbr%d" % index + index += 1 + unique = True + for dev in devs: + if name == dev["name"]: + unique = False + break + + if unique: + return name + + def _exists(self): + devs = scan_netdevs() + for dev in devs: + if self._name == dev["name"]: + return True + + return False + + def init(self): + if not self._exists(): + _brctl("addbr %s" % self._name) + self._remove = True + + _ip("link set %s up" % self._name) + + def cleanup(self): + if self._remove: + _ip("link set %s down" % self._name) + _brctl("delbr %s" % self._name)
From: Radek Pazdera rpazdera@redhat.com
This commit adds the necessary changes to NetTestController that implements integration with libvirt.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestController.py | 91 +++++++++++++++++++++++++++++++++++++++-- 1 files changed, 86 insertions(+), 5 deletions(-)
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py index 1dcfc69..63034eb 100644 --- a/NetTest/NetTestController.py +++ b/NetTest/NetTestController.py @@ -20,10 +20,14 @@ from pprint import pprint, pformat from Common.XmlRpc import ServerProxy from NetTest.NetTestParse import NetTestParse from Common.SlaveUtils import prepare_client_session -from Common.NetUtils import get_corespond_local_ip +from Common.NetUtils import get_corespond_local_ip, MacPool from NetTest.NetTestSlave import DefaultRPCPort from NetTest.NetTestCommand import NetTestCommand, str_command from Common.LoggingServer import LoggingServer +from Common.VirtUtils import VirtNetCtl, VirtDomainCtl, BridgeCtl +from Common.Utils import wait_for + +MAC_POOL_RANGE = {"start": "52:54:01:00:00:01", "end": "52:54:01:FF:FF:FF"}
class NetTestError(Exception): pass @@ -43,6 +47,8 @@ class NetTestController: definitions = {"recipe": self._recipe}
self._recipe["networks"] = {} + self._mac_pool = MacPool(MAC_POOL_RANGE["start"], + MAC_POOL_RANGE["end"])
ntparse = NetTestParse(recipe_path) ntparse.set_recipe(self._recipe) @@ -83,14 +89,70 @@ class NetTestController:
def _prepare_device(self, machine_id, dev_id): info = self._get_machineinfo(machine_id) + rpc = self._get_machinerpc(machine_id) dev = self._recipe["machines"][machine_id]["netdevices"][dev_id]
- dev_net = dev["network"] + dev_net_name = dev["network"] networks = self._recipe["networks"] - if not dev_net in networks: - networks[dev_net] = {"members": []} + if not dev_net_name in networks: + networks[dev_net_name] = {"members": []} + + dev_net = networks[dev_net_name] + dev_net["members"].append((machine_id, dev_id)) + + if dev["dynamic"]: + if not "virt_domain_ctl" in info: + msg = "Cannot create device. " \ + "Machine '%d' is not virtual." % (machine_id) + raise NetTestError(msg) + + if not "hwaddr" in dev: + dev["hwaddr"] = self._mac_pool.get_addr() + + if "target_bridge" in dev: + brctl = BridgeCtl(dev["target_bridge"]) + else: + if "default_bridge" in dev_net: + brctl = dev_net["default_bridge"] + else: + brctl = BridgeCtl() + dev_net["default_bridge"] = brctl + + br_name = brctl.get_name() + brctl.init() + + logging.info("Creating netdevice %d (%s) on machine %d", + dev_id, dev["hwaddr"], machine_id) + + domain_ctl = info["virt_domain_ctl"] + domain_ctl.attach_interface(dev["hwaddr"], br_name)
- networks[dev_net]["members"].append((machine_id, dev_id)) + ready_check_func = lambda: self._device_ready(machine_id, dev_id) + ready = wait_for(ready_check_func, timeout=10) + + if not ready: + msg = "Netdevice initialization failed." \ + "Unable to create device %d (%s) on machine %d" \ + % (dev_id, dev["hwaddr"], machine_id) + + phys_devs = rpc.get_devices_by_hwaddr(dev["hwaddr"]) + if len(phys_devs) == 1: + pass + elif len(phys_devs) < 1: + msg = "Device %d not found on machine %d" \ + % (dev_id, machine_id) + raise NetTestError(msg) + elif len(phys_devs) > 1: + msg = "Multiple netdevices with same address %s on machine %d" \ + % (dev["hwaddr"], machine_id) + raise NetTestError(msg) + + def _device_ready(self, machine_id, dev_id): + rpc = self._get_machinerpc(machine_id) + dev = self._recipe["machines"][machine_id]["netdevices"][dev_id] + + devs = rpc.get_devices_by_hwaddr(dev["hwaddr"]) + return len(devs) > 0
def _prepare_interface(self, machine_id, netdev_config_id): rpc = self._get_machinerpc(machine_id) @@ -122,6 +184,10 @@ class NetTestController: logging.info("Preparing machine #%d", machine_id) info = self._get_machineinfo(machine_id)
+ if "libvirt_domain" in info: + domain_ctl = VirtDomainCtl(info["libvirt_domain"]) + info["virt_domain_ctl"] = domain_ctl + if self._remoteexec and not "session" in info: self._init_slave_session(machine_id)
@@ -184,6 +250,21 @@ class NetTestController: for if_id in reversed(info["configured_interfaces"]): rpc.deconfigure_interface(if_id)
+ # detach dynamically created devices + machine = self._recipe["machines"][machine_id] + for dev_id, dev in machine["netdevices"].iteritems(): + if dev["dynamic"]: + logging.info("Removing netdevice %d (%s) from machine %d", + dev_id, dev["hwaddr"], machine_id) + domain_ctl = info["virt_domain_ctl"] + domain_ctl.detach_interface(dev["hwaddr"]) + + # remove dynamically created bridges + networks = self._recipe["networks"] + for net in networks.itervalues(): + if "default_bridge" in net: + net["default_bridge"].cleanup() + def _disconnect_slaves(self): for machine_id in self._recipe["machines"]: info = self._get_machineinfo(machine_id)
lnst-developers@lists.fedorahosted.org