Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
GÉANT Service Orchestrator
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Jira
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
GÉANT Orchestration and Automation Team
GAP
GÉANT Service Orchestrator
Commits
7e8a8f18
Commit
7e8a8f18
authored
2 years ago
by
JORGE SASIAIN
Browse files
Options
Downloads
Patches
Plain Diff
NAT-152
: support list of containers oer service
parent
02d113be
Branches
Branches containing commit
Tags
Tags containing commit
1 merge request
!9
Ipam service
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
gso/oss-params-example.json
+6
-6
6 additions, 6 deletions
gso/oss-params-example.json
gso/services/_ipam.py
+47
-56
47 additions, 56 deletions
gso/services/_ipam.py
gso/settings.py
+2
-2
2 additions, 2 deletions
gso/settings.py
with
55 additions
and
64 deletions
gso/oss-params-example.json
+
6
−
6
View file @
7e8a8f18
...
...
@@ -9,18 +9,18 @@
"password"
:
"robot-user-password"
},
"LO"
:
{
"V4"
:
{
"container"
:
"1.1.0.0/24"
,
"mask"
:
3
1
},
"V6"
:
{
"container"
:
"dead:beef::/64"
,
"mask"
:
12
6
},
"V4"
:
{
"container
s
"
:
[
"1.1.0.0/24"
]
,
"mask"
:
3
2
},
"V6"
:
{
"container
s
"
:
[
"dead:beef::/64"
]
,
"mask"
:
12
8
},
"domain_name"
:
".lo"
},
"TRUNK"
:
{
"V4"
:
{
"container"
:
"1.1.1.0/24"
,
"mask"
:
31
},
"V6"
:
{
"container"
:
"dead:beef::/64"
,
"mask"
:
126
},
"V4"
:
{
"container
s
"
:
[
"1.1.1.0/24"
]
,
"mask"
:
31
},
"V6"
:
{
"container
s
"
:
[
"dead:beef::/64"
]
,
"mask"
:
126
},
"domain_name"
:
".trunk"
},
"GEANT_IP"
:
{
"V4"
:
{
"container"
:
"1.1.2.0/24"
,
"mask"
:
31
},
"V6"
:
{
"container"
:
"dead:beef::/64"
,
"mask"
:
126
},
"V4"
:
{
"container
s
"
:
[
"1.1.2.0/24"
]
,
"mask"
:
31
},
"V6"
:
{
"container
s
"
:
[
"dead:beef::/64"
]
,
"mask"
:
126
},
"domain_name"
:
".geantip"
}
}
...
...
This diff is collapsed.
Click to expand it.
gso/services/_ipam.py
+
47
−
56
View file @
7e8a8f18
import
requests
from
requests.auth
import
HTTPBasicAuth
import
json
import
ipaddress
import
json
import
random
import
requests
from
enum
import
Enum
from
pydantic
import
BaseSettings
from
requests.auth
import
HTTPBasicAuth
from
typing
import
Union
import
random
from
gso
import
settings
...
...
@@ -35,12 +36,19 @@ class HostAddresses(BaseSettings):
v6
:
ipaddress
.
IPv6Address
class
IPAMErrors
(
Enum
):
# HTTP error code, match in error message
CONTAINER_FULL
=
400
,
"
Can not find requested number of networks
"
# TODO: remove this!
# lab infoblox cert is not valid for the ipv4 address
# ... disable warnings for now
requests
.
packages
.
urllib3
.
disable_warnings
()
def
_match_error_code
(
response
,
error_code
):
return
response
.
status_code
==
error_code
.
value
[
0
]
and
error_code
.
value
[
1
]
in
response
.
text
def
_wapi
(
infoblox_params
:
settings
.
InfoBloxParams
):
return
(
f
'
https://
{
infoblox_params
.
host
}
'
f
'
/wapi/
{
infoblox_params
.
wapi_version
}
'
)
...
...
@@ -68,7 +76,7 @@ def _ip_network_version(network):
def
_find_networks
(
network_container
=
None
,
network
=
None
,
ip_version
=
4
):
"""
If network_container is not None, find all networks within the specified container.
Otherwise if network is not None, find the specified network.
Otherwise
,
if network is not None, find the specified network.
Otherwise find all networks.
"""
assert
ip_version
in
[
4
,
6
]
...
...
@@ -121,46 +129,40 @@ def _get_network_capacity(network=None):
def
_allocate_network
(
infoblox_params
:
settings
.
InfoBloxParams
,
network_params
:
Union
[
settings
.
V4NetworkParams
,
settings
.
V6NetworkParams
],
ip_version
=
4
)
->
Union
[
V4ServiceNetwork
,
V6ServiceNetwork
]:
assert
ip_version
in
[
4
,
6
]
endpoint
=
'
network
'
if
ip_version
==
4
else
'
ipv6network
'
ip_container
=
'
networkcontainer
'
if
ip_version
==
4
else
'
ipv6networkcontainer
'
ipv4_
req_payload
=
{
req_payload
=
{
"
network
"
:
{
"
_object_function
"
:
"
next_available_network
"
,
"
_parameters
"
:
{
"
cidr
"
:
network_params
.
mask
},
"
_object
"
:
"
network
container
"
,
"
_object
"
:
ip_
container
,
"
_object_parameters
"
:
{
"
network
"
:
str
(
network_params
.
container
)
"
network
"
:
str
(
network_params
.
container
s
[
0
]
)
},
"
_result_field
"
:
"
networks
"
,
# only return in the response the allocated network, not all available
}
}
ipv6_req_payload
=
{
"
network
"
:
{
"
_object_function
"
:
"
next_available_network
"
,
"
_parameters
"
:
{
"
cidr
"
:
network_params
.
mask
},
"
_object
"
:
"
ipv6networkcontainer
"
,
"
_object_parameters
"
:
{
"
network
"
:
str
(
network_params
.
container
)
},
"
_result_field
"
:
"
networks
"
,
# only return in the response the allocated network, not all available
}
}
req_payload
=
ipv4_req_payload
if
ip_version
==
4
else
ipv6_req_payload
container_index
=
0
while
True
:
r
=
requests
.
post
(
f
'
{
_wapi
(
infoblox_params
)
}
/
{
endpoint
}
'
,
params
=
{
'
_return_fields
'
:
'
network
'
},
json
=
req_payload
,
auth
=
HTTPBasicAuth
(
infoblox_params
.
username
,
infoblox_params
.
password
),
headers
=
{
'
content-type
'
:
"
application/json
"
},
verify
=
False
)
if
not
_match_error_code
(
response
=
r
,
error_code
=
IPAMErrors
.
CONTAINER_FULL
):
break
# Container full: try with next valid container for the service (if any)
container_index
+=
1
if
len
(
network_params
.
containers
)
<
(
container_index
+
1
):
break
req_payload
[
"
network
"
][
"
_object_parameters
"
][
"
network
"
]
=
str
(
network_params
.
containers
[
container_index
])
r
=
requests
.
post
(
f
'
{
_wapi
(
infoblox_params
)
}
/
{
endpoint
}
'
,
params
=
{
'
_return_fields
'
:
'
network
'
},
json
=
req_payload
,
auth
=
HTTPBasicAuth
(
infoblox_params
.
username
,
infoblox_params
.
password
),
headers
=
{
'
content-type
'
:
"
application/json
"
},
verify
=
False
)
# TODO: handle no more available space for networks in the container
assert
r
.
status_code
>=
200
and
r
.
status_code
<
300
,
f
"
HTTP error
{
r
.
status_code
}
:
{
r
.
reason
}
\n\n
{
r
.
text
}
"
assert
'
network
'
in
r
.
json
()
...
...
@@ -226,10 +228,10 @@ def _allocate_host(hostname=None, addr=None, network=None) -> Union[V4HostAddres
else
:
ip_version
=
_ip_addr_version
(
addr
)
ip
v4
_req_payload
=
{
"
ipv
4
addrs
"
:
[
ip_req_payload
=
{
f
"
ipv
{
ip_version
}
addrs
"
:
[
{
"
ipv
4
addr
"
:
addr
f
"
ipv
{
ip_version
}
addr
"
:
addr
}
],
"
name
"
:
hostname
,
...
...
@@ -237,19 +239,6 @@ def _allocate_host(hostname=None, addr=None, network=None) -> Union[V4HostAddres
"
view
"
:
"
default
"
}
ipv6_req_payload
=
{
"
ipv6addrs
"
:
[
{
"
ipv6addr
"
:
addr
}
],
"
name
"
:
hostname
,
"
configure_for_dns
"
:
False
,
"
view
"
:
"
default
"
}
ip_req_payload
=
ipv4_req_payload
if
ip_version
==
4
else
ipv6_req_payload
r
=
requests
.
post
(
f
'
{
_wapi
(
infoblox_params
)
}
/record:host
'
,
json
=
ip_req_payload
,
...
...
@@ -298,19 +287,20 @@ def allocate_service_host(hostname=None, service_type=None, service_networks: Se
If service_networks is not provided, and host_addresses is provided, those specific addresses are used.
If neither is not provided, the first network with available space for this service type is used.
Note that if WFO will always specify the network/addresses after creating it, this mode won
'
t be needed.
Currently this mode doesn
'
t look further than the first container, so if needed, this will need to be updated.
"""
oss
=
settings
.
load_oss_params
()
assert
oss
.
IPAM
ipam_params
=
oss
.
IPAM
assert
hasattr
(
ipam_params
,
service_type
)
and
service_type
!=
'
INFOBLOX
'
,
"
Invalid service type.
"
ipv4_container
=
getattr
(
ipam_params
,
service_type
).
V4
.
container
ipv6_container
=
getattr
(
ipam_params
,
service_type
).
V6
.
container
ipv4_container
s
=
getattr
(
ipam_params
,
service_type
).
V4
.
container
s
ipv6_container
s
=
getattr
(
ipam_params
,
service_type
).
V6
.
container
s
domain_name
=
getattr
(
ipam_params
,
service_type
).
domain_name
# IPv4
if
not
service_networks
and
not
host_addresses
:
ipv4_networks_info
=
_find_networks
(
network_container
=
str
(
ipv4_container
),
ip_version
=
4
)
ipv4_networks_info
=
_find_networks
(
network_container
=
str
(
ipv4_container
s
[
0
]
),
ip_version
=
4
)
assert
len
(
ipv4_networks_info
)
>=
1
,
"
No IPv4 network exists in the container for this service type.
"
first_nonfull_ipv4_network
=
None
for
ipv4_network_info
in
ipv4_networks_info
:
...
...
@@ -326,29 +316,30 @@ def allocate_service_host(hostname=None, service_type=None, service_networks: Se
v4_host
=
_allocate_host
(
hostname
=
hostname
+
domain_name
,
network
=
first_nonfull_ipv4_network
)
elif
service_networks
:
network
=
service_networks
.
v4
assert
network
.
subnet_of
(
ipv4_container
)
assert
any
(
network
.
subnet_of
(
ipv4_container
)
for
ipv4_container
in
ipv4_containers
)
v4_host
=
_allocate_host
(
hostname
=
hostname
+
domain_name
,
network
=
str
(
network
))
elif
host_addresses
:
addr
=
host_addresses
.
v4
assert
addr
in
ipv4_container
assert
any
(
addr
in
ipv4_container
for
ipv4_container
in
ipv4_containers
)
v4_host
=
_allocate_host
(
hostname
=
hostname
+
domain_name
,
addr
=
str
(
addr
))
# IPv6
if
not
service_networks
and
not
host_addresses
:
# ipv6 does not support capacity fetching (not even the GUI displays it).
# Maybe it's assumed that there is always available space?
ipv6_networks_info
=
_find_networks
(
network_container
=
str
(
ipv6_container
),
ip_version
=
6
)
ipv6_networks_info
=
_find_networks
(
network_container
=
str
(
ipv6_container
s
[
0
]
),
ip_version
=
6
)
assert
len
(
ipv6_networks_info
)
>=
1
,
"
No IPv6 network exists in the container for this service type.
"
assert
'
network
'
in
ipv6_networks_info
[
0
]
# TODO: if "no available IP" error, create a new network?
v6_host
=
_allocate_host
(
hostname
=
hostname
+
domain_name
,
network
=
ipv6_networks_info
[
0
][
'
network
'
])
elif
service_networks
:
network
=
service_networks
.
v6
assert
network
.
subnet_of
(
ipv6_container
)
assert
any
(
network
.
subnet_of
(
ipv6_container
)
for
ipv6_container
in
ipv6_containers
)
v6_host
=
_allocate_host
(
hostname
=
hostname
+
domain_name
,
network
=
str
(
network
))
elif
host_addresses
:
addr
=
host_addresses
.
v6
assert
addr
in
ipv6_container
assert
any
(
addr
in
ipv6_container
for
ipv6_container
in
ipv6_containers
)
v6_host
=
_allocate_host
(
hostname
=
hostname
+
domain_name
,
addr
=
str
(
addr
))
return
HostAddresses
(
v4
=
v4_host
.
v4
,
v6
=
v6_host
.
v6
)
...
...
This diff is collapsed.
Click to expand it.
gso/settings.py
+
2
−
2
View file @
7e8a8f18
...
...
@@ -13,12 +13,12 @@ class InfoBloxParams(BaseSettings):
class
V4NetworkParams
(
BaseSettings
):
container
:
ipaddress
.
IPv4Network
container
s
:
list
[
ipaddress
.
IPv4Network
]
mask
:
int
# TODO: validation on mask?
class
V6NetworkParams
(
BaseSettings
):
container
:
ipaddress
.
IPv6Network
container
s
:
list
[
ipaddress
.
IPv6Network
]
mask
:
int
# TODO: validation on mask?
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment