Basic HIP connectivity test
Set up tests compatible with a newer version of CORE (v7.2.1). Added instructions on how to setup development of tests and running them.
This commit is contained in:
parent
9af42730ee
commit
25e5e1132c
|
@ -136,3 +136,5 @@ fabric.properties
|
|||
.history
|
||||
|
||||
# End of https://www.gitignore.io/api/c,clion,visualstudiocode
|
||||
|
||||
**/__pycache__
|
|
@ -0,0 +1,18 @@
|
|||
# Running
|
||||
Scripts must be executed within the core python virtual environment. It is
|
||||
recommended to use the executable installed with core to start this. Note
|
||||
that most scripts include editing network interfaces, which require super-user
|
||||
privileges.
|
||||
```bash
|
||||
sudo core-python test_hip.py
|
||||
```
|
||||
Also note that core-daemon does NOT have to be running.
|
||||
|
||||
# Developing
|
||||
To fully enjoy the capabilities your IDE provides, you must correctly setup the
|
||||
`PYTHONPATH` environment variable. This should include the directory in which
|
||||
core was compiled. As an example, to properly setup VSCode a file with the
|
||||
following contents named `.env` is sufficient:
|
||||
```
|
||||
PYTHONPATH=...path_to_core.../daemon
|
||||
```
|
|
@ -0,0 +1,99 @@
|
|||
import copy
|
||||
import re
|
||||
import uuid
|
||||
from enum import Enum
|
||||
from subprocess import PIPE, Popen
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from core.nodes.base import CoreNode
|
||||
|
||||
|
||||
class State(Enum):
|
||||
IDLE = 0
|
||||
HITGEN = 1
|
||||
READY = 2
|
||||
RUNNING = 3
|
||||
|
||||
|
||||
class HIPNode(CoreNode):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.state = State.IDLE
|
||||
|
||||
def shutdown(self):
|
||||
self.stop()
|
||||
super().shutdown()
|
||||
|
||||
def _state_check(self, *states):
|
||||
if self.state not in states:
|
||||
raise RuntimeError(f"Current state {self.state} is not one of {states}")
|
||||
|
||||
def hitgen(self, ip=None):
|
||||
self._state_check(State.IDLE, State.HITGEN, State.READY)
|
||||
|
||||
if not ip:
|
||||
ip = self.get_ifaces()[0].get_ip4().ip
|
||||
|
||||
self.cmd(
|
||||
" && ".join(
|
||||
[
|
||||
"mkdir -p usr.local.etc.hip",
|
||||
"mkdir -p /usr/local/etc/hip",
|
||||
"mount --bind usr.local.etc.hip /usr/local/etc/hip",
|
||||
"cd /usr/local/etc/hip",
|
||||
"hitgen -conf",
|
||||
f"echo '{uuid.uuid4().hex}' | hitgen",
|
||||
"hitgen -publish",
|
||||
]
|
||||
),
|
||||
shell=True,
|
||||
)
|
||||
tree = ElementTree.parse(
|
||||
f"{self.nodedir}/usr.local.etc.hip/{self.name}_host_identities.pub.xml"
|
||||
)
|
||||
root = tree.getroot()
|
||||
self.host_identity = copy.deepcopy(root[0])
|
||||
self.host_identity.append(ElementTree.fromstring(f"<addr>{ip}</addr>"))
|
||||
self.LSI = self.host_identity.find("LSI").text
|
||||
self.state = State.HITGEN
|
||||
|
||||
def set_known_hosts(self, nodes):
|
||||
self._state_check(State.HITGEN, State.READY)
|
||||
khi = ElementTree.Element("known_host_identities")
|
||||
for node in nodes:
|
||||
khi.append(node.host_identity)
|
||||
|
||||
tree = ElementTree.ElementTree(khi)
|
||||
tree.write(
|
||||
f"{self.nodedir}/usr.local.etc.hip/known_host_identities.xml",
|
||||
encoding="utf-8",
|
||||
xml_declaration=True,
|
||||
)
|
||||
self.state = State.READY
|
||||
|
||||
def command(self, cmd, *args, **kwargs):
|
||||
return Popen(
|
||||
f"vcmd -c {self.ctrlchnlname} -- {cmd}".split(" "), *args, **kwargs
|
||||
)
|
||||
|
||||
def start(self):
|
||||
self._state_check(State.READY)
|
||||
|
||||
self.hip = self.command(
|
||||
"hip -v",
|
||||
stdout=PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
self.state = State.RUNNING
|
||||
self.find(r".*?HIP threads initialization completed.*?")
|
||||
|
||||
def find(self, pattern):
|
||||
while line := self.hip.stdout.readline():
|
||||
if match := re.match(pattern, line):
|
||||
return match
|
||||
|
||||
def stop(self):
|
||||
if self.state == State.RUNNING:
|
||||
self.hip.kill()
|
||||
self.hip.wait()
|
||||
self.state = State.READY
|
|
@ -0,0 +1,58 @@
|
|||
import unittest
|
||||
from subprocess import PIPE
|
||||
from unittest import TestCase
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.network import SwitchNode
|
||||
|
||||
from hipnode import HIPNode
|
||||
|
||||
|
||||
class TestHIP(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.coreemu = CoreEmu()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""We cannot perform manual cleanup of CoreEmu since it ALWAYS performs
|
||||
a cleanup on terminating signals."""
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
self.session = self.coreemu.create_session()
|
||||
self.session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
self.ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||
|
||||
def tearDown(self):
|
||||
self.coreemu.delete_session(self.session.id)
|
||||
|
||||
def test_basic_connectivity(self):
|
||||
"""Test that two computers connected by a switch can communicate."""
|
||||
|
||||
switch = self.session.add_node(SwitchNode)
|
||||
n1 = self.session.add_node(HIPNode)
|
||||
n2 = self.session.add_node(HIPNode)
|
||||
iface1 = self.ip_prefixes.create_iface(n1)
|
||||
self.session.add_link(n1.id, switch.id, iface1)
|
||||
iface2 = self.ip_prefixes.create_iface(n2)
|
||||
self.session.add_link(n2.id, switch.id, iface2)
|
||||
self.session.instantiate()
|
||||
|
||||
n1.hitgen()
|
||||
n2.hitgen()
|
||||
n1.set_known_hosts([n1, n2])
|
||||
n2.set_known_hosts([n1, n2])
|
||||
n1.start()
|
||||
n2.start()
|
||||
|
||||
ret = n1.command(f"ping -c 1 {n2.LSI}", stdout=PIPE).wait()
|
||||
self.assertIsNotNone(n1.find(r".*?HIP exchange complete.*?"))
|
||||
self.assertIsNotNone(n2.find(r".*?HIP exchange complete.*?"))
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2, warnings="ignore")
|
Loading…
Reference in New Issue