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 Feb 12, 2025
1 parent df34975 commit f74f0f7
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 0 deletions.
8 changes: 8 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,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):
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,78 @@
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)
self.tap_fd = self.tap_fd.split(":")
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):
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
def switch(self):
return self._switch

@property
def export(self):
return self._export
1 change: 1 addition & 0 deletions virttest/vt_netmgr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .vt_netmgr import vt_netmgr
50 changes: 50 additions & 0 deletions virttest/vt_netmgr/vt_netmgr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import logging

from virttest.vt_resmgr import resmgr

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


class _VTNetworkManager(object):
def __init__(self):
self._res_config = dict()
self._resource_id = dict()

def define_and_create_network(self, network_name, params, net_type="port"):
LOG.debug(f"Defining the network configuration for {network_name}")
self._res_config[network_name] = resmgr.define_resource_config(
network_name, net_type, params
)
LOG.debug(f"Done to define the network configuration for {network_name}")

LOG.debug(f"Create the network object for {network_name}")
self._resource_id[network_name] = resmgr.create_resource_object(
self._res_config[network_name]
)
LOG.debug(f"Done to create the network object for {network_name}")

return self._resource_id[network_name]

def operation(self, resource_id, operation_params):
resmgr.update_resource(resource_id, operation_params)

def query_resource_id(self, network_name):
"""
Return all network names.
:return: All network names.
:rtype: list
"""
return self._resource_id[network_name]

def query_all_network_names(self):
"""
Return all network names.
:return: All network names.
:rtype: list
"""
return list(self._resource_id.keys())


vt_netmgr = _VTNetworkManager()
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,
)
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)
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
Loading

0 comments on commit f74f0f7

Please sign in to comment.