Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
O
opennsa3
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
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
Michal Hažlinský
opennsa3
Commits
05682d60
Commit
05682d60
authored
3 years ago
by
Michal Hažlinský
Browse files
Options
Downloads
Plain Diff
Merge branch 'feature_backend-juniper-csd'
merge csd backend
parents
f2fdc726
080a520b
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
opennsa/backends/junoscsd.py
+328
-0
328 additions, 0 deletions
opennsa/backends/junoscsd.py
with
328 additions
and
0 deletions
opennsa/backends/junoscsd.py
0 → 100644
+
328
−
0
View file @
05682d60
"""
Backend for Juniper Junos SPACE CSD plugin.
Author: Michal Hazlinsky <hazlinsky at cesnet.cz>
Backend specific configuraton:
space_user=USERNAME
space_password=SECRET_PASSWD
space_api_url=BASE_URL
csd_service_def=SERVICE_DEF_ID_TO_USE # from your CSD insatnce
csd_customer_id=CUSTOMER_ID_TO_USE # from your CSD instance
routers=ROUTER1_NAME@ROUTER1_ID
ROUTER2_NAME@ROUTER2_ID
"""
import
base64
import
random
import
time
from
twisted.python
import
log
from
twisted.web.error
import
Error
as
WebError
from
twisted.internet.ssl
import
ClientContextFactory
from
opennsa
import
constants
as
cnt
,
config
from
opennsa.backends.common
import
genericbackend
from
opennsa.protocols.shared
import
httpclient
from
lxml
import
etree
CSD_TIMEOUT
=
60
# ncs typically spends 25-32 seconds creating/deleting a vpn, sometimes a bit more
# NO_OUT_OF_SYNC_CHECK = 'no-out-of-sync-check' # put this as a query parameter to get ncs to bypass the check
URI_CREATE_ORDER
=
"
api/space/nsas/eline-ptp/service-management/service-orders/
"
URI_GET_SERVICES
=
"
api/space/nsas/eline-ptp/service-management/services
"
URI_DELETE_SERVICE
=
"
api/space/nsas/eline-ptp/service-management/services/%(service_id)s
"
ORDER_PAYLOAD
=
"""
<Data xmlns=
"
services.schema.networkapi.jmp.juniper.net
"
>
<ServiceResource>
<ServiceOrder>
<Common>
<Name>%(service_name)s</Name>
<Comments>%(description)s</Comments>
</Common>
<ServiceEndPointGroup>
<DeviceInfo>
<NA>
<DeviceName>%(router_a_name)s</DeviceName>
<DeviceID>%(router_a_id)s</DeviceID>
</NA>
</DeviceInfo>
<ServiceEndPoint>
<InterfaceName>%(interface_a)s</InterfaceName>
<ServiceEndpointConfiguration xmlns:xsi=
"
http://www.w3.org/2001/XMLSchema-instance
"
xsi:type=
"
PTPElineLDPEndPointConfigParameterOrderType
"
>
<EndPointCategory>PTP</EndPointCategory>
<TrafficType>DOT1Q Transport single vlan</TrafficType>
<EthernetOption>dot1q</EthernetOption>
<UnitId>%(vlan_a)s</UnitId>
<VlanId>%(vlan_a)s</VlanId>
</ServiceEndpointConfiguration>
</ServiceEndPoint>
</ServiceEndPointGroup>
<ServiceEndPointGroup>
<DeviceInfo>
<NA>
<DeviceName>%(router_b_name)s</DeviceName>
<DeviceID>%(router_b_id)s</DeviceID>
</NA>
</DeviceInfo>
<ServiceEndPoint>
<InterfaceName>%(interface_b)s</InterfaceName>
<ServiceEndpointConfiguration xmlns:xsi=
"
http://www.w3.org/2001/XMLSchema-instance
"
xsi:type=
"
PTPElineLDPEndPointConfigParameterOrderType
"
>
<EndPointCategory>PTP</EndPointCategory>
<TrafficType>DOT1Q Transport single vlan</TrafficType>
<EthernetOption>dot1q</EthernetOption>
<UnitId>%(vlan_a)s</UnitId>
<VlanId>%(vlan_b)s</VlanId>
</ServiceEndpointConfiguration>
</ServiceEndPoint>
</ServiceEndPointGroup>
<ServiceOrderParameter xmlns:xsi=
"
http://www.w3.org/2001/XMLSchema-instance
"
xsi:type=
"
PTPConfigParameterOrderType
"
>
</ServiceOrderParameter>
<Reference>
<Customer key=
"
%(cus_key)s
"
/>
<ServiceDefinition>
<ServiceDefinitionID key=
"
%(service_def_id)s
"
/>
</ServiceDefinition>
</Reference>
</ServiceOrder>
</ServiceResource>
<CustomAction xmlns:xsi=
"
http://www.w3.org/2001/XMLSchema-instance
"
xsi:type=
"
ServiceOrderCustomActionType
"
>
<Action>SaveAndDeployNow</Action>
</CustomAction>
</Data>
"""
LOG_SYSTEM
=
'
JUNOS.CSD
'
class
JUNOSSPACERouter
(
object
):
def
__init__
(
self
,
router_name
,
router_id
):
self
.
router_name
=
router_name
self
.
router_id
=
router_id
def
__repr__
(
self
):
return
self
.
__str__
()
def
__str__
(
self
):
return
"
Router name {} deviceId {}
"
.
format
(
self
.
router_name
,
self
.
router_id
)
class
CSDTarget
(
object
):
def
__init__
(
self
,
router
,
interface
,
vlan
=
None
):
self
.
router
=
router
self
.
interface
=
interface
self
.
vlan
=
vlan
def
__str__
(
self
):
if
self
.
vlan
:
return
'
<CSDTarget %s/%s#%i>
'
%
(
self
.
router
,
self
.
interface
,
self
.
vlan
)
else
:
return
'
<CSDTarget %s/%s>
'
%
(
self
.
router
,
self
.
interface
)
def
createCSDPayload
(
connection_id
,
source_target
,
dest_target
,
service_def_id
,
cus_id
,
space_routers
,
csd_descriptions
):
intps
=
{
'
service_name
'
:
connection_id
,
'
router_a_name
'
:
source_target
.
router
,
'
router_a_id
'
:
space_routers
[
source_target
.
router
].
router_id
,
'
interface_a
'
:
source_target
.
interface
,
'
router_b_name
'
:
dest_target
.
router
,
'
router_b_id
'
:
space_routers
[
dest_target
.
router
].
router_id
,
'
interface_b
'
:
dest_target
.
interface
,
'
cus_key
'
:
cus_id
,
'
service_def_id
'
:
service_def_id
}
timestamp
=
str
(
time
.
time
()).
split
(
"
.
"
,
1
)[
0
]
intps
[
'
description
'
]
=
csd_descriptions
+
"
"
+
source_target
.
router
+
"
.
"
+
dest_target
.
router
+
"
.
"
+
timestamp
+
"
OpenNSA build
"
intps
[
'
vlan_a
'
]
=
source_target
.
vlan
intps
[
'
vlan_b
'
]
=
dest_target
.
vlan
payload
=
ORDER_PAYLOAD
%
intps
log
.
msg
(
"
Payload created:
\n
{}
"
.
format
(
payload
),
debug
=
True
,
system
=
LOG_SYSTEM
)
return
payload
def
_extractErrorMessage
(
failure
):
# used to extract error messages from http requests
if
isinstance
(
failure
.
value
,
WebError
):
return
failure
.
value
.
response
else
:
return
failure
.
getErrorMessage
()
class
WebClientContextFactory
(
ClientContextFactory
):
def
getContext
(
self
):
return
ClientContextFactory
.
getContext
(
self
)
class
CSDConnectionManager
:
def
__init__
(
self
,
port_map
,
space_user
,
space_password
,
space_api_url
,
space_routers
,
csd_service_def
,
csd_customer_id
,
network_name
,
csd_descriptions
):
self
.
network_name
=
network_name
self
.
port_map
=
port_map
self
.
space_user
=
space_user
self
.
space_password
=
space_password
self
.
space_api_url
=
space_api_url
self
.
space_routers
=
space_routers
self
.
csd_service_def
=
csd_service_def
self
.
csd_customer_id
=
csd_customer_id
self
.
csd_descriptions
=
csd_descriptions
def
getResource
(
self
,
port
,
label
):
assert
label
is
None
or
label
.
type_
==
cnt
.
ETHERNET_VLAN
,
'
Label must be None or VLAN
'
val
=
""
if
label
is
None
else
str
(
label
.
labelValue
())
return
port
+
'
:
'
+
val
# port contains router and port
def
getTarget
(
self
,
port
,
label
):
assert
label
is
None
or
label
.
type_
==
cnt
.
ETHERNET_VLAN
,
'
Label must be None or VLAN
'
if
label
is
not
None
and
label
.
type_
==
cnt
.
ETHERNET_VLAN
:
vlan
=
int
(
label
.
labelValue
())
assert
1
<=
vlan
<=
4095
,
'
Invalid label value for vlan: %s
'
%
label
.
labelValue
()
else
:
vlan
=
None
ri
=
self
.
port_map
[
port
]
router
,
interface
=
ri
.
split
(
'
:
'
)
return
CSDTarget
(
router
,
interface
,
vlan
)
def
createConnectionId
(
self
,
source_target
,
dest_target
):
return
'
ON-
'
+
str
(
random
.
randint
(
100000
,
999999
))
def
canSwapLabel
(
self
,
label_type
):
return
True
def
_createAuthzHeader
(
self
):
credentials
=
self
.
space_user
+
"
:
"
+
self
.
space_password
cr
=
base64
.
b64encode
(
credentials
.
encode
(
"
utf-8
"
))
cred
=
"
Basic
"
+
cr
.
decode
(
"
utf-8
"
)
return
cred
def
_createHeaders
(
self
):
headers
=
{}
#TODO> set propper data type -- done
headers
[
"
Content-Type
"
]
=
"
application/vnd.net.juniper.space.service-management.service-order+xml;version=2;charset=UTF-8
"
headers
[
"
Authorization
"
]
=
self
.
_createAuthzHeader
()
return
headers
def
setupLink
(
self
,
connection_id
,
source_target
,
dest_target
,
bandwidth
):
payload
=
createCSDPayload
(
connection_id
,
source_target
,
dest_target
,
self
.
csd_service_def
,
self
.
csd_customer_id
,
self
.
space_routers
,
self
.
csd_descriptions
)
headers
=
self
.
_createHeaders
()
contextFactory
=
WebClientContextFactory
()
def
linkUp
(
data
):
log
.
msg
(
'
Link %s -> %s up
'
%
(
source_target
,
dest_target
),
system
=
LOG_SYSTEM
)
log
.
msg
(
'
Response:
\n
%s
'
%
(
data
),
debug
=
True
,
system
=
LOG_SYSTEM
)
def
error
(
failure
):
log
.
msg
(
'
Error bringing up link %s -> %s
'
%
(
source_target
,
dest_target
),
system
=
LOG_SYSTEM
)
log
.
msg
(
'
Message: %s
'
%
_extractErrorMessage
(
failure
),
system
=
LOG_SYSTEM
)
return
failure
spaceurl
=
self
.
space_api_url
+
URI_CREATE_ORDER
d
=
httpclient
.
httpRequest
(
spaceurl
,
payload
.
encode
(),
headers
,
method
=
b
'
POST
'
,
timeout
=
CSD_TIMEOUT
,
ctx_factory
=
contextFactory
)
d
.
addCallbacks
(
linkUp
,
error
)
return
d
def
teardownLink
(
self
,
connection_id
,
source_target
,
dest_target
,
bandwidth
):
headers
=
{}
headers
[
"
Accept
"
]
=
"
*/*
"
headers
[
"
Authorization
"
]
=
self
.
_createAuthzHeader
()
serviceID
=
None
contextFactory
=
WebClientContextFactory
()
def
linkDown
(
data
):
log
.
msg
(
'
Link %s -> %s down
'
%
(
source_target
,
dest_target
),
system
=
LOG_SYSTEM
)
log
.
msg
(
'
Response:
\n
%s
'
%
(
data
),
debug
=
True
,
system
=
LOG_SYSTEM
)
def
error
(
failure
):
log
.
msg
(
'
Error bringing down link %s -> %s
'
%
(
source_target
,
dest_target
),
system
=
LOG_SYSTEM
)
log
.
msg
(
'
Message from Get Service ID: %s
'
%
_extractErrorMessage
(
failure
),
system
=
LOG_SYSTEM
)
return
failure
def
doServiceDelete
(
data
):
headers
=
{}
#headers["Content-Type"] = "application/vnd.net.juniper.space.service-management.service-order+xml;version=2;charset=UTF-8"
headers
[
"
Authorization
"
]
=
self
.
_createAuthzHeader
()
contextFactory
=
WebClientContextFactory
()
serviceID
=
0
nsmap
=
{
'
a
'
:
'
services.schema.networkapi.jmp.juniper.net
'
}
services
=
etree
.
fromstring
(
data
).
xpath
(
"
/a:Data/a:ServiceResource/a:Service
"
,
namespaces
=
nsmap
)
for
service
in
services
:
if
service
.
xpath
(
"
a:Common/a:Name
"
,
namespaces
=
nsmap
)[
0
].
text
==
connection_id
:
serviceID
=
service
.
xpath
(
"
a:Common/a:Identity
"
,
namespaces
=
nsmap
)[
0
].
text
if
serviceID
is
0
:
raise
Exception
(
"
Can
'
t find service ID for connection %s
"
%
connection_id
)
log
.
msg
(
'
Link %s -> %s Call for DELETE. Service ID: %s
'
%
(
source_target
,
dest_target
,
serviceID
),
system
=
LOG_SYSTEM
)
spaceurl
=
self
.
space_api_url
+
URI_DELETE_SERVICE
%
{
'
service_id
'
:
serviceID
}
d
=
httpclient
.
httpRequest
(
spaceurl
,
b
''
,
headers
,
method
=
b
'
DELETE
'
,
timeout
=
CSD_TIMEOUT
,
ctx_factory
=
contextFactory
)
return
d
def
errorSerDel
(
failure
):
log
.
msg
(
'
Error bringing down link %s -> %s
'
%
(
source_target
,
dest_target
),
system
=
LOG_SYSTEM
)
log
.
msg
(
'
Message from Service delete: %s
'
%
_extractErrorMessage
(
failure
),
system
=
LOG_SYSTEM
)
return
failure
spaceurl
=
self
.
space_api_url
+
URI_GET_SERVICES
# spaceurl = self.space_api_url + "api/space/nsas/eline-ptp/service-management/services/32440380"
res
=
httpclient
.
httpRequest
(
spaceurl
,
b
''
,
headers
,
method
=
b
'
GET
'
,
timeout
=
CSD_TIMEOUT
,
ctx_factory
=
contextFactory
)
res
.
addCallbacks
(
doServiceDelete
,
error
)
res
.
addCallbacks
(
linkDown
,
errorSerDel
)
#TODO: set up propper URL for delete application/vnd.net.juniper.space.service-management.service+xml
#d = httpclient.httpRequest(self.space_api_url, None, headers, method='DELETE', timeout=CSD_TIMEOUT)
#d.addCallbacks(linkDown, error)
return
res
def
JunosCSDBackend
(
network_name
,
nrm_ports
,
parent_requester
,
cfg
):
name
=
'
CSD %s
'
%
network_name
nrm_map
=
dict
(
[
(
p
.
name
,
p
)
for
p
in
nrm_ports
]
)
# for the generic backend
port_map
=
dict
(
[
(
p
.
name
,
p
.
interface
)
for
p
in
nrm_ports
]
)
# for the nrm backend
# extract config items
space_user
=
cfg
[
config
.
SPACE_USER
]
space_password
=
cfg
[
config
.
SPACE_PASSWORD
]
space_api_url
=
cfg
[
config
.
SPACE_API_URL
]
space_routers_config
=
cfg
[
config
.
SPACE_ROUTERS
].
split
()
csd_service_def
=
cfg
[
config
.
CSD_SERVICE_DEF
]
csd_customer_id
=
cfg
[
config
.
CSD_CUSTOMER_ID
]
csd_descriptions
=
cfg
.
get
(
config
.
JUNOS_DESCRIPTIONS
,
"
OpenNSA
"
)
space_routers
=
dict
()
log
.
msg
(
"
Loaded JunosCSD backend with routers:
"
)
for
g
in
space_routers_config
:
r
,
n
=
g
.
split
(
'
@
'
,
1
)
junosspace_router
=
JUNOSSPACERouter
(
r
,
n
)
log
.
msg
(
"
%s
"
%
(
junosspace_router
))
space_routers
[
r
]
=
junosspace_router
# csd_services_url = str(cfg[config.NCS_SERVICES_URL]) # convert from unicode
# user = cfg[config.NCS_USER]
# password = cfg[config.NCS_PASSWORD]
cm
=
CSDConnectionManager
(
port_map
,
space_user
,
space_password
,
space_api_url
,
space_routers
,
csd_service_def
,
csd_customer_id
,
network_name
,
csd_descriptions
)
return
genericbackend
.
GenericBackend
(
network_name
,
nrm_map
,
cm
,
parent_requester
,
name
)
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