daemon: consolidated the code for CoreNode commands into the class itself
This commit is contained in:
parent
b085105a6b
commit
a42697ecce
3 changed files with 16 additions and 119 deletions
|
@ -16,8 +16,7 @@ from core.configservice.dependencies import ConfigServiceDependencies
|
||||||
from core.emulator.data import InterfaceData, LinkData
|
from core.emulator.data import InterfaceData, LinkData
|
||||||
from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes
|
from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes
|
||||||
from core.errors import CoreCommandError, CoreError
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.executables import MOUNT, TEST, VNODED
|
from core.executables import BASH, MOUNT, TEST, VCMD, VNODED
|
||||||
from core.nodes.client import VnodeClient
|
|
||||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface, TunTap, Veth
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface, TunTap, Veth
|
||||||
from core.nodes.netclient import LinuxNetClient, get_net_client
|
from core.nodes.netclient import LinuxNetClient, get_net_client
|
||||||
|
|
||||||
|
@ -504,7 +503,6 @@ class CoreNode(CoreNodeBase):
|
||||||
super().__init__(session, _id, name, server)
|
super().__init__(session, _id, name, server)
|
||||||
self.directory: Optional[Path] = directory
|
self.directory: Optional[Path] = directory
|
||||||
self.ctrlchnlname: Path = self.session.directory / self.name
|
self.ctrlchnlname: Path = self.session.directory / self.name
|
||||||
self.client: Optional[VnodeClient] = None
|
|
||||||
self.pid: Optional[int] = None
|
self.pid: Optional[int] = None
|
||||||
self.lock: RLock = RLock()
|
self.lock: RLock = RLock()
|
||||||
self._mounts: List[Tuple[Path, Path]] = []
|
self._mounts: List[Tuple[Path, Path]] = []
|
||||||
|
@ -546,7 +544,6 @@ class CoreNode(CoreNodeBase):
|
||||||
self.makenodedir()
|
self.makenodedir()
|
||||||
if self.up:
|
if self.up:
|
||||||
raise ValueError("starting a node that is already up")
|
raise ValueError("starting a node that is already up")
|
||||||
|
|
||||||
# create a new namespace for this node using vnoded
|
# create a new namespace for this node using vnoded
|
||||||
vnoded = (
|
vnoded = (
|
||||||
f"{VNODED} -v -c {self.ctrlchnlname} -l {self.ctrlchnlname}.log "
|
f"{VNODED} -v -c {self.ctrlchnlname} -l {self.ctrlchnlname}.log "
|
||||||
|
@ -557,25 +554,17 @@ class CoreNode(CoreNodeBase):
|
||||||
env = self.session.get_environment(state=False)
|
env = self.session.get_environment(state=False)
|
||||||
env["NODE_NUMBER"] = str(self.id)
|
env["NODE_NUMBER"] = str(self.id)
|
||||||
env["NODE_NAME"] = str(self.name)
|
env["NODE_NAME"] = str(self.name)
|
||||||
|
|
||||||
output = self.host_cmd(vnoded, env=env)
|
output = self.host_cmd(vnoded, env=env)
|
||||||
self.pid = int(output)
|
self.pid = int(output)
|
||||||
logger.debug("node(%s) pid: %s", self.name, self.pid)
|
logger.debug("node(%s) pid: %s", self.name, self.pid)
|
||||||
|
|
||||||
# create vnode client
|
|
||||||
self.client = VnodeClient(self.name, self.ctrlchnlname)
|
|
||||||
|
|
||||||
# bring up the loopback interface
|
# bring up the loopback interface
|
||||||
logger.debug("bringing up loopback interface")
|
logger.debug("bringing up loopback interface")
|
||||||
self.node_net_client.device_up("lo")
|
self.node_net_client.device_up("lo")
|
||||||
|
|
||||||
# set hostname for node
|
# set hostname for node
|
||||||
logger.debug("setting hostname: %s", self.name)
|
logger.debug("setting hostname: %s", self.name)
|
||||||
self.node_net_client.set_hostname(self.name)
|
self.node_net_client.set_hostname(self.name)
|
||||||
|
|
||||||
# mark node as up
|
# mark node as up
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
# create private directories
|
# create private directories
|
||||||
for dir_path in PRIVATE_DIRS:
|
for dir_path in PRIVATE_DIRS:
|
||||||
self.create_dir(dir_path)
|
self.create_dir(dir_path)
|
||||||
|
@ -609,13 +598,24 @@ class CoreNode(CoreNodeBase):
|
||||||
logger.exception("error removing node directory")
|
logger.exception("error removing node directory")
|
||||||
# clear interface data, close client, and mark self and not up
|
# clear interface data, close client, and mark self and not up
|
||||||
self.ifaces.clear()
|
self.ifaces.clear()
|
||||||
self.client.close()
|
|
||||||
self.up = False
|
self.up = False
|
||||||
except OSError:
|
except OSError:
|
||||||
logger.exception("error during shutdown")
|
logger.exception("error during shutdown")
|
||||||
finally:
|
finally:
|
||||||
self.rmnodedir()
|
self.rmnodedir()
|
||||||
|
|
||||||
|
def _create_cmd(self, args: str, shell: bool = False) -> str:
|
||||||
|
"""
|
||||||
|
Create command used to run commands within the context of a node.
|
||||||
|
|
||||||
|
:param args: command arguments
|
||||||
|
:param shell: True to run shell like, False otherwise
|
||||||
|
:return: node command
|
||||||
|
"""
|
||||||
|
if shell:
|
||||||
|
args = f'{BASH} -c "{args}"'
|
||||||
|
return f"{VCMD} -c {self.ctrlchnlname} -- {args}"
|
||||||
|
|
||||||
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
Runs a command that is used to configure and setup the network within a
|
Runs a command that is used to configure and setup the network within a
|
||||||
|
@ -627,10 +627,10 @@ class CoreNode(CoreNodeBase):
|
||||||
:return: combined stdout and stderr
|
:return: combined stdout and stderr
|
||||||
:raises CoreCommandError: when a non-zero exit status occurs
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
"""
|
"""
|
||||||
|
args = self._create_cmd(args, shell)
|
||||||
if self.server is None:
|
if self.server is None:
|
||||||
return self.client.check_cmd(args, wait=wait, shell=shell)
|
return utils.cmd(args, wait=wait, shell=shell)
|
||||||
else:
|
else:
|
||||||
args = self.client.create_cmd(args, shell)
|
|
||||||
return self.server.remote_cmd(args, wait=wait)
|
return self.server.remote_cmd(args, wait=wait)
|
||||||
|
|
||||||
def path_exists(self, path: str) -> bool:
|
def path_exists(self, path: str) -> bool:
|
||||||
|
@ -653,7 +653,7 @@ class CoreNode(CoreNodeBase):
|
||||||
:param sh: shell to execute command in
|
:param sh: shell to execute command in
|
||||||
:return: str
|
:return: str
|
||||||
"""
|
"""
|
||||||
terminal = self.client.create_cmd(sh)
|
terminal = self._create_cmd(sh)
|
||||||
if self.server is None:
|
if self.server is None:
|
||||||
return terminal
|
return terminal
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
"""
|
|
||||||
client.py: implementation of the VnodeClient class for issuing commands
|
|
||||||
over a control channel to the vnoded process running in a network namespace.
|
|
||||||
The control channel can be accessed via calls using the vcmd shell.
|
|
||||||
"""
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from core import utils
|
|
||||||
from core.executables import BASH, VCMD
|
|
||||||
|
|
||||||
|
|
||||||
class VnodeClient:
|
|
||||||
"""
|
|
||||||
Provides client functionality for interacting with a virtual node.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name: str, ctrlchnlname: Path) -> None:
|
|
||||||
"""
|
|
||||||
Create a VnodeClient instance.
|
|
||||||
|
|
||||||
:param name: name for client
|
|
||||||
:param ctrlchnlname: control channel name
|
|
||||||
"""
|
|
||||||
self.name: str = name
|
|
||||||
self.ctrlchnlname: Path = ctrlchnlname
|
|
||||||
|
|
||||||
def _verify_connection(self) -> None:
|
|
||||||
"""
|
|
||||||
Checks that the vcmd client is properly connected.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
:raises IOError: when not connected
|
|
||||||
"""
|
|
||||||
if not self.connected():
|
|
||||||
raise IOError("vcmd not connected")
|
|
||||||
|
|
||||||
def connected(self) -> bool:
|
|
||||||
"""
|
|
||||||
Check if node is connected or not.
|
|
||||||
|
|
||||||
:return: True if connected, False otherwise
|
|
||||||
"""
|
|
||||||
return True
|
|
||||||
|
|
||||||
def close(self) -> None:
|
|
||||||
"""
|
|
||||||
Close the client connection.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_cmd(self, args: str, shell: bool = False) -> str:
|
|
||||||
if shell:
|
|
||||||
args = f'{BASH} -c "{args}"'
|
|
||||||
return f"{VCMD} -c {self.ctrlchnlname} -- {args}"
|
|
||||||
|
|
||||||
def check_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
|
|
||||||
"""
|
|
||||||
Run command and return exit status and combined stdout and stderr.
|
|
||||||
|
|
||||||
:param args: command to run
|
|
||||||
:param wait: True to wait for command status, False otherwise
|
|
||||||
:param shell: True to use shell, False otherwise
|
|
||||||
:return: combined stdout and stderr
|
|
||||||
:raises core.CoreCommandError: when there is a non-zero exit status
|
|
||||||
"""
|
|
||||||
self._verify_connection()
|
|
||||||
args = self.create_cmd(args, shell)
|
|
||||||
return utils.cmd(args, wait=wait, shell=shell)
|
|
|
@ -63,39 +63,6 @@ class TestCore:
|
||||||
status = ping(node1, node2, ip_prefixes)
|
status = ping(node1, node2, ip_prefixes)
|
||||||
assert not status
|
assert not status
|
||||||
|
|
||||||
def test_vnode_client(self, request, session: Session, ip_prefixes: IpPrefixes):
|
|
||||||
"""
|
|
||||||
Test vnode client methods.
|
|
||||||
|
|
||||||
:param request: pytest request
|
|
||||||
:param session: session for test
|
|
||||||
:param ip_prefixes: generates ip addresses for nodes
|
|
||||||
"""
|
|
||||||
# create ptp
|
|
||||||
ptp_node = session.add_node(PtpNet)
|
|
||||||
|
|
||||||
# create nodes
|
|
||||||
node1 = session.add_node(CoreNode)
|
|
||||||
node2 = session.add_node(CoreNode)
|
|
||||||
|
|
||||||
# link nodes to ptp net
|
|
||||||
for node in [node1, node2]:
|
|
||||||
iface_data = ip_prefixes.create_iface(node)
|
|
||||||
session.add_link(node.id, ptp_node.id, iface1_data=iface_data)
|
|
||||||
|
|
||||||
# get node client for testing
|
|
||||||
client = node1.client
|
|
||||||
|
|
||||||
# instantiate session
|
|
||||||
session.instantiate()
|
|
||||||
|
|
||||||
# check we are connected
|
|
||||||
assert client.connected()
|
|
||||||
|
|
||||||
# validate command
|
|
||||||
if not request.config.getoption("mock"):
|
|
||||||
assert client.check_cmd("echo hello") == "hello"
|
|
||||||
|
|
||||||
def test_iface(self, session: Session, ip_prefixes: IpPrefixes):
|
def test_iface(self, session: Session, ip_prefixes: IpPrefixes):
|
||||||
"""
|
"""
|
||||||
Test interface methods.
|
Test interface methods.
|
||||||
|
|
Loading…
Reference in a new issue