Skip to content

Commit bcf7429

Browse files
authored
Merge pull request #654 from coreemu/develop
8.1.0 merge
2 parents 31e6839 + cdde1c8 commit bcf7429

File tree

36 files changed

+932
-767
lines changed

36 files changed

+932
-767
lines changed

CHANGELOG.md

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## 2022-02-18 CORE 8.1.0
2+
3+
* Installation
4+
* updated dependency versions to account for known vulnerabilities
5+
* GUI
6+
* fixed issue drawing asymmetric link configurations when joining a session
7+
* daemon
8+
* fixed issue getting templates and creating files for config services
9+
* added by directional support for network to network links
10+
* \#647 - fixed issue when creating RJ45 nodes
11+
* \#646 - fixed issue when creating files for Docker nodes
12+
* \#645 - improved wlan change updates to account for all updates with no delay
13+
* services
14+
* fixed file generation for OSPFv2 config service
15+
116
## 2022-01-12 CORE 8.0.0
217

318
*Breaking Changes

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ For more detailed installation see [here](https://coreemu.github.io/core/install
2525
```shell
2626
git clone https://github.com/coreemu/core.git
2727
cd core
28+
# install dependencies to run installation task
2829
./setup.sh
30+
# run the following or open a new terminal
31+
source ~/.bashrc
2932
# Ubuntu
3033
inv install
3134
# CentOS

configure.ac

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Process this file with autoconf to produce a configure script.
33

44
# this defines the CORE version number, must be static for AC_INIT
5-
AC_INIT(core, 8.0.0)
5+
AC_INIT(core, 8.1.0)
66

77
# autoconf and automake initialization
88
AC_CONFIG_SRCDIR([netns/version.h.in])

daemon/core/api/grpc/server.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -288,17 +288,25 @@ def StartSession(
288288

289289
# create links
290290
links = []
291-
asym_links = []
291+
edit_links = []
292+
known_links = set()
292293
for link in request.session.links:
293-
if link.options.unidirectional:
294-
asym_links.append(link)
294+
iface1 = link.iface1.id if link.iface1 else None
295+
iface2 = link.iface2.id if link.iface2 else None
296+
if link.node1_id < link.node2_id:
297+
link_id = (link.node1_id, iface1, link.node2_id, iface2)
295298
else:
299+
link_id = (link.node2_id, iface2, link.node1_id, iface1)
300+
if link_id in known_links:
301+
edit_links.append(link)
302+
else:
303+
known_links.add(link_id)
296304
links.append(link)
297305
_, exceptions = grpcutils.create_links(session, links)
298306
if exceptions:
299307
exceptions = [str(x) for x in exceptions]
300308
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
301-
_, exceptions = grpcutils.edit_links(session, asym_links)
309+
_, exceptions = grpcutils.edit_links(session, edit_links)
302310
if exceptions:
303311
exceptions = [str(x) for x in exceptions]
304312
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)

daemon/core/configservice/base.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@
1919
TEMPLATES_DIR: str = "templates"
2020

2121

22+
def get_template_path(file_path: Path) -> str:
23+
"""
24+
Utility to convert a given file path to a valid template path format.
25+
26+
:param file_path: file path to convert
27+
:return: template path
28+
"""
29+
if file_path.is_absolute():
30+
template_path = str(file_path.relative_to("/"))
31+
else:
32+
template_path = str(file_path)
33+
return template_path
34+
35+
2236
class ConfigServiceMode(enum.Enum):
2337
BLOCKING = 0
2438
NON_BLOCKING = 1
@@ -295,10 +309,7 @@ def get_templates(self) -> Dict[str, str]:
295309
templates = {}
296310
for file in self.files:
297311
file_path = Path(file)
298-
if file_path.is_absolute():
299-
template_path = str(file_path.relative_to("/"))
300-
else:
301-
template_path = str(file_path)
312+
template_path = get_template_path(file_path)
302313
if file in self.custom_templates:
303314
template = self.custom_templates[file]
304315
template = self.clean_text(template)
@@ -322,11 +333,12 @@ def create_files(self) -> None:
322333
"node(%s) service(%s) template(%s)", self.node.name, self.name, file
323334
)
324335
file_path = Path(file)
336+
template_path = get_template_path(file_path)
325337
if file in self.custom_templates:
326338
text = self.custom_templates[file]
327339
rendered = self.render_text(text, data)
328-
elif self.templates.has_template(file_path.name):
329-
rendered = self.render_template(file_path.name, data)
340+
elif self.templates.has_template(template_path):
341+
rendered = self.render_template(template_path, data)
330342
else:
331343
text = self.get_text_template(file)
332344
rendered = self.render_text(text, data)

daemon/core/configservices/quaggaservices/services.py

+43-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from core.emane.nodes import EmaneNet
88
from core.nodes.base import CoreNodeBase
99
from core.nodes.interface import DEFAULT_MTU, CoreInterface
10-
from core.nodes.network import WlanNode
10+
from core.nodes.network import PtpNet, WlanNode
11+
from core.nodes.physical import Rj45Node
1112

1213
logger = logging.getLogger(__name__)
1314
GROUP: str = "Quagga"
@@ -55,6 +56,20 @@ def get_router_id(node: CoreNodeBase) -> str:
5556
return "0.0.0.0"
5657

5758

59+
def rj45_check(iface: CoreInterface) -> bool:
60+
"""
61+
Helper to detect whether interface is connected an external RJ45
62+
link.
63+
"""
64+
if iface.net:
65+
for peer_iface in iface.net.get_ifaces():
66+
if peer_iface == iface:
67+
continue
68+
if isinstance(peer_iface.node, Rj45Node):
69+
return True
70+
return False
71+
72+
5873
class Zebra(ConfigService):
5974
name: str = "zebra"
6075
group: str = GROUP
@@ -105,7 +120,13 @@ def data(self) -> Dict[str, Any]:
105120
ip4s.append(str(ip4))
106121
for ip6 in iface.ip6s:
107122
ip6s.append(str(ip6))
108-
ifaces.append((iface, ip4s, ip6s, iface.control))
123+
configs = []
124+
if not iface.control:
125+
for service in services:
126+
config = service.quagga_iface_config(iface)
127+
if config:
128+
configs.append(config.split("\n"))
129+
ifaces.append((iface, ip4s, ip6s, configs))
109130

110131
return dict(
111132
quagga_bin_search=quagga_bin_search,
@@ -156,17 +177,32 @@ class Ospfv2(QuaggaService, ConfigService):
156177
ipv4_routing: bool = True
157178

158179
def quagga_iface_config(self, iface: CoreInterface) -> str:
159-
if has_mtu_mismatch(iface):
160-
return "ip ospf mtu-ignore"
161-
else:
162-
return ""
180+
has_mtu = has_mtu_mismatch(iface)
181+
has_rj45 = rj45_check(iface)
182+
is_ptp = isinstance(iface.net, PtpNet)
183+
data = dict(has_mtu=has_mtu, is_ptp=is_ptp, has_rj45=has_rj45)
184+
text = """
185+
% if has_mtu:
186+
ip ospf mtu-ignore
187+
% endif
188+
% if has_rj45:
189+
<% return STOP_RENDERING %>
190+
% endif
191+
% if is_ptp:
192+
ip ospf network point-to-point
193+
% endif
194+
ip ospf hello-interval 2
195+
ip ospf dead-interval 6
196+
ip ospf retransmit-interval 5
197+
"""
198+
return self.render_text(text, data)
163199

164200
def quagga_config(self) -> str:
165201
router_id = get_router_id(self.node)
166202
addresses = []
167203
for iface in self.node.get_ifaces(control=False):
168204
for ip4 in iface.ip4s:
169-
addresses.append(str(ip4.ip))
205+
addresses.append(str(ip4))
170206
data = dict(router_id=router_id, addresses=addresses)
171207
text = """
172208
router ospf

daemon/core/configservices/quaggaservices/templates/Quagga.conf renamed to daemon/core/configservices/quaggaservices/templates/usr/local/etc/quagga/Quagga.conf

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
% for iface, ip4s, ip6s, is_control in ifaces:
1+
% for iface, ip4s, ip6s, configs in ifaces:
22
interface ${iface.name}
33
% if want_ip4:
44
% for addr in ip4s:
@@ -10,13 +10,11 @@ interface ${iface.name}
1010
ipv6 address ${addr}
1111
% endfor
1212
% endif
13-
% if not is_control:
14-
% for service in services:
15-
% for line in service.quagga_iface_config(iface).split("\n"):
13+
% for config in configs:
14+
% for line in config:
1615
${line}
17-
% endfor
1816
% endfor
19-
% endif
17+
% endfor
2018
!
2119
% endfor
2220

daemon/core/emulator/data.py

+62-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
CORE data objects.
33
"""
44
from dataclasses import dataclass, field
5-
from typing import TYPE_CHECKING, List, Optional, Tuple
5+
from typing import TYPE_CHECKING, Any, List, Optional, Tuple
66

77
import netaddr
88

@@ -176,6 +176,67 @@ class LinkOptions:
176176
key: int = None
177177
buffer: int = None
178178

179+
def update(self, options: "LinkOptions") -> bool:
180+
"""
181+
Updates current options with values from other options.
182+
183+
:param options: options to update with
184+
:return: True if any value has changed, False otherwise
185+
"""
186+
changed = False
187+
if options.delay is not None and 0 <= options.delay != self.delay:
188+
self.delay = options.delay
189+
changed = True
190+
if options.bandwidth is not None and 0 <= options.bandwidth != self.bandwidth:
191+
self.bandwidth = options.bandwidth
192+
changed = True
193+
if options.loss is not None and 0 <= options.loss != self.loss:
194+
self.loss = options.loss
195+
changed = True
196+
if options.dup is not None and 0 <= options.dup != self.dup:
197+
self.dup = options.dup
198+
changed = True
199+
if options.jitter is not None and 0 <= options.jitter != self.jitter:
200+
self.jitter = options.jitter
201+
changed = True
202+
if options.buffer is not None and 0 <= options.buffer != self.buffer:
203+
self.buffer = options.buffer
204+
changed = True
205+
return changed
206+
207+
def is_clear(self) -> bool:
208+
"""
209+
Checks if the current option values represent a clear state.
210+
211+
:return: True if the current values should clear, False otherwise
212+
"""
213+
clear = self.delay is None or self.delay <= 0
214+
clear &= self.jitter is None or self.jitter <= 0
215+
clear &= self.loss is None or self.loss <= 0
216+
clear &= self.dup is None or self.dup <= 0
217+
clear &= self.bandwidth is None or self.bandwidth <= 0
218+
clear &= self.buffer is None or self.buffer <= 0
219+
return clear
220+
221+
def __eq__(self, other: Any) -> bool:
222+
"""
223+
Custom logic to check if this link options is equivalent to another.
224+
225+
:param other: other object to check
226+
:return: True if they are both link options with the same values,
227+
False otherwise
228+
"""
229+
if not isinstance(other, LinkOptions):
230+
return False
231+
return (
232+
self.delay == other.delay
233+
and self.jitter == other.jitter
234+
and self.loss == other.loss
235+
and self.dup == other.dup
236+
and self.bandwidth == other.bandwidth
237+
and self.buffer == other.buffer
238+
)
239+
179240

180241
@dataclass
181242
class LinkData:

0 commit comments

Comments
 (0)