Skip to content

Commit

Permalink
add network system of multi-host migration
Browse files Browse the repository at this point in the history
add support for network subsystem.

Signed-off-by: Houqi (Nick) Zuo <[email protected]>
  • Loading branch information
nickzhq committed Dec 4, 2024
1 parent f566a82 commit 48a61be
Show file tree
Hide file tree
Showing 13 changed files with 339 additions and 1 deletion.
6 changes: 6 additions & 0 deletions virttest/vt_agent/managers/resource_backings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
_NfsPoolConnection,
_NfsVolumeBacking,
)
from .network import (
_TapPortBacking,
_TapNetworkConnection,
)

_pool_conn_classes = dict()
_pool_conn_classes[_DirPoolConnection.get_pool_type()] = _DirPoolConnection
_pool_conn_classes[_NfsPoolConnection.get_pool_type()] = _NfsPoolConnection
_pool_conn_classes[_TapNetworkConnection.get_pool_type()] = _TapNetworkConnection

_backing_classes = dict()
_backing_classes[_DirVolumeBacking.get_pool_type()] = {
Expand All @@ -16,6 +21,7 @@
_backing_classes[_NfsVolumeBacking.get_pool_type()] = {
_NfsVolumeBacking.get_resource_type(): _NfsVolumeBacking,
}
_backing_classes[_TapPortBacking.get_pool_type()] = _TapPortBacking


def get_resource_backing_class(pool_type, resource_type):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .tap import _TapPortBacking, _TapNetworkConnection
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .tap_backing import _TapPortBacking
from .tap_network_connection import _TapNetworkConnection
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import logging

from virttest.vt_utils.net import interface, tap
from virttest.vt_utils.net.drivers import bridge

from ...backing import _ResourceBacking

LOG = logging.getLogger("avocado.agents.resource_backings.network.tap" + __name__)


class _TapPortBacking(_ResourceBacking):
_SOURCE_POOL_TYPE = "tap"
_BINDING_RESOURCE_TYPE = "port"

def __init__(self, backing_config):
super().__init__(backing_config)
self.switch = None
self.tap_fd = None

def create(self, network_connection):
if not self.switch:
self.switch = network_connection.ifname

def destroy(self, network_connection):
super().destroy(network_connection)
self.switch = None

def allocate_resource(self, network_connection, arguments=None):
"""
Create a tap device and put this device in to network_connection.
:params network_connection: the _TapNetworkConnection object.
:type network_connection: class _TapNetworkConnection.
:params arguments: the device's params as following:
{
"ifname": xxx,
}
:type arguments: dict.
:return: the tap file descriptor.
:rtype: int.
"""
self.tap_fd = tap.open_tap("/dev/net/tun", arguments["ifname"], vnet_hdr=True)
interface.bring_up_ifname(arguments["ifname"])
bridge.add_to_bridge(arguments["ifname"])

return self.get_resource_info(network_connection)

def release_resource(self, network_connection, arguments=None):
bridge.del_from_bridge(arguments["ifname"])
interface.bring_down_ifname(arguments["ifname"])
self.tap_fd = None

return self.get_resource_info(network_connection)

def get_resource_info(self, network_connection, arguments=None):
allocated = (
True
if self.switch is bridge.find_bridge_name(arguments["ifname"])
else False
)

return {
"meta": {
"allocated": allocated,
},
"spec": {
"switch": self.switch,
"fds": self.tap_fd,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import logging

from virttest.vt_utils.net import interface
from virttest.vt_utils.net.drivers import bridge

from ...pool_connection import _ResourcePoolConnection

LOG = logging.getLogger("avocado.agents.resource_backings.network.tap." + __name__)


class _TapNetworkConnection(_ResourcePoolConnection):
_CONNECT_POOL_TYPE = "linux_bridge"

def __init__(self, pool_config):
super().__init__(pool_config)
self._switch = pool_config["spec"]["switch"]
self._export = pool_config["spec"]["export"]

def open(self):
# TODO
pass

def close(self):
# TODO
pass

@property
def connected(self):
if_info = interface.net_get_iface_info(self._switch)
if (
if_info[0]
and if_info[0]["operstate"] in ("UP",)
and (
bridge.find_bridge_name(self._export) in (self._switch,)
or self._export is ""
)
):
return True
return False
2 changes: 2 additions & 0 deletions virttest/vt_resmgr/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
# from .cvm import _TdxPool
# from .storage import _CephPool
from .storage import _DirPool, _NfsPool
from .network import _LinuxBridgeNetwork

_pool_classes = dict()
# _pool_classes[_SnpPool.get_pool_type()] = _SnpPool
# _pool_classes[_TdxPool.get_pool_type()] = _TdxPool
# _pool_classes[_CephPool.get_pool_type()] = _CephPool
_pool_classes[_DirPool.get_pool_type()] = _DirPool
_pool_classes[_NfsPool.get_pool_type()] = _NfsPool
_pool_classes[_LinuxBridgeNetwork.get_pool_type()] = _LinuxBridgeNetwork


def get_resource_pool_class(pool_type):
Expand Down
6 changes: 6 additions & 0 deletions virttest/vt_resmgr/resources/network/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .tap import _LinuxBridgeNetwork


__all__ = (
_LinuxBridgeNetwork,
)
49 changes: 49 additions & 0 deletions virttest/vt_resmgr/resources/network/port_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import uuid
from abc import abstractmethod

from ..resource import _Resource


class _PortResource(_Resource):
"""
This class, inherited from _Resource, defines the port resource model.
"""

_RESOURCE_TYPE = "port"

def __init__(self, resource_config):
super().__init__(resource_config)
self._handlers = {
"bind": self.bind,
"unbind": self.unbind,
"allocate": self.allocate,
"release": self.release,
"sync": self.sync,
}

def bind(self, arguments):
"""
Bind the port resource to one worker node.
"""
raise NotImplemented

def unbind(self, arguments):
"""
Unbind the port resource from the worker node.
"""
raise NotImplemented

def allocate(self, arguments):
raise NotImplemented

def release(self, arguments):
raise NotImplemented

def sync(self, arguments):
raise NotImplemented

def create_object(self):
raise NotImplemented

def destroy_object(self):
raise NotImplemented
2 changes: 2 additions & 0 deletions virttest/vt_resmgr/resources/network/tap/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .tap_network import _LinuxBridgeNetwork
from .tap_port import _TapPort
62 changes: 62 additions & 0 deletions virttest/vt_resmgr/resources/network/tap/tap_network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import logging

from virttest.vt_cluster import cluster

from ...pool import _ResourcePool
from .tap_port import get_port_resource_class

LOG = logging.getLogger("avocado." + __name__)


class _LinuxBridgeNetwork(_ResourcePool):
_POOL_TYPE = "linux_bridge"

@classmethod
def define_config(cls, pool_name, pool_params):
config = super().define_config(pool_name, pool_params)
config["spec"].update(
{
"switch": pool_params["switch"],
"export": pool_params.get("export"),
}
)
return config

def customize_pool_config(self, node_name):
config = self._config
config["spec"]["switch"] = config["spec"]["switch"][node_name]["ifname"]
config["spec"]["export"] = config["spec"]["export"][node_name]["ifname"]
return config

@classmethod
def get_resource_class(cls, resource_type):
return get_port_resource_class(resource_type)

def meet_resource_request(self, resource_type, resource_params):
if resource_type is not ("port",) or resource_params.get("nettype") not in (
"bridge",
):
return False

if not self._check_nodes_access(resource_params):
return False

return True

def _check_nodes_access(self, resource_params):
# Note if you want the image is created from a specific pool or
# the image is handled on a specific worker node, you should
# specify its image_pool_name
vm_node_tag = resource_params.get("vm_node")
if vm_node_tag:
# Check if the pool can be accessed by the vm node
vm_node = cluster.get_node_by_tag(vm_node_tag)
if vm_node.name not in self.attaching_nodes:
return False
else:
# Check if the pool can be accessed by one of the partition nodes
node_names = [node.name for node in cluster.partition.nodes]
if not set(self.attaching_nodes).intersection(set(node_names)):
return False

return True
95 changes: 95 additions & 0 deletions virttest/vt_resmgr/resources/network/tap/tap_port.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import logging

from virttest.vt_cluster import cluster

from ..port_resource import _PortResource


LOG = logging.getLogger("avocado." + __name__)


class _TapPort(_PortResource):
"""
The tap port.
"""

def bind(self, arguments):
"""
Bind the resource to a backing on a worker node.
"""
nodes = arguments.pop("nodes", list(self.resource_bindings.keys()))
for node_name in nodes:
if not self.resource_bindings.get(node_name):
LOG.info(f"Bind the tap port {self.resource_id} to node {node_name}")
node = cluster.get_node(node_name)
r, o = node.proxy.resource.create_backing_object(self.resource_config)
if r != 0:
raise Exception(o["out"])
self.resource_bindings[node_name] = o["out"]
else:
LOG.info(f"The tap port {self.resource_id} has already bound to {node_name}")

def unbind(self, arguments):
"""
Unbind the tap port from a worker node
"""
nodes = arguments.pop("nodes", list(self.resource_bindings.keys()))
for node_name in nodes:
backing_id = self.resource_bindings.get(node_name)
if backing_id:
LOG.info(f"Unbind the tap port {self.resource_id} from node {node_name}")
node = cluster.get_node(node_name)
r, o = node.proxy.resource.destroy_backing_object(backing_id)
if r != 0:
raise Exception(o["out"])
self.resource_bindings[node_name] = None
else:
LOG.info(f"The tap port {self.resource_id} has already unbound from {node_name}")

def sync(self, arguments):
LOG.debug(f"Sync up the configuration of the tap port {self.resource_id}")
node_name, backing_id = list(self.resource_bindings.items())[0]
node = cluster.get_node(node_name)
r, o = node.proxy.resource.update_backing(backing_id,
{"sync": arguments})
if r != 0:
raise Exception(o["out"])

config = o["out"]
self.resource_meta["allocated"] = config["meta"]["allocated"]
self.resource_spec["switch"] = config["spec"]["switch"]
self.resource_spec["export"] = config["spec"]["export"]

def allocate(self, arguments):
node_name, backing_id = list(self.resource_bindings.items())[0]
LOG.debug(f"Allocate the tap port {self.resource_id} from {node_name}.")
node = cluster.get_node(node_name)
r, o = node.proxy.resource.update_backing(backing_id,
{"allocate": arguments})
if r != 0:
raise Exception(o["out"])

config = o["out"]
self.resource_meta["allocated"] = config["meta"]["allocated"]
self.resource_spec["switch"] = config["spec"]["switch"]
self.resource_spec["export"] = config["spec"]["export"]

def release(self, arguments):
node_name, backing_id = list(self.resource_bindings.items())[0]
LOG.debug(f"Release the tap port {self.resource_id} from {node_name}")
node = cluster.get_node(node_name)
r, o = node.proxy.resource.update_backing(backing_id,
{"release": arguments})
if r != 0:
raise Exception(o["error"])
self.resource_meta["allocated"] = False
self.resource_spec["switch"] = None
self.resource_spec["export"] = None


def get_port_resource_class(resource_type):
mapping = {
"port": _TapPort,
}

return mapping.get(resource_type)
3 changes: 3 additions & 0 deletions virttest/vt_resmgr/resources/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ def pool_id(self):
def pool_config(self):
return self._config

def customize_pool_config(self, node_name):
return self._config

@property
def pool_meta(self):
return self._config["meta"]
Expand Down
2 changes: 1 addition & 1 deletion virttest/vt_resmgr/vt_resmgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def _attach_pool_to(self, pool, node):
Attach a pool to a specific node
"""
LOG.info(f"Attach resource pool ({pool.pool_name}) to {node.name}")
r, o = node.proxy.resource.connect_pool(pool.pool_id, pool.pool_config)
r, o = node.proxy.resource.connect_pool(pool.pool_id, pool.customize_pool_config(node.name))
if r != 0:
raise Exception(o["out"])

Expand Down

0 comments on commit 48a61be

Please sign in to comment.