Skip to content
Snippets Groups Projects
Commit f288a1df authored by Michal Hažlinský's avatar Michal Hažlinský :family:
Browse files

Merge branch 'master' of https://github.com/NORDUnet/opennsa into stable

parents f2914e4c 3756a5dd
Branches
No related tags found
No related merge requests found
Showing
with 1106 additions and 916 deletions
......@@ -2,6 +2,13 @@ How to run the the unit/integration tests for OpenNSA
Make sure all the requirements are installed. Then:
```sh
./util/pg-test-run # This will start a Postgres in docker
PYTHONPATH=. trial test
```
Running the CI/CD pipeline locally:
1. Install the CLI tooling according to: https://docs.drone.io/quickstart/cli/
2. Run the pipeline by using `drone exec`. Please ensure you have docker installed.
TLS/SSL Configuration
---------------------
# TLS/SSL Configuration
The configuration of TLS/SSL of OpenNSA is something that has confused several
people. This guide tries to make it more comprehensible. OpenNSA is somewhat
......@@ -18,7 +18,7 @@ When you have obtained a certificate you should have a private key and a
certificate file (also contains the public key).
** Configuration Options **
## Configuration Options
`tls=true`
Enable TLS.
......@@ -40,7 +40,7 @@ If OpenNSA should verify the peer. You want this to true, unless debugging..
Comma-seperated list of hosts that are allowed to make request to OpenNSA.
** Common Issues **
## Common Issues
If you get:
AttributeError: 'OpenSSL.SSL.Context' object has no attribute 'set_session_cache_mode'
......
POSTGRES_DB=opennsa
POSTGRES_USER=opennsa
POSTGRES_PASSWORD=PASSWD_REPLACE
POSTGRES_HOST=opennsa-db
POSTGRES_PORT=5432
TLS_ENABLED=false
NRM_FILE=config/opennsa.nrm
#!/bin/sh
cp docker/opennsa.conf.template config/opennsa.conf
sed -e "s/PASSWD_REPLACE/$(openssl rand -base64 18)/" env.template > .env
echo "Start OpenNSA with: docker-compose up"
......@@ -10,12 +10,11 @@ from twisted.internet import reactor, defer
from opennsa import nsa
from opennsa.cli import options, parser, commands, logobserver
CLI_TIMEOUT = 130 # The default 2-PC timeout for nsi is 120 seconds, so just add a bit to that
CLI_TIMEOUT = 130 # The default 2-PC timeout for nsi is 120 seconds, so just add a bit to that
CLI_DEFAULTS = '.opennsa-cli'
REQUESTER_URL_BASE = '{}://{}:{}/NSI/services/ConnectionService'
HELP_MESSAGE = '{}: Try --help or <command> --help for usage details.'
CLI_DEFAULTS = '.opennsa-cli'
REQUESTER_URL_BASE = '{}://{}:{}/NSI/services/ConnectionService'
HELP_MESSAGE = '{}: Try --help or <command> --help for usage details.'
def getHostname(dst_nsa):
......@@ -26,9 +25,9 @@ def getHostname(dst_nsa):
fqdn used for the destination we are trying to reach. The best way to do
that is to open a socket towards the destination and then request the fqdn.
"""
dsthost,dstport = dst_nsa.getHostPort()
dsthost, dstport = dst_nsa.getHostPort()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((dsthost,dstport))
s.connect((dsthost, dstport))
hostname = s.getsockname()[0]
s.close()
return hostname
......@@ -58,15 +57,15 @@ def doMain():
observer.dump_payload = True
# read defaults
defaults_file = config.subOptions[options.DEFAULTS_FILE] or os.path.join( os.path.expanduser('~'), CLI_DEFAULTS )
defaults_file = config.subOptions[options.DEFAULTS_FILE] or os.path.join(os.path.expanduser('~'), CLI_DEFAULTS)
if os.path.exists(defaults_file):
defaults = options.readDefaults( open(defaults_file) )
defaults = options.readDefaults(open(defaults_file))
else:
defaults = {}
log.msg('Defaults:', debug=True)
for k,v in defaults.items():
log.msg(' %s : %s' % (k,v), debug=True)
log.msg(f"Defaults from {defaults_file}:", debug=True)
for k, v in defaults.items():
log.msg(' %s : %s' % (k, v), debug=True)
iport = None
......@@ -74,49 +73,52 @@ def doMain():
# network commands, listener port created in this block
# note: we currently only have network commands, but they may change in the future
if config.subCommand in ['reserve', 'reserveonly', 'reservecommit', 'reserveprovision', 'rprt', 'provision', 'release', 'terminate', 'query', 'queryrec']:
if config.subCommand in ['reserve', 'reserveonly', 'reservecommit', 'reserveprovision', 'rprt', 'provision',
'release', 'terminate', 'query', 'queryrec']:
if options.NSA_SHORTHAND in defaults and config.subOptions[options.PROVIDER] in defaults[options.NSA_SHORTHAND]:
ns = defaults[options.NSA_SHORTHAND][config.subOptions[options.PROVIDER]]
provider_nsa = ns[0]
service_url = ns[1]
service_url = ns[1]
else:
provider_nsa = config.subOptions[options.PROVIDER] or defaults.get(options.PROVIDER)
service_url = config.subOptions[options.SERVICE_URL] or defaults.get(options.SERVICE_URL)
provider_nsa = config.subOptions[options.PROVIDER] or defaults.get(options.PROVIDER)
service_url = config.subOptions[options.SERVICE_URL] or defaults.get(options.SERVICE_URL)
requester_nsa = config.subOptions[options.REQUESTER] or defaults.get(options.REQUESTER) or 'OpenNSA-CLI'
requester_nsa = config.subOptions[options.REQUESTER] or defaults.get(options.REQUESTER) or 'OpenNSA-CLI'
authz_header = config.subOptions[options.AUTHZ_HEADER] or defaults.get(options.AUTHZ_HEADER)
authz_header = config.subOptions[options.AUTHZ_HEADER] or defaults.get(options.AUTHZ_HEADER)
connection_id = config.subOptions[options.CONNECTION_ID] or defaults.get(options.CONNECTION_ID)
global_id = config.subOptions[options.GLOBAL_ID] or defaults.get(options.GLOBAL_ID)
connection_id = config.subOptions[options.CONNECTION_ID] or defaults.get(options.CONNECTION_ID)
global_id = config.subOptions[options.GLOBAL_ID] or defaults.get(options.GLOBAL_ID)
# can only be specified on command line for now
security_attributes = [ nsa.SecurityAttribute(type_, value) for type_, value in config.subOptions[options.SECURITY_ATTRIBUTES] ]
security_attributes = [nsa.SecurityAttribute(type_, value) for type_, value in
config.subOptions[options.SECURITY_ATTRIBUTES]]
if service_url is None:
raise usage.UsageError('Service URL not specified')
if provider_nsa is None:
raise usage.UsageError('ProviderNSA not specified')
provider_nsa = nsa.NetworkServiceAgent(provider_nsa, service_url)
provider_nsa = nsa.NetworkServiceAgent(provider_nsa, service_url)
tls = config.subOptions[options.TLS] or defaults.get(options.TLS) or False
scheme = 'https' if tls else 'http'
host = config.subOptions[options.HOST] or defaults.get(options.HOST) or getHostname(provider_nsa)
port = config.subOptions[options.PORT] or defaults.get(options.PORT) or (7443 if tls else 7080)
tls = config.subOptions[options.TLS] or defaults.get(options.TLS) or False
scheme = 'https' if tls else 'http'
host = config.subOptions[options.HOST] or defaults.get(options.HOST) or getHostname(provider_nsa)
port = config.subOptions[options.PORT] or defaults.get(options.PORT) or (7443 if tls else 7080)
requester_url = REQUESTER_URL_BASE.format(scheme, host, port)
client_nsa = nsa.NetworkServiceAgent(requester_nsa, requester_url)
requester_url = REQUESTER_URL_BASE.format(scheme, host, port)
client_nsa = nsa.NetworkServiceAgent(requester_nsa, requester_url)
log.msg("Requester URL: %s" % requester_url, debug=True)
nsi_header = nsa.NSIHeader(client_nsa.urn(), provider_nsa.urn(), reply_to=provider_nsa.endpoint, security_attributes=security_attributes)
nsi_header = nsa.NSIHeader(client_nsa.urn(), provider_nsa.urn(), reply_to=provider_nsa.endpoint,
security_attributes=security_attributes)
# setup ssl context
public_key = config.subOptions[options.CERTIFICATE] or defaults.get(options.CERTIFICATE)
private_key = config.subOptions[options.KEY] or defaults.get(options.KEY)
certificate_dir = config.subOptions[options.CERTIFICATE_DIR] or defaults.get(options.CERTIFICATE_DIR)
public_key = config.subOptions[options.CERTIFICATE] or defaults.get(options.CERTIFICATE)
private_key = config.subOptions[options.KEY] or defaults.get(options.KEY)
certificate_dir = config.subOptions[options.CERTIFICATE_DIR] or defaults.get(options.CERTIFICATE_DIR)
# verify cert is a flag, if it is set, it means it should be skipped
if config.subOptions[options.NO_VERIFY_CERT]:
verify_cert = False
......@@ -155,15 +157,15 @@ def doMain():
if config.subCommand in ('reserve', 'reserveonly', 'reserveprovision', 'rprt'):
source_stp = config.subOptions[options.SOURCE_STP] or defaults.get(options.SOURCE_STP)
dest_stp = config.subOptions[options.DEST_STP] or defaults.get(options.DEST_STP)
source_stp = config.subOptions[options.SOURCE_STP] or defaults.get(options.SOURCE_STP)
dest_stp = config.subOptions[options.DEST_STP] or defaults.get(options.DEST_STP)
if source_stp is None:
raise usage.UsageError('Source STP is not defined')
if dest_stp is None:
raise usage.UsageError('Dest STP is not defined')
start_time = config.subOptions[options.START_TIME] or defaults.get(options.START_TIME)
end_time = config.subOptions[options.END_TIME] or defaults.get(options.END_TIME)
start_time = config.subOptions[options.START_TIME] or defaults.get(options.START_TIME)
end_time = config.subOptions[options.END_TIME] or defaults.get(options.END_TIME)
bandwidth = config.subOptions[options.BANDWIDTH] or defaults.get(options.BANDWIDTH)
if bandwidth is None:
......@@ -175,7 +177,8 @@ def doMain():
raise usage.UsageError('Connection ID is not defined')
from opennsa.protocols import nsi2
client, factory = nsi2.createRequester(host, port, service_url, tls=tls, ctx_factory=ctx_factory, authz_header=authz_header, callback_timeout=CLI_TIMEOUT)
client, factory = nsi2.createRequester(host, port, service_url, tls=tls, ctx_factory=ctx_factory,
authz_header=authz_header, callback_timeout=CLI_TIMEOUT)
# setup listener port
if tls:
......@@ -183,20 +186,23 @@ def doMain():
else:
iport = reactor.listenTCP(port, factory)
# start over on commands, now we do the actual dispatch
if config.subCommand == 'reserve':
yield commands.reserve(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero, connection_id, global_id)
yield commands.reserve(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero,
connection_id, global_id)
elif config.subCommand == 'reserveonly':
yield commands.reserveonly(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero, connection_id, global_id)
yield commands.reserveonly(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero,
connection_id, global_id)
elif config.subCommand == 'reserveprovision':
yield commands.reserveprovision(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero, connection_id, global_id, notification_wait)
yield commands.reserveprovision(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero,
connection_id, global_id, notification_wait)
elif config.subCommand == 'rprt':
yield commands.rprt(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero, connection_id, global_id)
yield commands.rprt(client, nsi_header, source_stp, dest_stp, start_time, end_time, bandwidth, ero,
connection_id, global_id)
elif config.subCommand == 'reservecommit':
yield commands.reservecommit(client, nsi_header, connection_id)
......@@ -211,27 +217,24 @@ def doMain():
yield commands.terminate(client, nsi_header, connection_id)
elif config.subCommand == 'query':
connection_ids = [ connection_id ] if connection_id else None
global_ids = [ global_id ] if global_id else None
connection_ids = [connection_id] if connection_id else None
global_ids = [global_id] if global_id else None
yield commands.querySummary(client, nsi_header, connection_ids, global_ids)
elif config.subCommand == 'queryrec':
connection_ids = [ connection_id ] if connection_id else None
global_ids = [ global_id ] if global_id else None
connection_ids = [connection_id] if connection_id else None
global_ids = [global_id] if global_id else None
yield commands.queryRecursive(client, nsi_header, connection_ids, global_ids)
else:
print('Invalid subcommand specified')
print('{}: Try --help for usage details.'.format(sys.argv[0]))
if iport:
yield iport.stopListening()
def main():
def slightlyDelayedShutdown(_):
# this means that the reactor/kernel will have a bit of time
# to push off any replies/acks before shutdown
......@@ -243,7 +246,7 @@ def main():
elif error.type == usage.UsageError:
log.msg("Usage error: " + error.getErrorMessage())
else:
#print "Error: %s" % error.value
# print "Error: %s" % error.value
log.err(error)
d = defer.maybeDeferred(doMain)
......@@ -255,4 +258,3 @@ def main():
if __name__ == '__main__':
reactor.callWhenRunning(main)
reactor.run()
# This is a configuration file for running an OpenNSA service directly from the development directory
[service]
# Change network name at will
domain=example.net
# This means we output log to stdout
logfile=
#peers=example.org@http://example.org:9080/NSI/topology/example.org.xml
# You will need to set these
database=opennsa
dbuser=opennsa
dbpassword=opennsa
tls=false
[dud:topology]
nrmmap=opennsa.nrm
......@@ -7,7 +7,10 @@
from opennsa import setup
from dotenv import load_dotenv
load_dotenv() ## Loads ENV values from .env file
# you can get debug and/or payload info in the log by setting one of the flags to true
application = setup.createApplication('opennsa.conf', payload=False, debug=False)
application = setup.createApplication('config/opennsa.conf', payload=False, debug=False)
This diff is collapsed.
......@@ -42,15 +42,15 @@ def http_query(conn, sub_path):
Mini Twisted Web Client
"""
full_url = conn.url + sub_path
full_url = full_url.encode('latin-1')
full_url = full_url.encode()
log.msg("http_query: %r" % full_url, debug=True, system=LOG_SYSTEM)
context_factory = WebClientContextFactory()
agent = Agent(reactor, context_factory)
d = agent.request('GET', full_url,
d = agent.request(b'GET', full_url,
headers=Headers(
{'Content-Type': ['application/x-www-form-urlencoded'],
'Authorization': ['Basic ' + conn.auth]
'Authorization': ['Basic ' + conn.auth.decode()]
}),
bodyProducer=None)
d.addCallbacks(readBody, log.err)
......@@ -111,6 +111,8 @@ def oess_confirm_vlan_availability(result, vlan):
raise Exception(err)
if result["results"][0]["available"] == 1:
return True
elif result["results"][0]["available"] == 0:
return True
raise Exception("Vlan %s not available" % vlan)
......@@ -234,7 +236,7 @@ class OessSetup(object):
self.workgroup = workgroup
self.workgroup_id = None
self.circuit_id = None
self.auth = b64encode(b"%s:%s" % (self.username, self.password))
self.auth = b64encode(("%s:%s" % (self.username, self.password)).encode())
self.conn = UrlConnection(self.url, self.auth)
@defer.inlineCallbacks
......@@ -386,19 +388,29 @@ class OESSConnectionManager:
return True
def setupLink(self, connection_id, source_target, dest_target, bandwidth):
def logSetupLink(pt, source_target, dest_target):
log.msg('Link %s -> %s up' % (source_target, dest_target),
system=self.log_system)
return pt
log.msg('OESS: setupLink', debug=True, system=self.log_system)
self.oess_conn.setupLink(source_target, dest_target)
log.msg('Link %s -> %s up' % (source_target, dest_target),
system=self.log_system)
return defer.succeed(None)
d = self.oess_conn.setupLink(source_target, dest_target)
d.addCallback(logSetupLink, source_target, dest_target)
return d
def teardownLink(self, connection_id, source_target, dest_target, bandwidth):
def logTearDownLink(pt, source_target, dest_target):
log.msg('Link %s -> %s down' % (source_target, dest_target),
system=self.log_system)
return pt
# Debug
log.msg('OESS: teardownLink', system=self.log_system)
self.oess_conn.tearDownLink(source_target, dest_target)
log.msg('Link %s -> %s down' % (source_target, dest_target),
system=self.log_system)
return defer.succeed(None)
d = self.oess_conn.tearDownLink(source_target, dest_target)
d.addCallback(logTearDownLink, source_target, dest_target)
return d
# ********************************************************************************
......
......@@ -6,25 +6,25 @@ from twisted.internet import defer
from opennsa import constants as cnt, nsa, error
LABEL_MAP = {
'vlan' : cnt.ETHERNET_VLAN,
'mpls' : cnt.MPLS
'vlan': cnt.ETHERNET_VLAN,
'mpls': cnt.MPLS
}
def _createSTP(stp_arg):
if not ':' in stp_arg:
raise usage.UsageError('No ":" in stp, invalid format (see docs/cli.md)')
if '#' in stp_arg:
stp_desc, label_desc = stp_arg.split('#')
network, port = stp_desc.rsplit(':',1)
network, port = stp_desc.rsplit(':', 1)
if not '=' in label_desc:
raise usage.UsageError('No "=" in stp label, invalid format (see docs/cli.md)')
label_type,label_value = label_desc.split("=")
label = nsa.Label(LABEL_MAP[label_type],label_value) # FIXME need good error message if label type doesn't exist
label_type, label_value = label_desc.split("=")
label = nsa.Label(LABEL_MAP[label_type],
label_value) # FIXME need good error message if label type doesn't exist
else:
network, port = stp_arg.rsplit(':',1)
network, port = stp_arg.rsplit(':', 1)
label = None
return nsa.STP(network, port, label)
......@@ -37,12 +37,11 @@ def _createSTPList(ero):
if ero is None:
return None
ero_stps = [ _createSTP(stp_spec.strip()) for stp_spec in ero.split(',') ]
ero_stps = [_createSTP(stp_spec.strip()) for stp_spec in ero.split(',')]
return ero_stps
def _createP2PS(src, dst, capacity, ero):
src_stp = _createSTP(src)
dst_stp = _createSTP(dst)
ordered_stp = _createSTPList(ero)
......@@ -51,7 +50,6 @@ def _createP2PS(src, dst, capacity, ero):
def _handleEvent(event):
notification_type, header, entry = event
if notification_type == 'errorEvent':
......@@ -65,7 +63,7 @@ def _handleEvent(event):
return False
else:
log.msg('Connection %s Data plane down, version %i, consistent: %s' % (cid, version, consistent))
return consistent # this means we don't exit on initial partially down, where we are not consistent
return consistent # this means we don't exit on initial partially down, where we are not consistent
else:
log.msg('Unrecognized event %s ' % notification_type)
......@@ -80,13 +78,11 @@ def _logError(e):
log.msg('%s from %s' % (error_type, e.nsaId))
log.msg(' %s' % e)
if e.variables:
log.msg('Variables: %s' % ' '.join ( [ ': '.join(tvp) for tvp in e.variables ] ) )
log.msg('Variables: %s' % ' '.join([': '.join(tvp) for tvp in e.variables]))
@defer.inlineCallbacks
def discover(client, service_url):
res = yield client.queryNSA(service_url)
print("-- COMMAND RESULT --")
print(res)
......@@ -95,14 +91,14 @@ def discover(client, service_url):
@defer.inlineCallbacks
def reserveonly(client, nsi_header, src, dst, start_time, end_time, capacity, ero, connection_id, global_id):
schedule = nsa.Schedule(start_time, end_time)
service_def = _createP2PS(src, dst, capacity, ero)
crt = nsa.Criteria(0, schedule, service_def)
try:
nsi_header.connection_trace = [ nsi_header.requester_nsa + ':' + '1' ]
connection_id, _,_,criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection', crt)
nsi_header.connection_trace = [nsi_header.requester_nsa + ':' + '1']
connection_id, _, _, criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection',
crt)
nsi_header.connection_trace = None
sd = criteria.service_def
log.msg("Connection created and held. Id %s at %s" % (connection_id, nsi_header.provider_nsa))
......@@ -114,14 +110,15 @@ def reserveonly(client, nsi_header, src, dst, start_time, end_time, capacity, er
@defer.inlineCallbacks
def reserve(client, nsi_header, src, dst, start_time, end_time, capacity, ero, connection_id, global_id):
schedule = nsa.Schedule(start_time, end_time)
service_def = _createP2PS(src, dst, capacity, ero)
crt = nsa.Criteria(0, schedule, service_def)
try:
nsi_header.connection_trace = [ nsi_header.requester_nsa + ':' + '1' ]
connection_id, global_reservation_id, description, criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection', crt)
nsi_header.connection_trace = [nsi_header.requester_nsa + ':' + '1']
connection_id, global_reservation_id, description, criteria = yield client.reserve(nsi_header, connection_id,
global_id, 'Test Connection',
crt)
nsi_header.connection_trace = None
sd = criteria.service_def
log.msg("Connection created and held. Id %s at %s" % (connection_id, nsi_header.provider_nsa))
......@@ -136,15 +133,16 @@ def reserve(client, nsi_header, src, dst, start_time, end_time, capacity, ero, c
@defer.inlineCallbacks
def reserveprovision(client, nsi_header, src, dst, start_time, end_time, capacity, ero, connection_id, global_id, notification_wait):
def reserveprovision(client, nsi_header, src, dst, start_time, end_time, capacity, ero, connection_id, global_id,
notification_wait):
schedule = nsa.Schedule(start_time, end_time)
service_def = _createP2PS(src, dst, capacity, ero)
crt = nsa.Criteria(0, schedule, service_def)
try:
nsi_header.connection_trace = [ nsi_header.requester_nsa + ':' + '1' ]
connection_id, _,_, criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection', crt)
nsi_header.connection_trace = [nsi_header.requester_nsa + ':' + '1']
connection_id, _, _, criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection',
crt)
nsi_header.connection_trace = []
sd = criteria.service_def
log.msg("Connection created and held. Id %s at %s" % (connection_id, nsi_header.provider_nsa))
......@@ -156,7 +154,7 @@ def reserveprovision(client, nsi_header, src, dst, start_time, end_time, capacit
# query
nsi_header.newCorrelationId()
qr = yield client.querySummary(nsi_header, connection_ids=[connection_id] )
qr = yield client.querySummary(nsi_header, connection_ids=[connection_id])
print('Query result: {}'.format(qr))
# provision
......@@ -174,7 +172,6 @@ def reserveprovision(client, nsi_header, src, dst, start_time, end_time, capacit
_logError(e)
@defer.inlineCallbacks
def rprt(client, nsi_header, src, dst, start_time, end_time, capacity, ero, connection_id, global_id):
# reserve, provision, release, terminate
......@@ -183,8 +180,9 @@ def rprt(client, nsi_header, src, dst, start_time, end_time, capacity, ero, conn
crt = nsa.Criteria(0, schedule, service_def)
try:
nsi_header.connection_trace = [ nsi_header.requester_nsa + ':' + '1' ]
connection_id, _,_, criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection', crt)
nsi_header.connection_trace = [nsi_header.requester_nsa + ':' + '1']
connection_id, _, _, criteria = yield client.reserve(nsi_header, connection_id, global_id, 'Test Connection',
crt)
nsi_header.connection_trace = []
sd = criteria.service_def
log.msg("Connection created and held. Id %s at %s" % (connection_id, nsi_header.provider_nsa))
......@@ -216,7 +214,6 @@ def rprt(client, nsi_header, src, dst, start_time, end_time, capacity, ero, conn
@defer.inlineCallbacks
def reservecommit(client, nsi_header, connection_id):
try:
yield client.reserveCommit(nsi_header, connection_id)
log.msg("Reservation committed at %s" % nsi_header.provider_nsa)
......@@ -227,7 +224,6 @@ def reservecommit(client, nsi_header, connection_id):
@defer.inlineCallbacks
def provision(client, nsi_header, connection_id, notification_wait):
try:
yield client.provision(nsi_header, connection_id)
log.msg('Connection %s provisioned' % connection_id)
......@@ -240,7 +236,6 @@ def provision(client, nsi_header, connection_id, notification_wait):
@defer.inlineCallbacks
def release(client, nsi_header, connection_id, notification_wait):
try:
yield client.release(nsi_header, connection_id)
log.msg('Connection %s released' % connection_id)
......@@ -253,7 +248,6 @@ def release(client, nsi_header, connection_id, notification_wait):
@defer.inlineCallbacks
def terminate(client, nsi_header, connection_id):
try:
yield client.terminate(nsi_header, connection_id)
log.msg('Connection %s terminated' % connection_id)
......@@ -261,14 +255,11 @@ def terminate(client, nsi_header, connection_id):
_logError(e)
def _emitQueryResult(query_result, i='', child=False):
qr = query_result
log.msg('')
log.msg(i + 'Connection %s (%s)' % (qr.connection_id, qr.provider_nsa) )
log.msg(i + 'Connection %s (%s)' % (qr.connection_id, qr.provider_nsa))
if qr.global_reservation_id:
log.msg(i + 'Global ID %s' % qr.global_reservation_id)
if qr.description:
......@@ -285,13 +276,13 @@ def _emitQueryResult(query_result, i='', child=False):
log.msg(i + 'Start-End %s - %s' % (crit.schedule.start_time, crit.schedule.end_time))
if type(crit.service_def) is nsa.Point2PointService:
sd = crit.service_def
#log.msg(i + 'Source : %s' % sd.source_stp.shortName())
#log.msg(i + 'Destination : %s' % sd.dest_stp.shortName())
log.msg(i + 'Path %s -- %s' % (sd.source_stp.shortName(), sd.dest_stp.shortName()) )
if not child: # these should be the same everywhere
# log.msg(i + 'Source : %s' % sd.source_stp.shortName())
# log.msg(i + 'Destination : %s' % sd.dest_stp.shortName())
log.msg(i + 'Path %s -- %s' % (sd.source_stp.shortName(), sd.dest_stp.shortName()))
if not child: # these should be the same everywhere
log.msg(i + 'Bandwidth %s' % sd.capacity)
log.msg(i + 'Direction %s' % sd.directionality)
if sd.symmetric: # only show symmetric if set
if sd.symmetric: # only show symmetric if set
log.msg(i + 'Symmetric %s' % sd.symmetric)
if sd.parameters:
log.msg(i + 'Params %s' % sd.parameters)
......@@ -302,11 +293,8 @@ def _emitQueryResult(query_result, i='', child=False):
_emitQueryResult(c, i + ' ', True)
@defer.inlineCallbacks
def querySummary(client, nsi_header, connection_ids, global_reservation_ids):
try:
qc = yield client.querySummary(nsi_header, connection_ids, global_reservation_ids)
if not qc:
......@@ -324,7 +312,6 @@ def querySummary(client, nsi_header, connection_ids, global_reservation_ids):
@defer.inlineCallbacks
def queryRecursive(client, nsi_header, connection_ids, global_reservation_ids):
try:
qc = yield client.queryRecursive(nsi_header, connection_ids, global_reservation_ids)
if not qc:
......@@ -338,4 +325,3 @@ def queryRecursive(client, nsi_header, connection_ids, global_reservation_ids):
except error.NSIError as e:
_logError(e)
......@@ -10,48 +10,45 @@ from twisted.python import log
from opennsa import config
from opennsa.shared.xmlhelper import UTC
# option names, as constants so we don't use strings in other modules
VERBOSE = 'verbose'
DEFAULTS_FILE = 'defaults-file'
DUMP_PAYLOAD = 'dump-payload'
HOST = 'host'
PORT = 'port'
TOPOLOGY_FILE = 'topology'
NETWORK = 'network'
SERVICE_URL = 'service'
AUTHZ_HEADER = 'authzheader'
REQUESTER = 'requester'
PROVIDER = 'provider'
VERBOSE = 'verbose'
DEFAULTS_FILE = 'defaults-file'
DUMP_PAYLOAD = 'dump-payload'
HOST = 'host'
PORT = 'port'
TOPOLOGY_FILE = 'topology'
NETWORK = 'network'
SERVICE_URL = 'service'
AUTHZ_HEADER = 'authzheader'
REQUESTER = 'requester'
PROVIDER = 'provider'
SECURITY_ATTRIBUTES = 'securityattributes'
CONNECTION_ID = 'connection-id'
GLOBAL_ID = 'global-id'
CONNECTION_ID = 'connection-id'
GLOBAL_ID = 'global-id'
SOURCE_STP = 'source'
DEST_STP = 'dest'
BANDWIDTH = 'bandwidth'
START_TIME = 'starttime'
END_TIME = 'endtime'
ERO = 'ero'
SOURCE_STP = 'source'
DEST_STP = 'dest'
BANDWIDTH = 'bandwidth'
START_TIME = 'starttime'
END_TIME = 'endtime'
ERO = 'ero'
TLS = config.TLS
KEY = config.KEY
CERTIFICATE = config.CERTIFICATE
TLS = config.TLS
KEY = config.KEY
CERTIFICATE = config.CERTIFICATE
CERTIFICATE_DIR = config.CERTIFICATE_DIR
NO_VERIFY_CERT = 'no-verify'
NO_VERIFY_CERT = 'no-verify'
NOTIFICATION_WAIT = 'notification_wait'
# other constants
XSD_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
NSA_SHORTHAND = 'nsa'
NSA_SHORTHAND = 'nsa'
def parseTimestamp(value):
if value.startswith('+'):
offset = int(value[1:])
ts = datetime.datetime.fromtimestamp(time.time() + offset, UTC()).replace(tzinfo=None)
......@@ -61,9 +58,7 @@ def parseTimestamp(value):
return ts
def readDefaults(file_):
defaults = {}
for line in file_.readlines():
......@@ -72,13 +67,13 @@ def readDefaults(file_):
line = line.strip()
if not line or line.startswith('#'):
continue # skip comment
continue # skip comment
option, value = line.split('=',2)
option, value = line.split('=', 2)
# nsa shorthand, this one is a bit special so we do it first, and continue on match
if option == NSA_SHORTHAND:
shorthand, nsa_id, service_url = value.split(',',3)
shorthand, nsa_id, service_url = value.split(',', 3)
defaults.setdefault(option, {})[shorthand] = (nsa_id, service_url)
continue
......@@ -89,7 +84,7 @@ def readDefaults(file_):
if option in (PORT, BANDWIDTH):
value = int(value)
if option in (TLS,NO_VERIFY_CERT): # flags
if option in (TLS, NO_VERIFY_CERT): # flags
value = False if value.lower() in ('false', 'no', '0') else True
defaults[option] = value
......@@ -99,4 +94,3 @@ def readDefaults(file_):
log.msg('Error parsing line in CLI defaults file. Line: %s. Error: %s' % (line, str(e)))
return defaults
......@@ -55,111 +55,139 @@ from opennsa.cli import options
# parameters used for all commands
class DefaultsFileOption(usage.Options):
optParameters = [ [ options.DEFAULTS_FILE, 'f', None, 'Defaults file'] ]
optParameters = [[options.DEFAULTS_FILE, 'f', None, 'Defaults file']]
class HostOption(usage.Options):
optParameters = [ [ options.HOST, 'h', None, 'Host (for callback)'] ]
optParameters = [[options.HOST, 'h', None, 'Host (for callback)']]
class PortOption(usage.Options):
optParameters = [ [ options.PORT, 'o', None, 'Port (for callback)', int] ]
optParameters = [[options.PORT, 'o', None, 'Port (for callback)', int]]
# parameters which are only used for some commands
class ServiceURLOption(usage.Options):
optParameters = [ [ options.SERVICE_URL, 'u', None, 'Service URL'] ]
optParameters = [[options.SERVICE_URL, 'u', None, 'Service URL']]
class AuthzHeaderOption(usage.Options):
optParameters = [ [ options.AUTHZ_HEADER, 'm', None, 'Authorization header'] ]
optParameters = [[options.AUTHZ_HEADER, 'm', None, 'Authorization header']]
class ProviderNSAOption(usage.Options):
optParameters = [ [ options.PROVIDER, 'p', None, 'Provider NSA Identity'] ]
optParameters = [[options.PROVIDER, 'p', None, 'Provider NSA Identity']]
class RequesterNSAOption(usage.Options):
optParameters = [ [ options.REQUESTER, 'r', None, 'Requester NSA Identity'] ]
optParameters = [[options.REQUESTER, 'r', None, 'Requester NSA Identity']]
class SourceSTPOption(usage.Options):
optParameters = [ [ options.SOURCE_STP, 's', None, 'Source STP'] ]
optParameters = [[options.SOURCE_STP, 's', None, 'Source STP']]
class DestSTPOption(usage.Options):
optParameters = [ [ options.DEST_STP, 'd', None, 'Dest STP'] ]
optParameters = [[options.DEST_STP, 'd', None, 'Dest STP']]
class ConnectionIDOption(usage.Options):
optParameters = [ [ options.CONNECTION_ID, 'c', None, 'Connection id'] ]
optParameters = [[options.CONNECTION_ID, 'c', None, 'Connection id']]
class GlobalIDOption(usage.Options):
optParameters = [ [ options.GLOBAL_ID, 'g', None, 'Global id'] ]
optParameters = [[options.GLOBAL_ID, 'g', None, 'Global id']]
class StartTimeOption(usage.Options):
optParameters = [ [ options.START_TIME, 'a', None, 'Start time (UTC time)'] ]
optParameters = [[options.START_TIME, 'a', None, 'Start time (UTC time)']]
def postOptions(self):
if self[options.START_TIME] is not None:
self[options.START_TIME] = datetime.datetime.strptime(self[options.START_TIME], options.XSD_DATETIME_FORMAT) #.replace(tzinfo=None)
self[options.START_TIME] = datetime.datetime.strptime(self[options.START_TIME],
options.XSD_DATETIME_FORMAT) # .replace(tzinfo=None)
class EndTimeOption(usage.Options):
optParameters = [ [ options.END_TIME, 'e', None, 'End time (UTC time)'] ]
optParameters = [[options.END_TIME, 'e', None, 'End time (UTC time)']]
def postOptions(self):
if self[options.END_TIME] is not None:
self[options.END_TIME] = datetime.datetime.strptime(self[options.END_TIME], options.XSD_DATETIME_FORMAT) # .replace(tzinfo=None)
self[options.END_TIME] = datetime.datetime.strptime(self[options.END_TIME],
options.XSD_DATETIME_FORMAT) # .replace(tzinfo=None)
class SecurityAttributeOptions(usage.Options):
optParameters = [ [ options.SECURITY_ATTRIBUTES, 'j', None, 'Security attributes (format attr1=value1,attr2=value2)'] ]
optParameters = [[options.SECURITY_ATTRIBUTES, 'j', None, 'Security attributes (format attr1=value1,attr2=value2)']]
def postOptions(self):
sats = []
if self[options.SECURITY_ATTRIBUTES]:
for kv_split in self[options.SECURITY_ATTRIBUTES].split(','):
if not '=' in kv_split:
raise usage.UsageError('No = in key-value attribute %s' % kv_split)
key, value = kv_split.split('=',1)
sats.append( (key, value) )
key, value = kv_split.split('=', 1)
sats.append((key, value))
self[options.SECURITY_ATTRIBUTES] = sats
class BandwidthOption(usage.Options):
optParameters = [ [ options.BANDWIDTH, 'b', None, 'Bandwidth (Megabits)'] ]
optParameters = [[options.BANDWIDTH, 'b', None, 'Bandwidth (Megabits)']]
class EroOption(usage.Options):
optParameters = [ [ options.ERO, '0', None, 'ERO list'] ]
optParameters = [[options.ERO, '0', None, 'ERO list']]
class PublicKeyOption(usage.Options):
optParameters = [ [ options.CERTIFICATE, 'l', None, 'Certificate path' ] ]
optParameters = [[options.CERTIFICATE, 'l', None, 'Certificate path']]
class PrivateKeyOption(usage.Options):
optParameters = [ [ options.KEY, 'k', None, 'Private key path' ] ]
optParameters = [[options.KEY, 'k', None, 'Private key path']]
class CertificateDirectoryOption(usage.Options):
optParameters = [ [ options.CERTIFICATE_DIR, 'i', None, 'Certificate directory' ] ]
optParameters = [[options.CERTIFICATE_DIR, 'i', None, 'Certificate directory']]
# flags
class NotificationWaitFlag(usage.Options):
optFlags = [ [ options.NOTIFICATION_WAIT, 'y', 'Wait for notifications, exists on data plane deactive and errorEvent' ] ]
optFlags = [
[options.NOTIFICATION_WAIT, 'y', 'Wait for notifications, exists on data plane deactive and errorEvent']]
class TLSFlag(usage.Options):
optFlags = [ [ options.TLS, 'x', 'Use TLS for listener port' ] ]
optFlags = [[options.TLS, 'x', 'Use TLS for listener port']]
class SkipCertificateVerificationFlag(usage.Options):
optFlags = [ [ options.NO_VERIFY_CERT, 'z', 'Skip certificate verification' ] ]
optFlags = [[options.NO_VERIFY_CERT, 'z', 'Skip certificate verification']]
# command options
class BaseOptions(DefaultsFileOption):
optFlags = [
[ options.VERBOSE, 'v', 'Print out more information'],
[ options.DUMP_PAYLOAD, 'q', 'Dump message payloads'],
[options.VERBOSE, 'v', 'Print out more information'],
[options.DUMP_PAYLOAD, 'q', 'Dump message payloads'],
]
class NetworkBaseOptions(BaseOptions, HostOption, PortOption,
ServiceURLOption, AuthzHeaderOption, SecurityAttributeOptions,
TLSFlag, PublicKeyOption, PrivateKeyOption, CertificateDirectoryOption, SkipCertificateVerificationFlag):
TLSFlag, PublicKeyOption, PrivateKeyOption, CertificateDirectoryOption,
SkipCertificateVerificationFlag):
def postOptions(self):
# technically we should do this for all superclasses, but these are the only ones that has anything to do
SecurityAttributeOptions.postOptions(self)
class NetworkCommandOptions(NetworkBaseOptions, ProviderNSAOption, RequesterNSAOption, ConnectionIDOption, GlobalIDOption):
class NetworkCommandOptions(NetworkBaseOptions, ProviderNSAOption, RequesterNSAOption, ConnectionIDOption,
GlobalIDOption):
pass
......@@ -167,7 +195,8 @@ class ProvisionOptions(NetworkCommandOptions, NotificationWaitFlag):
pass
class ReserveOptions(NetworkCommandOptions, SourceSTPOption, DestSTPOption, StartTimeOption, EndTimeOption, BandwidthOption, EroOption):
class ReserveOptions(NetworkCommandOptions, SourceSTPOption, DestSTPOption, StartTimeOption, EndTimeOption,
BandwidthOption, EroOption):
def postOptions(self):
NetworkCommandOptions.postOptions(self)
......@@ -185,27 +214,24 @@ class ProvisionReleaseTerminateOptions(NetworkCommandOptions):
class Options(usage.Options):
subCommands = [
['reserve', None, ReserveOptions, 'Create and commit a reservation.'],
['reserveonly', None, ReserveOptions, 'Create a reservation without comitting it.'],
['reservecommit', None, ProvisionOptions, 'Commit a held reservation.'],
['reserveprovision',None, ReserveProvisionOptions,'Create a reservation and provision the connection.'],
['rprt', None, ReserveOptions, 'Create a reservation and provision, release and terminate the connection.'],
['provision', None, ProvisionOptions, 'Provision a connection.'],
['release', None, ProvisionOptions, 'Release a connection.'],
['terminate', None, NetworkCommandOptions, 'Terminate a connection.'],
['query', None, NetworkCommandOptions, 'Query a connection (provider summary).'],
['queryrec', None, NetworkCommandOptions, 'Query a connection (recursive).']
['reserve', None, ReserveOptions, 'Create and commit a reservation.'],
['reserveonly', None, ReserveOptions, 'Create a reservation without comitting it.'],
['reservecommit', None, ProvisionOptions, 'Commit a held reservation.'],
['reserveprovision', None, ReserveProvisionOptions, 'Create a reservation and provision the connection.'],
['rprt', None, ReserveOptions, 'Create a reservation and provision, release and terminate the connection.'],
['provision', None, ProvisionOptions, 'Provision a connection.'],
['release', None, ProvisionOptions, 'Release a connection.'],
['terminate', None, NetworkCommandOptions, 'Terminate a connection.'],
['query', None, NetworkCommandOptions, 'Query a connection (provider summary).'],
['queryrec', None, NetworkCommandOptions, 'Query a connection (recursive).']
]
def postOptions(self):
if self.subCommand is None:
return usage.UsageError('No option specified')
def opt_version(self):
from opennsa import __version__
from twisted import copyright
print("OpenNSA version %s. Running on Twisted version %s." % (__version__, copyright.version))
raise SystemExit
This diff is collapsed.
......@@ -44,11 +44,14 @@ class opennsaTlsContext:
try:
CACertificates.append(ssl.Certificate.loadPEM(CAFileContent))
except crypto.Error as error:
log.msg('Cannot load CA certificate from %s: %s' % (CAFilename, error), system = LOG_SYSTEM)
log.msg(f'Cannot load CA certificate from {CAFilename}: {error}', system=LOG_SYSTEM)
else:
log.msg('Loaded CA certificate commonName %s' % (str(CACertificates[-1].getSubject().commonName)), system = LOG_SYSTEM)
try:
log.msg(f'Loaded CA certificate {CACertificates[-1].getSubject()}', system=LOG_SYSTEM)
except:
log.msg("Failed to serialize Certificate Subject")
if len(CACertificates) == 0:
print('No certificiates loaded for CTX verificiation. CA verification will not work.')
print('No certificates loaded for CTX verification. CA verification will not work.')
return ssl.trustRootFromCertificates(CACertificates)
def getTrustRoot(self):
......
......@@ -209,7 +209,7 @@ class Provider:
return defer.succeed(None)
if (header.correlation_id, QUERY_SUMMARY_RESPONSE) in self.notifications:
dc = self.notifications.pop( (header.correlation_id, QUERY_SUMMARY_RESPONSE) )
dc = self.notifications.pop((header.correlation_id, QUERY_SUMMARY_RESPONSE))
dc.callback( reservations )
else:
return self.provider_client.querySummaryConfirmed(header.reply_to, header.requester_nsa, header.provider_nsa, header.correlation_id, reservations)
......
......@@ -21,9 +21,7 @@ from opennsa.protocols.shared import minisoap, httpclient
from opennsa.protocols.nsi2 import helper, queryhelper
from opennsa.protocols.nsi2.bindings import actions, nsiconnection, p2pservices
LOG_SYSTEM = 'nsi2.RequesterClient'
LOG_SYSTEM = 'nsi2.RequesterClient'
@implementer(INSIProvider)
......@@ -35,19 +33,17 @@ class RequesterClient:
assert type(reply_to) is str, 'Reply to URL must be of type str'
self.service_url = service_url.encode()
self.reply_to = reply_to
self.reply_to = reply_to
self.ctx_factory = ctx_factory
self.http_headers = {}
if authz_header:
self.http_headers['Authorization'] = authz_header
def _checkHeader(self, header):
if header.reply_to and header.correlation_id is None:
raise AssertionError('Header must specify correlation id, if reply to is specified')
def _createGenericRequestType(self, body_element_name, header, connection_id):
header_element = helper.convertProviderHeader(header, self.reply_to)
......@@ -56,8 +52,6 @@ class RequesterClient:
payload = minisoap.createSoapPayload(body_element, header_element)
return payload
def _handleErrorReply(self, err, header):
if err.check(WebError) is None:
......@@ -90,7 +84,6 @@ class RequesterClient:
return err
def reserve(self, header, connection_id, global_reservation_id, description, criteria, request_info=None):
# request_info is local only, so it isn't used
......@@ -117,14 +110,16 @@ class RequesterClient:
if not type(sd) is nsa.Point2PointService:
raise ValueError('Cannot create request for service definition of type %s' % str(type(sd)))
params = [ p2pservices.TypeValueType(p[0], p[1]) for p in sd.parameters ] if sd.parameters else None
service_def = p2pservices.P2PServiceBaseType(sd.capacity, sd.directionality, sd.symmetric, sd.source_stp.urn(), sd.dest_stp.urn(), sd.ero, params)
params = [p2pservices.TypeValueType(p[0], p[1]) for p in sd.parameters] if sd.parameters else None
service_def = p2pservices.P2PServiceBaseType(sd.capacity, sd.directionality, sd.symmetric, sd.source_stp.urn(),
sd.dest_stp.urn(), sd.ero, params)
schedule_type = nsiconnection.ScheduleType(start_time, end_time)
#service_type = str(p2pservices.p2ps)
# service_type = str(p2pservices.p2ps)
service_type = 'http://services.ogf.org/nsi/2013/12/descriptions/EVTS.A-GOLE'
criteria = nsiconnection.ReservationRequestCriteriaType(criteria.revision, schedule_type, service_type, service_def)
criteria = nsiconnection.ReservationRequestCriteriaType(criteria.revision, schedule_type, service_type,
service_def)
reservation = nsiconnection.ReserveType(connection_id, global_reservation_id, description, criteria)
......@@ -135,65 +130,65 @@ class RequesterClient:
header, ack = helper.parseRequest(soap_data)
return ack.connectionId
d = httpclient.soapRequest(self.service_url, actions.RESERVE, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d = httpclient.soapRequest(self.service_url, actions.RESERVE, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(_handleAck, self._handleErrorReply, errbackArgs=(header,))
return d
def reserveCommit(self, header, connection_id, request_info=None):
self._checkHeader(header)
payload = self._createGenericRequestType(nsiconnection.reserveCommit, header, connection_id)
d = httpclient.soapRequest(self.service_url, actions.RESERVE_COMMIT, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.RESERVE_COMMIT, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
def reserveAbort(self, header, connection_id, request_info=None):
self._checkHeader(header)
payload = self._createGenericRequestType(nsiconnection.reserveAbort, header, connection_id)
d = httpclient.soapRequest(self.service_url, actions.RESERVE_ABORT, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.RESERVE_ABORT, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
def provision(self, header, connection_id, request_info=None):
self._checkHeader(header)
payload = self._createGenericRequestType(nsiconnection.provision, header, connection_id)
d = httpclient.soapRequest(self.service_url, actions.PROVISION, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.PROVISION, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
def release(self, header, connection_id, request_info=None):
self._checkHeader(header)
payload = self._createGenericRequestType(nsiconnection.release, header, connection_id)
d = httpclient.soapRequest(self.service_url, actions.RELEASE, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.RELEASE, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
def terminate(self, header, connection_id, request_info=None):
self._checkHeader(header)
payload = self._createGenericRequestType(nsiconnection.terminate, header, connection_id)
d = httpclient.soapRequest(self.service_url, actions.TERMINATE, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.TERMINATE, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
def querySummary(self, header, connection_ids=None, global_reservation_ids=None, request_info=None):
self._checkHeader(header)
......@@ -205,16 +200,16 @@ class RequesterClient:
payload = minisoap.createSoapPayload(body_element, header_element)
d = httpclient.soapRequest(self.service_url, actions.QUERY_SUMMARY, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.QUERY_SUMMARY, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
def querySummarySync(self, header, connection_ids=None, global_reservation_ids=None, request_info=None):
def gotReply(soap_data):
header, query_confirmed = helper.parseRequest(soap_data)
return [ queryhelper.buildQueryResult(resv, header.provider_nsa) for resv in query_confirmed.reservations ]
return [queryhelper.buildQueryResult(resv, header.provider_nsa) for resv in query_confirmed.reservations]
# don't need to check header here
header_element = helper.convertProviderHeader(header, self.reply_to)
......@@ -224,11 +219,11 @@ class RequesterClient:
payload = minisoap.createSoapPayload(body_element, header_element)
d = httpclient.soapRequest(self.service_url, actions.QUERY_SUMMARY_SYNC, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d = httpclient.soapRequest(self.service_url, actions.QUERY_SUMMARY_SYNC, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(gotReply, self._handleErrorReply, errbackArgs=(header,))
return d
def queryRecursive(self, header, connection_ids, global_reservation_ids=None, request_info=None):
self._checkHeader(header)
......@@ -240,7 +235,7 @@ class RequesterClient:
payload = minisoap.createSoapPayload(body_element, header_element)
d = httpclient.soapRequest(self.service_url, actions.QUERY_RECURSIVE, payload, ctx_factory=self.ctx_factory, headers=self.http_headers)
d.addCallbacks(lambda sd : None, self._handleErrorReply, errbackArgs=(header,))
d = httpclient.soapRequest(self.service_url, actions.QUERY_RECURSIVE, payload, ctx_factory=self.ctx_factory,
headers=self.http_headers)
d.addCallbacks(lambda sd: None, self._handleErrorReply, errbackArgs=(header,))
return d
......@@ -153,8 +153,8 @@ def parseFault(payload):
detail = None
dt = fault.find('detail')
if dt is not None:
dc = dt.getchildren()[0]
if dt is not None and len(list(dt)) > 0:
dc = dt[0]
if dc is not None:
detail = ET.tostring(dc)
......
......@@ -24,18 +24,17 @@ from twisted.application import internet, service as twistedservice
from opennsa import __version__ as version
from opennsa.config import Config
from opennsa import config, logging, constants as cnt, nsa, provreg, database, aggregator, viewresource
from opennsa.topology import nrm, nml, linkvector, service as nmlservice
from opennsa.protocols import rest, nsi2
from opennsa.protocols.shared import httplog
from opennsa.discovery import service as discoveryservice, fetcher
NSI_RESOURCE = b'NSI'
def setupBackend(backend_cfg, network_name, nrm_ports, parent_requester):
bc = backend_cfg.copy()
backend_type = backend_cfg.pop('_backend_type')
......@@ -98,53 +97,46 @@ def setupBackend(backend_cfg, network_name, nrm_ports, parent_requester):
return b
def setupTLSContext(vc):
# ssl/tls contxt
if vc[config.TLS]:
if vc[config.KEY] and vc[config.CERTIFICATE]:
log.msg('setup full 2Way TLS context')
from opennsa.opennsaTlsContext import opennsa2WayTlsContext
ctx_factory = opennsa2WayTlsContext(vc[config.KEY], vc[config.CERTIFICATE], vc[config.CERTIFICATE_DIR], vc[config.VERIFY_CERT])
elif vc[config.CERTIFICATE_DIR]:
# create a context so we can verify https urls
if not os.path.isdir(vc[config.CERTIFICATE_DIR]):
raise config.ConfigurationError('certdir value {} is not a directory'.format(vc[config.CERTIFICATE_DIR]))
from opennsa.opennsaTlsContext import opennsaTlsContext
ctx_factory = opennsaTlsContext(vc[config.CERTIFICATE_DIR], vc[config.VERIFY_CERT])
ctx_factory = opennsa2WayTlsContext(
vc[config.KEY], vc[config.CERTIFICATE], vc[config.CERTIFICATE_DIR], vc[config.VERIFY_CERT])
else:
ctx_factory = None
from opennsa.opennsaTlsContext import opennsaTlsContext
log.msg('setup client TLS context without client authentication')
ctx_factory = opennsaTlsContext(
vc[config.CERTIFICATE_DIR], vc[config.VERIFY_CERT])
return ctx_factory
class CS2RequesterCreator:
def __init__(self, top_resource, aggregator, host, port, tls, ctx_factory):
self.top_resource = top_resource
self.aggregator = aggregator
self.host = host
self.port = port
self.tls = tls
self.ctx_factory = ctx_factory
self.aggregator = aggregator
self.host = host
self.port = port
self.tls = tls
self.ctx_factory = ctx_factory
def create(self, nsi_agent):
hash_input = nsi_agent.urn() + nsi_agent.endpoint
resource_name = b'RequesterService2-' + hashlib.sha1(hash_input.encode()).hexdigest().encode()
resource_name = b'RequesterService2-' + \
hashlib.sha1(hash_input.encode()).hexdigest().encode()
return nsi2.setupRequesterPair(self.top_resource, self.host, self.port, nsi_agent.endpoint, self.aggregator,
resource_name, tls=self.tls, ctx_factory=self.ctx_factory)
class OpenNSAService(twistedservice.MultiService):
def __init__(self, vc):
twistedservice.MultiService.__init__(self)
self.vc = vc
def setupServiceFactory(self):
"""
This sets up the OpenNSA service and ties together everything in the initialization.
......@@ -162,31 +154,37 @@ class OpenNSAService(twistedservice.MultiService):
vc[config.HOST] = socket.getfqdn()
# database
database.setupDatabase(vc[config.DATABASE], vc[config.DATABASE_USER], vc[config.DATABASE_PASSWORD], vc[config.DATABASE_HOST], vc[config.SERVICE_ID_START])
database.setupDatabase(vc[config.DATABASE], vc[config.DATABASE_USER],
vc[config.DATABASE_PASSWORD], vc[config.DATABASE_HOST], vc[config.SERVICE_ID_START])
service_endpoints = []
# base names
domain_name = vc[config.DOMAIN] # FIXME rename variable to domain
nsa_name = domain_name + ':nsa'
domain_name = vc[config.DOMAIN] # FIXME rename variable to domain
nsa_name = domain_name + ':nsa'
# base url
base_protocol = 'https://' if vc[config.TLS] else 'http://'
base_url = base_protocol + vc[config.HOST] + ':' + str(vc[config.PORT])
if vc[config.BASE_URL]:
base_url = vc[config.BASE_URL]
else:
base_protocol = 'https://' if vc[config.TLS] else 'http://'
base_url = base_protocol + vc[config.HOST] + ':' + str(vc[config.PORT])
# nsi endpoint and agent
provider_endpoint = base_url + '/NSI/services/CS2' # hardcode for now
service_endpoints.append( ('Provider', provider_endpoint) )
provider_endpoint = base_url + '/NSI/services/CS2' # hardcode for now
service_endpoints.append(('Provider', provider_endpoint))
ns_agent = nsa.NetworkServiceAgent(nsa_name, provider_endpoint, 'local')
ns_agent = nsa.NetworkServiceAgent(
nsa_name, provider_endpoint, 'local')
# ssl/tls context
ctx_factory = setupTLSContext(vc) # May be None
ctx_factory = setupTLSContext(vc) # May be None
# plugin
if vc[config.PLUGIN]:
from twisted.python import reflect
plugin = reflect.namedAny('opennsa.plugins.%s.plugin' % vc[config.PLUGIN])
plugin = reflect.namedAny(
'opennsa.plugins.%s.plugin' % vc[config.PLUGIN])
else:
from opennsa.plugin import BasePlugin
plugin = BasePlugin()
......@@ -195,21 +193,25 @@ class OpenNSAService(twistedservice.MultiService):
# the dance to setup dynamic providers right
top_resource = resource.Resource()
requester_creator = CS2RequesterCreator(top_resource, None, vc[config.HOST], vc[config.PORT], vc[config.TLS], ctx_factory) # set aggregator later
requester_creator = CS2RequesterCreator(
top_resource, None, vc[config.HOST], vc[config.PORT], vc[config.TLS], ctx_factory) # set aggregator later
provider_registry = provreg.ProviderRegistry( { cnt.CS2_SERVICE_TYPE : requester_creator.create } )
provider_registry = provreg.ProviderRegistry(
{cnt.CS2_SERVICE_TYPE: requester_creator.create})
link_vector = linkvector.LinkVector()
networks = {}
ports = {} # { network : { port : nrmport } }
ports = {} # { network : { port : nrmport } }
parent_requester = None # parent requester is set later
aggr = aggregator.Aggregator(ns_agent, ports, link_vector, parent_requester, provider_registry, vc[config.POLICY], plugin )
parent_requester = None # parent requester is set later
aggr = aggregator.Aggregator(
ns_agent, ports, link_vector, parent_requester, provider_registry, vc[config.POLICY], plugin)
requester_creator.aggregator = aggr
pc = nsi2.setupProvider(aggr, top_resource, ctx_factory=ctx_factory, allowed_hosts=vc.get(config.ALLOWED_HOSTS))
pc = nsi2.setupProvider(
aggr, top_resource, ctx_factory=ctx_factory, allowed_hosts=vc.get(config.ALLOWED_HOSTS))
aggr.parent_requester = pc
# setup backend(s) - for now we only support one
......@@ -219,23 +221,27 @@ class OpenNSAService(twistedservice.MultiService):
if not cnt.AGGREGATOR in vc[config.POLICY]:
vc[config.POLICY].append(cnt.AGGREGATOR)
else: # at least one backend
else: # at least one backend
# This is all temporary right now... clean up later
for backend_name, b_cfg in backend_configs.items():
if backend_name is None or backend_name == '':
raise config.ConfigurationError('You need to specify backend name, use [backend:name]')
raise config.ConfigurationError(
'You need to specify backend name, use [backend:name]')
backend_network_name = '{}:{}'.format(domain_name, backend_name)
backend_network_name = '{}:{}'.format(
domain_name, backend_name)
if not config.NRM_MAP_FILE in b_cfg: # move to verify config
raise config.ConfigurationError('No nrm map specified for backend')
if not config.NRM_MAP_FILE in b_cfg: # move to verify config
raise config.ConfigurationError(
'No nrm map specified for backend')
backend_nrm_map_file = b_cfg[config.NRM_MAP_FILE]
if not os.path.exists(backend_nrm_map_file): # move to verify config
raise config.ConfigError('nrm map file {} for backend {} does not exists'.format(backend_nrm_map_file, backend_name))
if not os.path.exists(backend_nrm_map_file): # move to verify config
raise config.ConfigError('nrm map file {} for backend {} does not exists'.format(
backend_nrm_map_file, backend_name))
nrm_map = open(backend_nrm_map_file)
backend_nrm_ports = nrm.parsePortSpec(nrm_map)
......@@ -243,37 +249,44 @@ class OpenNSAService(twistedservice.MultiService):
link_vector.addLocalNetwork(backend_network_name)
for np in backend_nrm_ports:
if np.remote_network is not None:
link_vector.updateVector(backend_network_name, np.name, { np.remote_network : 1 } ) # hack
link_vector.updateVector(backend_network_name, np.name, {
np.remote_network: 1}) # hack
for network, cost in np.vectors.items():
link_vector.updateVector(np.name, { network : cost })
link_vector.updateVector(np.name, {network: cost})
# build port map for aggreator to lookup
ports.setdefault(backend_network_name, {})[np.name] = np
backend_service = setupBackend(b_cfg, backend_network_name, backend_nrm_ports, aggr)
backend_service = setupBackend(
b_cfg, backend_network_name, backend_nrm_ports, aggr)
networks[backend_network_name] = {
'backend' : backend_service,
'nrm_ports' : backend_nrm_ports
'backend': backend_service,
'nrm_ports': backend_nrm_ports
}
provider_registry.addProvider(ns_agent.urn(), backend_network_name, backend_service)
provider_registry.addProvider(
ns_agent.urn(), backend_network_name, backend_service)
# fetcher
if vc[config.PEERS]:
fetcher_service = fetcher.FetcherService(link_vector, networks, vc[config.PEERS], provider_registry, ctx_factory=ctx_factory)
fetcher_service = fetcher.FetcherService(
link_vector, networks, vc[config.PEERS], provider_registry, ctx_factory=ctx_factory)
fetcher_service.setServiceParent(self)
else:
log.msg('No peers configured, will not be able to do outbound requests (UPA mode)')
log.msg(
'No peers configured, will not be able to do outbound requests (UPA mode)')
# discovery service
opennsa_version = 'OpenNSA-' + version
network_urns = [ '{}{}'.format(cnt.URN_OGF_PREFIX, network_name) for network_name in networks ]
interfaces = [ ( cnt.CS2_PROVIDER, provider_endpoint, None), ( cnt.CS2_SERVICE_TYPE, provider_endpoint, None) ]
features = []
network_urns = ['{}{}'.format(
cnt.URN_OGF_PREFIX, network_name) for network_name in networks]
interfaces = [(cnt.CS2_PROVIDER, provider_endpoint, None),
(cnt.CS2_SERVICE_TYPE, provider_endpoint, None)]
features = []
if networks:
features.append( (cnt.FEATURE_UPA, None) )
features.append((cnt.FEATURE_UPA, None))
if vc[config.PEERS]:
features.append( (cnt.FEATURE_AGGREGATOR, None) )
features.append((cnt.FEATURE_AGGREGATOR, None))
# view resource
vr = viewresource.ConnectionListResource()
......@@ -285,75 +298,77 @@ class OpenNSAService(twistedservice.MultiService):
rest.setupService(aggr, top_resource, vc.get(config.ALLOWED_HOSTS))
service_endpoints.append( ('REST', rest_url) )
interfaces.append( (cnt.OPENNSA_REST, rest_url, None) )
service_endpoints.append(('REST', rest_url))
interfaces.append((cnt.OPENNSA_REST, rest_url, None))
for backend_network_name, no in networks.items():
nml_resource_name = '{}.nml.xml'.format(backend_network_name)
nml_url = '%s/NSI/%s' % (base_url, nml_resource_name)
nml_url = '%s/NSI/%s' % (base_url, nml_resource_name)
nml_network = nml.createNMLNetwork(no['nrm_ports'], backend_network_name, backend_network_name)
can_swap_label = no['backend'].connection_manager.canSwapLabel(cnt.ETHERNET_VLAN)
nml_network = nml.createNMLNetwork(
no['nrm_ports'], backend_network_name, backend_network_name)
can_swap_label = no['backend'].connection_manager.canSwapLabel(
cnt.ETHERNET_VLAN)
nml_service = nmlservice.NMLService(nml_network, can_swap_label)
top_resource.children[NSI_RESOURCE].putChild(nml_resource_name.encode(), nml_service.resource() )
service_endpoints.append( ('NML Topology', nml_url) )
interfaces.append( (cnt.NML_SERVICE_TYPE, nml_url, None) )
top_resource.children[NSI_RESOURCE].putChild(
nml_resource_name.encode(), nml_service.resource())
service_endpoints.append(('NML Topology', nml_url))
interfaces.append((cnt.NML_SERVICE_TYPE, nml_url, None))
# discovery service
discovery_resource_name = b'discovery.xml'
discovery_url = '%s/NSI/%s' % (base_url, discovery_resource_name.decode())
discovery_url = '%s/NSI/%s' % (base_url,
discovery_resource_name.decode())
ds = discoveryservice.DiscoveryService(ns_agent.urn(), now, domain_name, opennsa_version, now, network_urns, interfaces, features, provider_registry, link_vector)
ds = discoveryservice.DiscoveryService(ns_agent.urn(
), now, domain_name, opennsa_version, now, network_urns, interfaces, features, provider_registry, link_vector)
discovery_resource = ds.resource()
top_resource.children[NSI_RESOURCE].putChild(discovery_resource_name, discovery_resource)
link_vector.callOnUpdate( lambda : discovery_resource.updateResource ( ds.xml() ))
top_resource.children[NSI_RESOURCE].putChild(
discovery_resource_name, discovery_resource)
link_vector.callOnUpdate(
lambda: discovery_resource.updateResource(ds.xml()))
service_endpoints.append( ('Discovery', discovery_url) )
service_endpoints.append(('Discovery', discovery_url))
# log service urls
for service_name, url in service_endpoints:
log.msg('{:<12} URL: {}'.format(service_name, url))
factory = server.Site(top_resource)
factory.log = httplog.logRequest # default logging is weird, so we do our own
factory.log = httplog.logRequest # default logging is weird, so we do our own
return factory, ctx_factory
def startService(self):
factory, ctx_factory = self.setupServiceFactory()
if self.vc[config.TLS]:
internet.SSLServer(self.vc[config.PORT], factory, ctx_factory).setServiceParent(self)
internet.SSLServer(
self.vc[config.PORT], factory, ctx_factory).setServiceParent(self)
else:
internet.TCPServer(self.vc[config.PORT], factory).setServiceParent(self)
internet.TCPServer(self.vc[config.PORT],
factory).setServiceParent(self)
# do not start sub-services until we have started this one
twistedservice.MultiService.startService(self)
log.msg('OpenNSA service started')
def stopService(self):
twistedservice.Service.stopService(self)
def createApplication(config_file=config.DEFAULT_CONFIG_FILE, debug=False, payload=False):
application = twistedservice.Application('OpenNSA')
try:
cfg = config.readConfig(config_file)
vc = config.readVerifyConfig(cfg)
configIns = Config.instance()
cfg, vc = configIns.read_config(config_file)
# if log file is empty string use stdout
if vc[config.LOG_FILE]:
......@@ -365,11 +380,11 @@ def createApplication(config_file=config.DEFAULT_CONFIG_FILE, debug=False, paylo
nsa_service = OpenNSAService(vc)
nsa_service.setServiceParent(application)
application.setComponent(log.ILogObserver, logging.DebugLogObserver(log_file, debug, payload=payload).emit)
application.setComponent(log.ILogObserver, logging.DebugLogObserver(
log_file, debug, payload=payload).emit)
return application
except config.ConfigurationError as e:
import sys
sys.stderr.write("Configuration error: %s\n" % e)
sys.exit(1)
......@@ -46,7 +46,7 @@ class ModifiableResource(resource.Resource):
if self.representation is None:
# we haven't been given a representation yet
request.setResponseCode(500)
return 'Resource has not yet been created/updated.'
return b'Resource has not yet been created/updated.'
# check for if-modified-since header, and send 304 back if it is not been modified
msd_header = request.getHeader(IF_MODIFIED_SINCE)
......@@ -55,7 +55,7 @@ class ModifiableResource(resource.Resource):
msd = datetime.datetime.strptime(msd_header, RFC850_FORMAT)
if msd >= self.last_update_time:
request.setResponseCode(304)
return ''
return b''
except ValueError:
pass # error parsing timestamp
......
twisted>=19.7.0
twisted>=21.2.0
twistar>=2.0
psycopg2>=2.7,<2.8 --no-binary psycopg2
psycopg2>=2.9,<2.10 --no-binary psycopg2
pyOpenSSL>=17.5.0
python-dateutil
service_identity
idna
python-dotenv>=0.19.0
cryptography>=3.4.8
python-dateutil>=2.8,<2.9
service-identity>=21.1.0,<22.0.0
idna>=3.2,<3.3
pyasn1>=0.4.8
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment