Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Multi-host migration] Network subsystem #3987

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
add network system of multi-host migration
add support for network subsystem.

Signed-off-by: Houqi (Nick) Zuo <hzuo@redhat.com>
nickzhq committed Dec 11, 2024
commit 9f587515b4a33ceee6aa524134c85ff1da6f9308
8 changes: 8 additions & 0 deletions virttest/vt_agent/managers/resource_backings/__init__.py
Original file line number Diff line number Diff line change
@@ -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()] = {
@@ -16,6 +21,9 @@
_backing_classes[_NfsVolumeBacking.get_pool_type()] = {
_NfsVolumeBacking.get_resource_type(): _NfsVolumeBacking,
}
_backing_classes[_TapPortBacking.get_pool_type()] = {
_TapPortBacking.get_resource_type(): _TapPortBacking,
}


def get_resource_backing_class(pool_type, resource_type):
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,77 @@
import logging

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

from ...backing import _ResourceBacking

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


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

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

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

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
:type arguments: dict.

:return: the resource info.
:rtype: dict.
"""
if not self.tap_ifname:
self.tap_ifname = "tap_" + utils_misc.generate_random_string(8)
self.tap_fd = tap.open_tap("/dev/net/tun", self.tap_ifname, vnet_hdr=True)
interface.bring_up_ifname(self.tap_ifname)
bridge.add_to_bridge(self.tap_ifname, self.switch)

return self.get_resource_info(network_connection)

def release_resource(self, network_connection, arguments=None):
bridge.del_from_bridge(self.tap_ifname)
interface.bring_down_ifname(self.tap_ifname)
self.tap_fd = None
self.tap_ifname = None

return self.get_resource_info(network_connection)

def get_resource_info(self, network_connection=None, arguments=None):
if self.switch and self.tap_fd and self.tap_ifname:
allocated = (
True
if self.switch in (bridge.find_bridge_name(self.tap_ifname),)
else False
)
else:
allocated = False

return {
"meta": {
"allocated": allocated,
},
"spec": {
"switch": self.switch,
"fds": self.tap_fd,
"ifname": self.tap_ifname,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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.service.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):
nickzhq marked this conversation as resolved.
Show resolved Hide resolved
nickzhq marked this conversation as resolved.
Show resolved Hide resolved
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

@property
nickzhq marked this conversation as resolved.
Show resolved Hide resolved
def switch(self):
return self._switch

@property
def export(self):
return self._export
2 changes: 2 additions & 0 deletions virttest/vt_resmgr/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -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):
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,
)
66 changes: 66 additions & 0 deletions virttest/vt_resmgr/resources/network/port_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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):
pass

def destroy_object(self):
pass

def define_config_from_self(self, pool_id):
pass

@classmethod
def _define_config_legacy(cls, resource_name, resource_params):
return {
"meta": {
"name": resource_name,
"uuid": None,
"type": cls._RESOURCE_TYPE,
"pool": None,
"allocated": False,
"bindings": dict(),
},
"spec": {},
}
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)
nickzhq marked this conversation as resolved.
Show resolved Hide resolved
config["spec"].update(
{
"switch": pool_params["switch"],
"export": pool_params.get("export"),
}
)
return config

def customize_pool_config(self, node_name):
config = self.pool_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 not in ("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
nickzhq marked this conversation as resolved.
Show resolved Hide resolved
Loading