Skip to content

Commit

Permalink
Merge pull request #654 from coreemu/develop
Browse files Browse the repository at this point in the history
8.1.0 merge
  • Loading branch information
bharnden authored Feb 18, 2022
2 parents 31e6839 + cdde1c8 commit bcf7429
Show file tree
Hide file tree
Showing 36 changed files with 932 additions and 767 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## 2022-02-18 CORE 8.1.0

* Installation
* updated dependency versions to account for known vulnerabilities
* GUI
* fixed issue drawing asymmetric link configurations when joining a session
* daemon
* fixed issue getting templates and creating files for config services
* added by directional support for network to network links
* \#647 - fixed issue when creating RJ45 nodes
* \#646 - fixed issue when creating files for Docker nodes
* \#645 - improved wlan change updates to account for all updates with no delay
* services
* fixed file generation for OSPFv2 config service

## 2022-01-12 CORE 8.0.0

*Breaking Changes
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ For more detailed installation see [here](https://coreemu.github.io/core/install
```shell
git clone https://github.com/coreemu/core.git
cd core
# install dependencies to run installation task
./setup.sh
# run the following or open a new terminal
source ~/.bashrc
# Ubuntu
inv install
# CentOS
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.

# this defines the CORE version number, must be static for AC_INIT
AC_INIT(core, 8.0.0)
AC_INIT(core, 8.1.0)

# autoconf and automake initialization
AC_CONFIG_SRCDIR([netns/version.h.in])
Expand Down
16 changes: 12 additions & 4 deletions daemon/core/api/grpc/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,17 +288,25 @@ def StartSession(

# create links
links = []
asym_links = []
edit_links = []
known_links = set()
for link in request.session.links:
if link.options.unidirectional:
asym_links.append(link)
iface1 = link.iface1.id if link.iface1 else None
iface2 = link.iface2.id if link.iface2 else None
if link.node1_id < link.node2_id:
link_id = (link.node1_id, iface1, link.node2_id, iface2)
else:
link_id = (link.node2_id, iface2, link.node1_id, iface1)
if link_id in known_links:
edit_links.append(link)
else:
known_links.add(link_id)
links.append(link)
_, exceptions = grpcutils.create_links(session, links)
if exceptions:
exceptions = [str(x) for x in exceptions]
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
_, exceptions = grpcutils.edit_links(session, asym_links)
_, exceptions = grpcutils.edit_links(session, edit_links)
if exceptions:
exceptions = [str(x) for x in exceptions]
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
Expand Down
24 changes: 18 additions & 6 deletions daemon/core/configservice/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
TEMPLATES_DIR: str = "templates"


def get_template_path(file_path: Path) -> str:
"""
Utility to convert a given file path to a valid template path format.
:param file_path: file path to convert
:return: template path
"""
if file_path.is_absolute():
template_path = str(file_path.relative_to("/"))
else:
template_path = str(file_path)
return template_path


class ConfigServiceMode(enum.Enum):
BLOCKING = 0
NON_BLOCKING = 1
Expand Down Expand Up @@ -295,10 +309,7 @@ def get_templates(self) -> Dict[str, str]:
templates = {}
for file in self.files:
file_path = Path(file)
if file_path.is_absolute():
template_path = str(file_path.relative_to("/"))
else:
template_path = str(file_path)
template_path = get_template_path(file_path)
if file in self.custom_templates:
template = self.custom_templates[file]
template = self.clean_text(template)
Expand All @@ -322,11 +333,12 @@ def create_files(self) -> None:
"node(%s) service(%s) template(%s)", self.node.name, self.name, file
)
file_path = Path(file)
template_path = get_template_path(file_path)
if file in self.custom_templates:
text = self.custom_templates[file]
rendered = self.render_text(text, data)
elif self.templates.has_template(file_path.name):
rendered = self.render_template(file_path.name, data)
elif self.templates.has_template(template_path):
rendered = self.render_template(template_path, data)
else:
text = self.get_text_template(file)
rendered = self.render_text(text, data)
Expand Down
50 changes: 43 additions & 7 deletions daemon/core/configservices/quaggaservices/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from core.emane.nodes import EmaneNet
from core.nodes.base import CoreNodeBase
from core.nodes.interface import DEFAULT_MTU, CoreInterface
from core.nodes.network import WlanNode
from core.nodes.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node

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


def rj45_check(iface: CoreInterface) -> bool:
"""
Helper to detect whether interface is connected an external RJ45
link.
"""
if iface.net:
for peer_iface in iface.net.get_ifaces():
if peer_iface == iface:
continue
if isinstance(peer_iface.node, Rj45Node):
return True
return False


class Zebra(ConfigService):
name: str = "zebra"
group: str = GROUP
Expand Down Expand Up @@ -105,7 +120,13 @@ def data(self) -> Dict[str, Any]:
ip4s.append(str(ip4))
for ip6 in iface.ip6s:
ip6s.append(str(ip6))
ifaces.append((iface, ip4s, ip6s, iface.control))
configs = []
if not iface.control:
for service in services:
config = service.quagga_iface_config(iface)
if config:
configs.append(config.split("\n"))
ifaces.append((iface, ip4s, ip6s, configs))

return dict(
quagga_bin_search=quagga_bin_search,
Expand Down Expand Up @@ -156,17 +177,32 @@ class Ospfv2(QuaggaService, ConfigService):
ipv4_routing: bool = True

def quagga_iface_config(self, iface: CoreInterface) -> str:
if has_mtu_mismatch(iface):
return "ip ospf mtu-ignore"
else:
return ""
has_mtu = has_mtu_mismatch(iface)
has_rj45 = rj45_check(iface)
is_ptp = isinstance(iface.net, PtpNet)
data = dict(has_mtu=has_mtu, is_ptp=is_ptp, has_rj45=has_rj45)
text = """
% if has_mtu:
ip ospf mtu-ignore
% endif
% if has_rj45:
<% return STOP_RENDERING %>
% endif
% if is_ptp:
ip ospf network point-to-point
% endif
ip ospf hello-interval 2
ip ospf dead-interval 6
ip ospf retransmit-interval 5
"""
return self.render_text(text, data)

def quagga_config(self) -> str:
router_id = get_router_id(self.node)
addresses = []
for iface in self.node.get_ifaces(control=False):
for ip4 in iface.ip4s:
addresses.append(str(ip4.ip))
addresses.append(str(ip4))
data = dict(router_id=router_id, addresses=addresses)
text = """
router ospf
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% for iface, ip4s, ip6s, is_control in ifaces:
% for iface, ip4s, ip6s, configs in ifaces:
interface ${iface.name}
% if want_ip4:
% for addr in ip4s:
Expand All @@ -10,13 +10,11 @@ interface ${iface.name}
ipv6 address ${addr}
% endfor
% endif
% if not is_control:
% for service in services:
% for line in service.quagga_iface_config(iface).split("\n"):
% for config in configs:
% for line in config:
${line}
% endfor
% endfor
% endif
% endfor
!
% endfor

Expand Down
63 changes: 62 additions & 1 deletion daemon/core/emulator/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
CORE data objects.
"""
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, List, Optional, Tuple
from typing import TYPE_CHECKING, Any, List, Optional, Tuple

import netaddr

Expand Down Expand Up @@ -176,6 +176,67 @@ class LinkOptions:
key: int = None
buffer: int = None

def update(self, options: "LinkOptions") -> bool:
"""
Updates current options with values from other options.
:param options: options to update with
:return: True if any value has changed, False otherwise
"""
changed = False
if options.delay is not None and 0 <= options.delay != self.delay:
self.delay = options.delay
changed = True
if options.bandwidth is not None and 0 <= options.bandwidth != self.bandwidth:
self.bandwidth = options.bandwidth
changed = True
if options.loss is not None and 0 <= options.loss != self.loss:
self.loss = options.loss
changed = True
if options.dup is not None and 0 <= options.dup != self.dup:
self.dup = options.dup
changed = True
if options.jitter is not None and 0 <= options.jitter != self.jitter:
self.jitter = options.jitter
changed = True
if options.buffer is not None and 0 <= options.buffer != self.buffer:
self.buffer = options.buffer
changed = True
return changed

def is_clear(self) -> bool:
"""
Checks if the current option values represent a clear state.
:return: True if the current values should clear, False otherwise
"""
clear = self.delay is None or self.delay <= 0
clear &= self.jitter is None or self.jitter <= 0
clear &= self.loss is None or self.loss <= 0
clear &= self.dup is None or self.dup <= 0
clear &= self.bandwidth is None or self.bandwidth <= 0
clear &= self.buffer is None or self.buffer <= 0
return clear

def __eq__(self, other: Any) -> bool:
"""
Custom logic to check if this link options is equivalent to another.
:param other: other object to check
:return: True if they are both link options with the same values,
False otherwise
"""
if not isinstance(other, LinkOptions):
return False
return (
self.delay == other.delay
and self.jitter == other.jitter
and self.loss == other.loss
and self.dup == other.dup
and self.bandwidth == other.bandwidth
and self.buffer == other.buffer
)


@dataclass
class LinkData:
Expand Down
Loading

0 comments on commit bcf7429

Please sign in to comment.