Commit 49ce8ef5 authored by Kirill Smelkov's avatar Kirill Smelkov

software/ors-amarisoft: Provide dedicated TAP interface for each Radio Unit

SlapOS provides to each partition dedicated TAP interface, so that an instance
could organize internal networking. In practice this is used by KVM software
release and here in ors-amarisoft, where we feed to eNB such TAP interface for
CPRI-based radio unit so that eNB, in turn, could provide access to CPRI
Control and Management channel via provided TAP.

However there is a problem: SlapOS provides only one TAP interface per
instance, while we need to have one TAP for each Radio Unit.

-> Solve this problem by creating TAP "subinterfaces" per each RU ourselves.

The interfaces we create are full TAP interfaces, just we name them with prefix
based on main interface, and we allocate only part of address space of sole
SlapOS TAP to each subtap. For example if SlapOS provided us slaptap16 with
2401:5180:0:66:a200::/71 IPv6 range and we want to split it for 2 radio units,
we'll be splitting it into 3 regions as follows:

    slaptap16: split 2401:5180:0:66:a200::/71 by 2
    preserve         2401:5180:0:66:a200::/73
    -> slaptap16-1   2401:5180:0:66:a280::/73
    -> slaptap16-2   2401:5180:0:66:a300::/73

Here we preserve 2401:5180:0:66:a200::/73 for usage on original slaptap16, and
we create slaptap16-1 and slaptap16-2 with correspondingly allocated address
range subparts for the RUs.

The splitting is done easily but depends on having networking administration
capability to be able to work. We solve this with employing
/opt/amarisoft/setcap, which we already use for dnsmasq, and with compiled
trampoline program because setcap does not really work directly on scripts.

To avoid hard dependency on having network capability rights, we fallback to
using regular SlapOS slaptap in case there is only one RU.

ru/lopcomm/* and enb.cfg are adapted straightforwardly, but dnsmasq adaptation
is left to a later step not to mix everything into one pile.

NOTE that relying on setcap is not a good in the long term and should be
     reworked once SlapOS is improved to provide ability for instances to
     request several TAP interfaces. Please see discussion at
     !1471 (comment 194356)
     for details.

/cc @jhuge, @lu.xu, @xavier_thompson, @Daetalus
/reviewed-on !1471
parent 695518f2
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
[template] [template]
filename = instance.cfg filename = instance.cfg
md5sum = 4a38fac68d3a6ca8d9114673cfa42b77 md5sum = b2f62658035bd70231a57fcd753f0035
[amarisoft-stats.jinja2.py] [amarisoft-stats.jinja2.py]
_update_hash_filename_ = amarisoft-stats.jinja2.py _update_hash_filename_ = amarisoft-stats.jinja2.py
...@@ -28,7 +28,7 @@ md5sum = ab666fdfadbfc7d8a16ace38d295c883 ...@@ -28,7 +28,7 @@ md5sum = ab666fdfadbfc7d8a16ace38d295c883
[ru_libinstance.jinja2.cfg] [ru_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/libinstance.jinja2.cfg _update_hash_filename_ = ru/libinstance.jinja2.cfg
md5sum = 09cc6f04c43ef3a00519f585716343d9 md5sum = 46f5ba10f37f188806b5c5de104dc185
[ru_sdr_libinstance.jinja2.cfg] [ru_sdr_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/sdr/libinstance.jinja2.cfg _update_hash_filename_ = ru/sdr/libinstance.jinja2.cfg
...@@ -36,7 +36,7 @@ md5sum = c20b620111a4dc4bc2bcae57c2007cbe ...@@ -36,7 +36,7 @@ md5sum = c20b620111a4dc4bc2bcae57c2007cbe
[ru_lopcomm_libinstance.jinja2.cfg] [ru_lopcomm_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/lopcomm/libinstance.jinja2.cfg _update_hash_filename_ = ru/lopcomm/libinstance.jinja2.cfg
md5sum = e67ccf369ccd016e38c5be42442edeae md5sum = 02a7a12b933544b4287599afed0fc68d
[ru_sunwave_libinstance.jinja2.cfg] [ru_sunwave_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/sunwave/libinstance.jinja2.cfg _update_hash_filename_ = ru/sunwave/libinstance.jinja2.cfg
...@@ -48,27 +48,35 @@ md5sum = 6f8d0592cc4b0b695cea5a0c25aafc4e ...@@ -48,27 +48,35 @@ md5sum = 6f8d0592cc4b0b695cea5a0c25aafc4e
[ru_lopcomm_stats.jinja2.py] [ru_lopcomm_stats.jinja2.py]
_update_hash_filename_ = ru/lopcomm/stats.jinja2.py _update_hash_filename_ = ru/lopcomm/stats.jinja2.py
md5sum = b861ef43beba4a0a2904e8c2aee04723 md5sum = 3a5a3c5588e1698ee81e983bb08e70d7
[ru_lopcomm_config.jinja2.py] [ru_lopcomm_config.jinja2.py]
_update_hash_filename_ = ru/lopcomm/config.jinja2.py _update_hash_filename_ = ru/lopcomm/config.jinja2.py
md5sum = f2f550b68c8ab243ce1a4bb73a9abc1c md5sum = 167537a6aa2762355ee703d4c96351ea
[ru_lopcomm_reset-info.jinja2.py] [ru_lopcomm_reset-info.jinja2.py]
_update_hash_filename_ = ru/lopcomm/reset-info.jinja2.py _update_hash_filename_ = ru/lopcomm/reset-info.jinja2.py
md5sum = e03d7fea24566aa8291f0dc4aaf09c67 md5sum = 3d78df1993211efaabd3dc6f2ec8de30
[ru_lopcomm_reset.jinja2.py] [ru_lopcomm_reset.jinja2.py]
_update_hash_filename_ = ru/lopcomm/reset.jinja2.py _update_hash_filename_ = ru/lopcomm/reset.jinja2.py
md5sum = 63472d5dc9bd46923d3941b5189e2ccd md5sum = 9741fbc99aaf768e9cc3ab48925dfee5
[ru_lopcomm_software.jinja2.py] [ru_lopcomm_software.jinja2.py]
_update_hash_filename_ = ru/lopcomm/software.jinja2.py _update_hash_filename_ = ru/lopcomm/software.jinja2.py
md5sum = f36eef13210ff519f6a6e56f081f397a md5sum = 2b08bb666c5f3ab287cdddbfdb4c9249
[ru_lopcomm_supervision.jinja2.py] [ru_lopcomm_supervision.jinja2.py]
_update_hash_filename_ = ru/lopcomm/supervision.jinja2.py _update_hash_filename_ = ru/lopcomm/supervision.jinja2.py
md5sum = 11623b42a0b6271ba0abc8b40e7630e5 md5sum = 437cc45f132d6c74647a716c6a1920e1
[ru_tapsplit]
_update_hash_filename_ = ru/tapsplit
md5sum = 2b8b57c5771b2a2203c0e7767e629e55
[ru_capdo.c]
_update_hash_filename_ = ru/capdo.c
md5sum = 52da9fe3a569199e35ad89ae1a44c30e
[template-enb] [template-enb]
_update_hash_filename_ = instance-enb.jinja2.cfg _update_hash_filename_ = instance-enb.jinja2.cfg
...@@ -100,7 +108,7 @@ md5sum = dcaac06553a3222b14c0013a13f4a149 ...@@ -100,7 +108,7 @@ md5sum = dcaac06553a3222b14c0013a13f4a149
[enb.jinja2.cfg] [enb.jinja2.cfg]
filename = config/enb.jinja2.cfg filename = config/enb.jinja2.cfg
md5sum = 8bcf0524a89da28a0329ae598165684b md5sum = 8e49dfc9318da43bd817b8891cba24b7
[drb_lte.jinja2.cfg] [drb_lte.jinja2.cfg]
filename = config/drb_lte.jinja2.cfg filename = config/drb_lte.jinja2.cfg
......
...@@ -84,18 +84,7 @@ ...@@ -84,18 +84,7 @@
{{ cell_list[k].get('cpri_tx_dbm', 0) }} {{ cell_list[k].get('cpri_tx_dbm', 0) }}
{%- endfor -%} {%- endfor -%}
", ",
ifname: " ifname: "{{ cell_list.values() | map(attribute='_tap') | join(',') }}",
{%- if cell_count == 1 -%}
{{ slap_configuration.get('tap-name', '') }}
{%- else -%}
{%- for i, k in enumerate(cell_list) %}
{%- if i != 0 -%}
,
{%- endif -%}
{{ slap_configuration.get('tap-name', '') }}-{{ i }}
{%- endfor -%}
{%- endif -%}
",
}, },
tx_gain: 0, tx_gain: 0,
rx_gain: 0, rx_gain: 0,
......
...@@ -29,6 +29,7 @@ context = ...@@ -29,6 +29,7 @@ context =
key eggs_directory buildout:eggs-directory key eggs_directory buildout:eggs-directory
key develop_eggs_directory buildout:develop-eggs-directory key develop_eggs_directory buildout:develop-eggs-directory
raw buildout_directory ${buildout:directory} raw buildout_directory ${buildout:directory}
raw pythonwitheggs ${buildout:bin-directory}/pythonwitheggs
section slap_connection slap-connection section slap_connection slap-connection
key slapparameter_dict slap-configuration:configuration key slapparameter_dict slap-configuration:configuration
key lan_ipv4 lan-ip:ipv4 key lan_ipv4 lan-ip:ipv4
...@@ -268,6 +269,7 @@ extra-context = ...@@ -268,6 +269,7 @@ extra-context =
raw monitor_template ${monitor2-template:output} raw monitor_template ${monitor2-template:output}
section ors_version ors-version section ors_version ors-version
section ors_id ors-id section ors_id ors-id
section slap_configuration slap-configuration
key lte_version amarisoft:lte-version key lte_version amarisoft:lte-version
key lte_expiration amarisoft:lte-expiration key lte_expiration amarisoft:lte-expiration
key enb amarisoft:enb key enb amarisoft:enb
...@@ -287,6 +289,8 @@ extra-context = ...@@ -287,6 +289,8 @@ extra-context =
raw ru_lopcomm_cu_config_template ${ru_lopcomm_cu_config.jinja2.xml:target} raw ru_lopcomm_cu_config_template ${ru_lopcomm_cu_config.jinja2.xml:target}
raw ru_lopcomm_firmware_path ${ru_lopcomm_firmware-dl:target} raw ru_lopcomm_firmware_path ${ru_lopcomm_firmware-dl:target}
raw ru_lopcomm_firmware_filename ${ru_lopcomm_firmware-dl:filename} raw ru_lopcomm_firmware_filename ${ru_lopcomm_firmware-dl:filename}
raw ru_tapsplit ${ru_tapsplit:target}
raw netcapdo ${netcapdo:exe}
raw openssl_location ${openssl:location} raw openssl_location ${openssl:location}
raw default_lte_bandwidth ${default-params:default-lte-bandwidth} raw default_lte_bandwidth ${default-params:default-lte-bandwidth}
raw default_lte_inactivity_timer ${default-params:default-lte-inactivity-timer} raw default_lte_inactivity_timer ${default-params:default-lte-inactivity-timer}
......
...@@ -6,5 +6,27 @@ extends = ...@@ -6,5 +6,27 @@ extends =
lopcomm/buildout.cfg lopcomm/buildout.cfg
sunwave/buildout.cfg sunwave/buildout.cfg
parts +=
netcapdo
setcap-netcapdo
[ru_libinstance.jinja2.cfg] [ru_libinstance.jinja2.cfg]
<= download-base <= download-base
[ru_tapsplit]
<= download-base
[ru_capdo.c]
<= download-base
[netcapdo]
recipe = plone.recipe.command
exe = ${buildout:directory}/netcapdo
command = gcc ${:ccflags} -o ${:exe} ${ru_capdo.c:target} -lcap
ccflags = -I${libcap:location}/include -L${libcap:location}/lib -Wl,-rpath=${libcap:location}/lib
stop-on-error = true
[setcap-netcapdo]
<= setcap
exe = ${netcapdo:exe}
// Copyright (C) 2023 Nexedi SA and Contributors.
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Program `capdo prog ...` executes prog with inherited capabilities.
// It is used as trampoline to run scripts under setcap environment.
//
// TODO Relying on setcap should be removed once SlapOS is improved to provide
// several TAP interfaces to instances. See discussion at
// https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
// for details.
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/capability.h>
#include <sys/prctl.h>
static int die(const char *fmt, ...);
static int die_err(const char *msg);
int main(int argc, const char *argv[]) {
cap_t caps;
cap_value_t cap;
cap_flag_value_t flag;
uint64_t capbits = 0;
if (argc < 2)
die("usage: capdo prog arguments...");
// permitted -> inheritable (so that we can raise ambient below)
caps = cap_get_proc();
if (!caps)
die("cap_get_proc failed");
for (cap = 0; cap < CAP_LAST_CAP; cap++) {
if (cap_get_flag(caps, cap, CAP_PERMITTED, &flag)) {
if (errno = EINVAL)
continue; // this cap is not supported by running kernel
die_err("cap_get_flag");
}
if (flag) {
cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, flag) && die_err("cap_set_flag");
capbits |= (1ULL << cap);
}
}
cap_set_proc(caps) && die_err("cap_set_proc");
// raise ambient capabilities to what is permitted/inheritable
// this way executed program will have the same capabilities that we have
for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
if (capbits & (1ULL << cap))
prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) && die_err("prctl ambient raise");
}
// tail to exec target
argv++;
execv(argv[0], argv);
// the only chance we are here is due to exec error
die_err(argv[0]);
}
static int die(const char* fmt, ...) {
va_list ap;
fprintf(stderr, "E: capdo: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(128);
return 0;
}
static int die_err(const char* msg) {
return die("%s: %s", msg, strerror(errno));
}
...@@ -39,6 +39,82 @@ config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }} ...@@ -39,6 +39,82 @@ config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
lopcomm=rudrv_lopcomm, lopcomm=rudrv_lopcomm,
sunwave=rudrv_sunwave) %} sunwave=rudrv_sunwave) %}
{#- slaptap indicates tap interface, that slapos told us to use,
or 'xxx-notap-xxx' if slapos provided us either nothing or empty string. #}
{%- set slaptap = slap_configuration.get('tap-name', '') %}
{%- if slaptap == '' %}
{%- set slaptap = 'xxx-notap-xxx' %}
{%- endif %}
{#- split slapos tap interface for each RU
fallback to non-split approach for ntap <= 1 to avoid hard-dependecy on setcap/tapsplit
TODO Relying on setcap and tapsplit should be removed once SlapOS is improved to
provide several TAP interfaces to instances. See discussion at
https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
for details. #}
{%- set ntap = len(list(cell_list|dictsort)) %}
[vtap]
recipe = plone.recipe.command
ntap = {{ ntap }}
command = {{ netcapdo }} {{ pythonwitheggs }} {{ ru_tapsplit }} {{ slaptap }} ${:ntap}
update-command = ${:command}
stop-on-error = true
{%- if ntap <= 1 %}
[vtap]
ntap = 0
stop-on-error = false
{%- if ntap == 1 %}
[vtap.{{ slaptap }}]
network = {{ slap_configuration['tap-ipv6-network'] }}
gateway = {{ slap_configuration['tap-ipv6-gateway'] }}
addr = {{ slap_configuration['tap-ipv6-addr'] }}
{%- endif %}
{%- else %}
{%- for i in range(1,1+ntap) %}
{%- set tap = '%s-%d' % (slaptap, i) %}
[vtap.{{ tap }}]
recipe = slapos.recipe.build
depends = ${vtap:recipe}
init =
import types
def readfile(path):
with open(path) as f:
return f.read()
# ~ import tapsplit
tapsplit = types.ModuleType('tapsplit')
exec(readfile('{{ ru_tapsplit }}'), tapsplit.__dict__)
# simulate what tapsplit would assign to the tap
# ( tap subinterface will be created for real later at install time - when it
# is too late to update section options )
slapnet = tapsplit.ifnet6('{{ slaptap }}')
tapnet = tapsplit.netsplit(slapnet, {{ 1+ntap }}) [{{ i }}]
options['network'] = str(tapnet)
options['gateway'] = str(tapnet[1])
options['addr'] = str(tapnet[-1])
{%- endfor %}
{%- endif %}
{#- assign TAP interfaces to RUs #}
{%- for i, (cell_ref, cell) in enumerate(cell_list|dictsort) %}
{%- if len(cell_list) > 1 %}
{%- set ru_tap = "%s-%d" % (slaptap, i+1) %}
{%- else %}
{%- set ru_tap = slaptap %}
{%- endif %}
{%- do cell.update({'_tap': ru_tap}) %}
{%- endfor %}
{#- go through all RUs and for each RU invoke {#- go through all RUs and for each RU invoke
RU-specific buildout handler #} RU-specific buildout handler #}
{%- set ru_type = {'lopcomm': 'lopcomm', 'm2ru': 'sunwave'}.get(ru, 'sdr') %} {%- set ru_type = {'lopcomm': 'lopcomm', 'm2ru': 'sunwave'}.get(ru, 'sdr') %}
......
...@@ -8,7 +8,7 @@ if __name__ == '__main__': ...@@ -8,7 +8,7 @@ if __name__ == '__main__':
nc = LopcommNetconfClient(log_file="{{ log_file }}") nc = LopcommNetconfClient(log_file="{{ log_file }}")
while True: while True:
try: try:
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword") nc.connect("{{ netaddr.IPAddress(vtap.gateway) }}", 830, "oranuser", "oranpassword")
nc.edit_config(["{{ CreateProcessingEle_template }}", "{{ cu_config_template }}"]) nc.edit_config(["{{ CreateProcessingEle_template }}", "{{ cu_config_template }}"])
break break
except Exception as e: except Exception as e:
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
{{ promise('%s-netconf-socket' % ru_ref) }} {{ promise('%s-netconf-socket' % ru_ref) }}
promise = check_socket_listening promise = check_socket_listening
config-host = ${slap-configuration:tap-ipv6-gateway} config-host = ${vtap.{{cell._tap}}:gateway}
config-port = 830 config-port = 830
...@@ -21,7 +21,7 @@ supervision-reply-json-log-output = ${:_logbase}-reply.json.log ...@@ -21,7 +21,7 @@ supervision-reply-json-log-output = ${:_logbase}-reply.json.log
is_netconf_connected = ${directory:etc}/{{ru_ref}}.is_netconf_connected is_netconf_connected = ${directory:etc}/{{ru_ref}}.is_netconf_connected
context = context =
section directory directory section directory directory
section slap_configuration slap-configuration section vtap vtap.{{ cell._tap }}
key slapparameter_dict slap-configuration:configuration key slapparameter_dict slap-configuration:configuration
key log_file :log-output key log_file :log-output
key supervision_reply_json_log_file :supervision-reply-json-log-output key supervision_reply_json_log_file :supervision-reply-json-log-output
...@@ -59,7 +59,7 @@ remote-file-path = sftp://${user-info:pw-name}@[${slap-configuration:ipv6-random ...@@ -59,7 +59,7 @@ remote-file-path = sftp://${user-info:pw-name}@[${slap-configuration:ipv6-random
is_firmware_updated = ${directory:etc}/{{ru_ref}}.is_firmware_updated is_firmware_updated = ${directory:etc}/{{ru_ref}}.is_firmware_updated
context = context =
section directory directory section directory directory
section slap_configuration slap-configuration section vtap vtap.{{ cell._tap }}
key slapparameter_dict slap-configuration:configuration key slapparameter_dict slap-configuration:configuration
key log_file :log-output key log_file :log-output
key software_reply_json_log_file :software-reply-json-log-output key software_reply_json_log_file :software-reply-json-log-output
...@@ -104,8 +104,7 @@ extensions = jinja2.ext.do ...@@ -104,8 +104,7 @@ extensions = jinja2.ext.do
log-output = ${directory:var}/log/{{ru_ref}}-config.log log-output = ${directory:var}/log/{{ru_ref}}-config.log
context = context =
section directory directory section directory directory
section slap_configuration slap-configuration section vtap vtap.{{ cell._tap }}
key slapparameter_dict slap-configuration:configuration
key log_file :log-output key log_file :log-output
raw testing {{ slapparameter_dict.get("testing", False) }} raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs raw python_path {{ buildout_directory}}/bin/pythonwitheggs
...@@ -136,7 +135,7 @@ ncsession-json-log-output = ${:_logbase}-ncsession.json.log ...@@ -136,7 +135,7 @@ ncsession-json-log-output = ${:_logbase}-ncsession.json.log
software-json-log-output = ${:_logbase}-software.json.log software-json-log-output = ${:_logbase}-software.json.log
context = context =
section directory directory section directory directory
section slap_configuration slap-configuration section vtap vtap.{{ cell._tap }}
key slapparameter_dict slap-configuration:configuration key slapparameter_dict slap-configuration:configuration
key log_file :log-output key log_file :log-output
key json_log_file :json-log-output key json_log_file :json-log-output
...@@ -206,7 +205,7 @@ _logbase = ${directory:var}/log/{{ru_ref}}-reset-info ...@@ -206,7 +205,7 @@ _logbase = ${directory:var}/log/{{ru_ref}}-reset-info
log-output = ${:_logbase}.log log-output = ${:_logbase}.log
json-log-output = ${:_logbase}.json.log json-log-output = ${:_logbase}.json.log
context = context =
section slap_configuration slap-configuration section vtap vtap.{{ cell._tap }}
key log_file :log-output key log_file :log-output
key json_log_file :json-log-output key json_log_file :json-log-output
raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }} raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
...@@ -224,7 +223,7 @@ _logbase = ${directory:var}/log/{{ru_ref}}-reset ...@@ -224,7 +223,7 @@ _logbase = ${directory:var}/log/{{ru_ref}}-reset
log-output = ${:_logbase}.log log-output = ${:_logbase}.log
json-log-output = ${:_logbase}.json.log json-log-output = ${:_logbase}.json.log
context = context =
section slap_configuration slap-configuration section vtap vtap.{{ cell._tap }}
key log_file :log-output key log_file :log-output
raw python_path {{ buildout_directory}}/bin/pythonwitheggs raw python_path {{ buildout_directory}}/bin/pythonwitheggs
raw buildout_directory_path {{ buildout_directory }} raw buildout_directory_path {{ buildout_directory }}
......
...@@ -19,7 +19,7 @@ def get_uptime(hostname, username, password): ...@@ -19,7 +19,7 @@ def get_uptime(hostname, username, password):
client.close() client.close()
# Usage # Usage
hostname = "{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}" hostname = "{{ netaddr.IPAddress(vtap.gateway) }}"
username = "oranuser" username = "oranuser"
password = "oranpassword" password = "oranpassword"
......
...@@ -7,7 +7,7 @@ from ncclient_common import LopcommNetconfClient ...@@ -7,7 +7,7 @@ from ncclient_common import LopcommNetconfClient
if __name__ == '__main__': if __name__ == '__main__':
nc = LopcommNetconfClient(log_file="{{ log_file }}") nc = LopcommNetconfClient(log_file="{{ log_file }}")
try: try:
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword") nc.connect("{{ netaddr.IPAddress(vtap.gateway) }}", 830, "oranuser", "oranpassword")
nc.reset_device() nc.reset_device()
nc.logger.info("Device reset successful.") nc.logger.info("Device reset successful.")
except Exception as e: except Exception as e:
......
...@@ -16,7 +16,7 @@ if __name__ == '__main__': ...@@ -16,7 +16,7 @@ if __name__ == '__main__':
while True: while True:
try: try:
firmware_check_file= '{{ is_firmware_updated }}' firmware_check_file= '{{ is_firmware_updated }}'
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword") nc.connect("{{ netaddr.IPAddress(vtap.gateway) }}", 830, "oranuser", "oranpassword")
# Fetch software inventory # Fetch software inventory
inventory_vars = nc.get_inventory() inventory_vars = nc.get_inventory()
......
...@@ -15,7 +15,7 @@ if __name__ == '__main__': ...@@ -15,7 +15,7 @@ if __name__ == '__main__':
) )
while True: while True:
try: try:
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword") nc.connect("{{ netaddr.IPAddress(vtap.gateway) }}", 830, "oranuser", "oranpassword")
nc.subscribe() nc.subscribe()
while True: while True:
nc.get_notification() nc.get_notification()
......
...@@ -15,7 +15,7 @@ if __name__ == '__main__': ...@@ -15,7 +15,7 @@ if __name__ == '__main__':
) )
try: try:
netconf_check_file = '{{ is_netconf_connected }}' netconf_check_file = '{{ is_netconf_connected }}'
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword") nc.connect("{{ netaddr.IPAddress(vtap.gateway) }}", 830, "oranuser", "oranpassword")
supervision_subscription_rpc_xml = """ supervision_subscription_rpc_xml = """
<create-subscription xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"> <create-subscription xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
......
#!/usr/bin/env python
# Copyright (C) 2023 Nexedi SA and Contributors.
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""Program tapsplit brings tap interface into state with several children
interfaces each covering part of original interface address space.
Usage: tapsplit <interface> <nchildren>
"""
# TODO Relying on tapsplit should be removed once SlapOS is improved to provide
# several TAP interfaces to instances. See discussion at
# https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
# for details.
import netifaces
import netaddr
from socket import AF_INET6
from math import log2, ceil
import sys
import subprocess
def main():
tap = sys.argv[1]
n = int(sys.argv[2])
assert n >= 0, n
# determine tap's network address and owner
owner = readfile(sysnet(tap) + '/owner') .strip()
net = ifnet6(tap)
print('%s: split %s by %d' % (tap, net, n))
# cleanup existing children
for ifname in netifaces.interfaces():
if ifname.startswith('%s-' % tap):
run('ip', 'link', 'del', ifname)
# do the split
# with leaving first range for the original tap
for i, subnet in enumerate(netsplit(net, 1+n)):
if i == 0:
print('preserve %s' % subnet)
continue # leave this range for original tap
subtap = '%s-%d' % (tap, i)
print('-> %s %s' % (subtap, subnet))
run('ip', 'tuntap', 'add', 'dev', subtap, 'mode', 'tap', 'user', owner)
run('ip', 'link', 'set', subtap, 'up')
run('ip', 'addr', 'add', str(subnet), 'dev', subtap, 'noprefixroute')
run('ip', 'route', 'add', str(subnet[1]), 'dev', subtap)
run('ip', 'route', 'add', str(subnet), 'dev', subtap, 'via', str(subnet[1]))
# netsplit splits network into n subnetworks.
def netsplit(net, n): # -> []subnet
# see how much prefix bits we need to take to be able to divide by n
ptake = ceil(log2(n))
return list( net.subnet(net.prefixlen + ptake) )[:n]
# ifnet6 returns IPv6 network address associated with given interface.
def ifnet6(ifname):
addr = None
net = None
prefixlen = None
for iaddr in netifaces.ifaddresses(ifname)[AF_INET6]:
a = iaddr['addr']
if '%' in a: # link-local
a = a.split('%')[0]
a = netaddr.IPAddress(a)
assert a.is_link_local(), a
continue
if addr is not None:
raise RuntimeError('%s: multiple addresses: %s and %s' % (ifname, addr, a))
addr = netaddr.IPAddress(a)
netmask, plen = iaddr['netmask'].split('/')
prefixlen = int(plen)
net = netaddr.IPNetwork('%s/%d' % (a, prefixlen))
if addr is None:
raise RuntimeError('%s: no non link-local addresses' % ifname)
# normalize network
# ex 2401:5180:0:66:a7ff:ffff:ffff:ffff/71 -> 2401:5180:0:66:a600::/71
net = net.cidr
return net
# sysnet returns path on /sys corresponding to given interface.
def sysnet(ifname):
return '/sys/devices/virtual/net/%s' % ifname
def run(*argv):
print(' # %s' % ' '.join(argv))
subprocess.check_call(argv)
def readfile(path):
with open(path) as f:
return f.read()
if __name__ == '__main__':
main()
...@@ -15,6 +15,7 @@ extends = ...@@ -15,6 +15,7 @@ extends =
../../component/dnsmasq/buildout.cfg ../../component/dnsmasq/buildout.cfg
../../component/fluent-bit/buildout.cfg ../../component/fluent-bit/buildout.cfg
../../component/openssh/buildout.cfg ../../component/openssh/buildout.cfg
../../component/libcap/buildout.cfg
ru/buildout.cfg ru/buildout.cfg
parts += parts +=
...@@ -43,7 +44,7 @@ parts += ...@@ -43,7 +44,7 @@ parts +=
dnsmasq dnsmasq
eggs eggs
xamari xamari
setcap setcap-dnsmasq
# unimplemented parts - the http monitor and better log handling using logrotate # unimplemented parts - the http monitor and better log handling using logrotate
# apache-php # apache-php
# logrotate # logrotate
...@@ -170,6 +171,8 @@ eggs = ...@@ -170,6 +171,8 @@ eggs =
xmltodict xmltodict
ncclient ncclient
${lxml-python:egg} ${lxml-python:egg}
netifaces
netaddr
interpreter = pythonwitheggs interpreter = pythonwitheggs
[xlte-repository] [xlte-repository]
...@@ -193,9 +196,13 @@ scripts = xamari ...@@ -193,9 +196,13 @@ scripts = xamari
[setcap] [setcap]
recipe = plone.recipe.command recipe = plone.recipe.command
command = sudo -n /opt/amarisoft/setcap ${dnsmasq:location}/sbin/dnsmasq || true command = sudo -n /opt/amarisoft/setcap ${:exe} || true
update-command = ${:command} update-command = ${:command}
[setcap-dnsmasq]
<= setcap
exe = ${dnsmasq:location}/sbin/dnsmasq
[versions] [versions]
websocket-client = 1.4.2 websocket-client = 1.4.2
ncclient = 0.6.13 ncclient = 0.6.13
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment