Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Boxiang Sun
slapos.core
Commits
1bc59b59
Commit
1bc59b59
authored
Jul 03, 2023
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Plain Diff
slap/standalone: Use IPv6 range when available
See merge request
nexedi/slapos.core!538
parents
8f9a4ffd
3da9c477
Changes
6
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
257 additions
and
82 deletions
+257
-82
slapos/format.py
slapos/format.py
+9
-3
slapos/grid/promise/generic.py
slapos/grid/promise/generic.py
+2
-6
slapos/slap/standalone.py
slapos/slap/standalone.py
+86
-13
slapos/tests/test_cli.py
slapos/tests/test_cli.py
+1
-4
slapos/tests/test_standalone.py
slapos/tests/test_standalone.py
+144
-54
slapos/util.py
slapos/util.py
+15
-2
No files found.
slapos/format.py
View file @
1bc59b59
...
...
@@ -1464,9 +1464,15 @@ def parse_computer_definition(conf, definition_path):
address_list
.
append
(
dict
(
addr
=
address
,
netmask
=
netmask
))
if
computer_definition
.
has_option
(
section
,
'ipv6_range'
):
ipv6_range_network
=
computer_definition
.
get
(
section
,
'ipv6_range'
)
addr
,
netmask
=
ipv6_range_network
.
split
(
'/'
)
netmask
=
netmaskFromLenIPv6
(
int
(
netmask
))
ipv6_range
=
{
'addr'
:
address
,
'netmask'
:
netmask
,
'network'
:
ipv6_range_network
}
ipv6_range_addr
,
ipv6_range_prefixlen
=
ipv6_range_network
.
split
(
'/'
)
ipv6_range_prefixlen
=
int
(
ipv6_range_prefixlen
)
ipv6_range_netmask
=
netmaskFromLenIPv6
(
ipv6_range_prefixlen
)
ipv6_range
=
{
'addr'
:
ipv6_range_addr
,
'netmask'
:
ipv6_range_netmask
,
'network'
:
ipv6_range_network
,
'prefixlen'
:
ipv6_range_prefixlen
,
}
else
:
ipv6_range
=
{}
tap
=
Tap
(
computer_definition
.
get
(
section
,
'network_interface'
))
...
...
slapos/grid/promise/generic.py
View file @
1bc59b59
...
...
@@ -493,11 +493,7 @@ class GenericPromise(with_metaclass(ABCMeta, object)):
))
elif (not self.__is_tested and not check_anomaly) or
\
(not self.__is_anomaly_detected and check_anomaly):
# Anomaly or Test is disabled on this promise, send empty result
if self.getConfig('
slapgrid
-
version
', '') <= '
1.4
.
17
':
# old version cannot send EmptyResult
self.__sendResult(PromiseQueueResult(item=TestResult()))
else:
# Anomaly or Test is disabled on this promise, send empty
self.__sendResult(PromiseQueueResult())
else:
try:
...
...
slapos/slap/standalone.py
View file @
1bc59b59
...
...
@@ -59,12 +59,25 @@ from .interface.slap import IRequester
from
..grid.slapgrid
import
SLAPGRID_PROMISE_FAIL
from
.slap
import
slap
from
..util
import
dumps
,
rmtree
from
..util
import
dumps
,
rmtree
,
getPartitionIpv6Addr
,
getPartitionIpv6Range
from
..grid.svcbackend
import
getSupervisorRPC
from
..grid.svcbackend
import
_getSupervisordSocketPath
NETMASK_IPV6_FULL
=
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
NETMASK_IPV4_FULL
=
'255.255.255.255'
def
_parseIPv6
(
ipv6
):
try
:
addr
,
prefixlen
=
ipv6
.
split
(
'/'
)
prefixlen
=
int
(
prefixlen
)
except
ValueError
:
addr
,
prefixlen
=
ipv6
,
None
return
addr
,
prefixlen
@
zope
.
interface
.
implementer
(
IException
)
class
SlapOSNodeCommandError
(
Exception
):
"""Exception raised when running a SlapOS Node command failed.
...
...
@@ -205,6 +218,7 @@ class SlapOSConfigWriter(ConfigWriter):
read_only_shared_part_list
=
'
\
n
'
.
join
(
# pylint: disable=unused-variable; used in format()
standalone_slapos
.
_shared_part_list
)
partition_forward_configuration
=
'
\
n
'
.
join
(
self
.
_getPartitionForwardConfiguration
())
has_ipv6_range
=
(
'false'
,
'true'
)[
standalone_slapos
.
_partitions_have_ipv6_range
]
with
open
(
path
,
'w'
)
as
f
:
f
.
write
(
textwrap
.
dedent
(
...
...
@@ -232,6 +246,7 @@ class SlapOSConfigWriter(ConfigWriter):
create_tap = false
create_tun = false
computer_xml = {standalone_slapos._slapos_xml}
partition_has_ipv6_range = {has_ipv6_range}
[slapproxy]
host = {standalone_slapos._server_ip}
...
...
@@ -287,9 +302,7 @@ class SlapformatDefinitionWriter(ConfigWriter):
"""
def
writeConfig
(
self
,
path
):
ipv4
=
self
.
_standalone_slapos
.
_ipv4_address
ipv6
=
self
.
_standalone_slapos
.
_ipv6_address
ipv4_cidr
=
ipv4
+
'/255.255.255.255'
if
ipv4
else
''
ipv6_cidr
=
ipv6
+
'/64'
if
ipv6
else
''
ipv4_cidr
=
'%s/%s'
%
(
ipv4
,
NETMASK_IPV4_FULL
)
if
ipv4
else
''
user
=
pwd
.
getpwuid
(
os
.
getuid
()).
pw_name
partition_base_name
=
self
.
_standalone_slapos
.
_partition_base_name
with
open
(
path
,
'w'
)
as
f
:
...
...
@@ -299,12 +312,24 @@ class SlapformatDefinitionWriter(ConfigWriter):
[computer]
address = {ipv4_cidr}
\
n
"""
).
format
(
**
locals
()))
ipv6
=
self
.
_standalone_slapos
.
_ipv6_address
for
i
in
range
(
self
.
_standalone_slapos
.
_partition_count
):
ipv6_single
,
ipv6_range
=
self
.
_standalone_slapos
.
_getPartitionIpv6
(
i
)
if
ipv6_single
:
ipv6_single_cidr
=
'%s/%s'
%
(
ipv6_single
,
NETMASK_IPV6_FULL
)
else
:
ipv6_single_cidr
=
''
if
ipv6_range
:
ipv6_range_cidr
=
'%(addr)s/%(prefixlen)s'
%
ipv6_range
ipv6_range_config_line
=
'ipv6_range = '
+
ipv6_range_cidr
else
:
ipv6_range_config_line
=
''
f
.
write
(
textwrap
.
dedent
(
"""
[partition_{i}]
address = {ipv6_cidr} {ipv4_cidr}
address = {ipv6_single_cidr} {ipv4_cidr}
{ipv6_range_config_line}
pathname = {partition_base_name}{i}
user = {user}
network_interface =
\
n
...
...
@@ -415,7 +440,13 @@ class StandaloneSlapOS(object):
self
.
_partition_base_name
=
'slappart'
self
.
_ipv4_address
=
None
self
.
_ipv6_address
=
None
self
.
_ipv6_range_prefixlen
=
None
self
.
_partitions_have_ipv6_range
=
False
# NOTE: Using Standalone's own slapos (slapos.cli.entry) instead
# is not that easy because in test nodes standalone is often run
# with gpython (pygolang), and gpython currently doesn't support
# buildout
self
.
_slapos_bin
=
slapos_bin
self
.
_slapos_commands
=
{
...
...
@@ -594,8 +625,12 @@ class StandaloneSlapOS(object):
partition_base_name
=
"slappart"
):
"""Creates `partition_count` partitions.
All partitions have the same `ipv4_address` and `ipv6_address` and
use the current system user.
All partitions have the same `ipv4_address` and use the current system
user.
`ipv6_address` can be a single address (in this case all partitions have
the same address) or a range in the form IPV6/CIDR (in this case each
partition has a subrange).
When calling this a second time with a lower `partition_count` or with
different `partition_base_name` will delete existing partitions.
...
...
@@ -628,17 +663,19 @@ class StandaloneSlapOS(object):
if
not
(
os
.
path
.
exists
(
partition_path
)):
os
.
mkdir
(
partition_path
)
os
.
chmod
(
partition_path
,
0o750
)
ipv6_addr
,
ipv6_range
=
self
.
_getPartitionIpv6
(
i
)
partition_list
.
append
({
'address_list'
:
[
{
'addr'
:
ipv4_address
,
'netmask'
:
'255.255.255.255'
'netmask'
:
NETMASK_IPV4_FULL
},
{
'addr'
:
ipv6_addr
ess
,
'netmask'
:
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
}
,
'addr'
:
ipv6_addr
,
'netmask'
:
NETMASK_IPV6_FULL
}
],
'ipv6_range'
:
ipv6_range
,
'path'
:
partition_path
,
'reference'
:
partition_reference
,
'tap'
:
{
...
...
@@ -665,7 +702,7 @@ class StandaloneSlapOS(object):
self
.
computer
.
updateConfiguration
(
dumps
({
'address'
:
ipv4_address
,
'netmask'
:
'255.255.255.255'
,
'netmask'
:
NETMASK_IPV4_FULL
,
'partition_list'
:
partition_list
,
'reference'
:
self
.
_computer_id
,
'instance_root'
:
self
.
_instance_root
,
...
...
@@ -694,11 +731,33 @@ class StandaloneSlapOS(object):
self
.
_partition_count
=
partition_count
self
.
_partition_base_name
=
partition_base_name
self
.
_ipv4_address
=
ipv4_address
self
.
_ipv6_address
=
ipv6_address
self
.
_ipv6_address
,
prefixlen
=
_parseIPv6
(
ipv6_address
)
self
.
_ipv6_range_prefixlen
=
prefixlen
self
.
_partitions_have_ipv6_range
=
bool
(
prefixlen
)
and
prefixlen
<
112
if
old_partition_count
!=
partition_count
:
SlapOSConfigWriter
(
self
).
writeConfig
(
self
.
_slapos_config
)
SlapformatDefinitionWriter
(
self
).
writeConfig
(
self
.
_slapformat_definition
)
# remove slapos xml configuration in case of ip changes
try
:
os
.
unlink
(
self
.
_slapos_xml
)
except
OSError
as
e
:
if
e
.
errno
!=
errno
.
ENOENT
:
raise
# run slapos format --now
command
=
(
self
.
_slapos_bin
,
'node'
,
'format'
,
'--now'
,
'--cfg'
,
self
.
_slapos_config
)
self
.
_logger
.
debug
(
"Running %s"
,
command
)
try
:
output
=
subprocess
.
check_output
(
command
,
stderr
=
subprocess
.
STDOUT
)
self
.
_logger
.
info
(
output
)
except
subprocess
.
CalledProcessError
as
e
:
self
.
_logger
.
error
(
e
.
output
)
raise
def
supply
(
self
,
software_url
,
computer_guid
=
None
,
state
=
"available"
):
"""Supply a software, see ISupply.supply
...
...
@@ -949,3 +1008,17 @@ class StandaloneSlapOS(object):
return
time
.
sleep
(
i
*
.
01
)
raise
RuntimeError
(
"SlapOS not started"
)
def
_getPartitionIpv6
(
self
,
i
):
# returns (single_ipv6_address, ipv6_range) for a partition
# ipv6_address can be either a range or a single IPv6 address (with no /)
prefixlen
=
self
.
_ipv6_range_prefixlen
if
prefixlen
is
None
:
return
self
.
_ipv6_address
,
None
ipv6_range
=
{
'addr'
:
self
.
_ipv6_address
,
'prefixlen'
:
prefixlen
}
ipv6_single
=
getPartitionIpv6Addr
(
ipv6_range
,
i
)[
'addr'
]
if
self
.
_partitions_have_ipv6_range
:
ipv6_partition_range
=
getPartitionIpv6Range
(
ipv6_range
,
i
,
16
)
return
ipv6_single
,
ipv6_partition_range
else
:
return
ipv6_single
,
None
slapos/tests/test_cli.py
View file @
1bc59b59
...
...
@@ -442,16 +442,13 @@ class TestCliBoot(CliMixin):
patch
(
'slapos.cli.boot.ConfigCommand.config_path'
,
return_value
=
slapos_conf
.
name
),
\
patch
(
'slapos.cli.boot.netifaces.ifaddresses'
,
return_value
=
{
socket
.
AF_INET6
:
({
'addr'
:
'2000::1'
},),},)
as
ifaddresses
,
\
patch
(
'slapos.cli.boot._ping_hostname'
,
return_value
=
1
)
as
_ping_hostname
:
return_value
=
{
socket
.
AF_INET6
:
({
'addr'
:
'2000::1'
},),},)
as
ifaddresses
:
app
.
run
((
'node'
,
'boot'
))
# boot command runs as root
check_root_user
.
assert_called_once
()
# it waits for interface to have an IPv6 address
ifaddresses
.
assert_called_once_with
(
'interface_name_from_config'
)
# then ping master hostname to wait for connectivity
_ping_hostname
.
assert_called_once_with
(
'slap.vifib.com'
)
# then format and bang
SlapOSApp
().
run
.
assert_any_call
([
'node'
,
'format'
,
'--now'
,
'--verbose'
])
SlapOSApp
().
run
.
assert_any_call
([
'node'
,
'bang'
,
'-m'
,
'Reboot'
])
...
...
slapos/tests/test_standalone.py
View file @
1bc59b59
This diff is collapsed.
Click to expand it.
slapos/util.py
View file @
1bc59b59
...
...
@@ -38,6 +38,7 @@ import socket
import
sqlite3
import
struct
import
subprocess
import
sys
import
warnings
import
jsonschema
...
...
@@ -201,7 +202,7 @@ def ipv6FromBin(ip, suffix=''):
if
suffix_len
>
0
:
ip
+=
suffix
.
rjust
(
suffix_len
,
'0'
)
elif
suffix_len
:
sys
.
exit
(
"Prefix
exceeds 128 bits"
)
sys
.
exit
(
"Prefix
%s exceeds 128 bits by %d bit"
%
(
ip
,
-
suffix_len
)
)
return
socket
.
inet_ntop
(
socket
.
AF_INET6
,
struct
.
pack
(
'>QQ'
,
int
(
ip
[:
64
],
2
),
int
(
ip
[
64
:],
2
)))
...
...
@@ -214,11 +215,23 @@ def getPartitionIpv6Addr(ipv6_range, partition_index):
}
returns the IPv6 addr
addr::(partition_index+2) (address 1 is is used by re6st)
If the range is too small, wrap around
"""
addr
=
ipv6_range
[
'addr'
]
prefixlen
=
ipv6_range
[
'prefixlen'
]
prefix
=
binFromIpv6
(
addr
)[:
prefixlen
]
return
dict
(
addr
=
ipv6FromBin
(
prefix
+
bin
(
partition_index
+
2
)[
2
:].
zfill
(
128
-
prefixlen
)),
prefixlen
=
prefixlen
)
remaining
=
128
-
prefixlen
suffix
=
bin
(
partition_index
+
2
)[
2
:]
if
len
(
suffix
)
>
remaining
:
if
remaining
>=
2
:
# skip reserved addresses 0 and 1
suffix
=
bin
((
partition_index
%
((
1
<<
remaining
)
-
2
))
+
2
)[
2
:]
else
:
# truncate, we have no other addresses than 0 and 1
suffix
=
suffix
[
len
(
suffix
)
-
remaining
:]
suffix
=
suffix
.
zfill
(
remaining
)
bits
=
prefix
+
suffix
return
dict
(
addr
=
ipv6FromBin
(
bits
),
prefixlen
=
prefixlen
)
def
getIpv6RangeFactory
(
k
,
s
):
"""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment