Skip to content
Snippets Groups Projects
Unverified Commit c865935a authored by JohannesGarm's avatar JohannesGarm Committed by GitHub
Browse files

Merge pull request #29 from safaci2000/feature/admin_query_summary

Introduce Admin access to querySummary, added support for SOAPFaults and fixes for rest_config, test_providers and test_multiple
parents eddf14f9 ada881b0
Branches
No related tags found
No related merge requests found
Showing with 904 additions and 765 deletions
......@@ -12,3 +12,4 @@ docker-compose.override.yml
.devcontainer
twistd.pid
.env
.DS_Store
......@@ -31,24 +31,22 @@ Dmz vs. behind the firewall: Should work with both, not required.
## Dependencies:
* Python 2.7 or later (Python 3 not supported yet)
* Python 3
* Twisted 16.x.x or later, http://twistedmatrix.com/trac/
* Twisted 21.x.x or later, http://twistedmatrix.com/trac/
* Psycopg 2.5.0 or later (http://initd.org/psycopg/, 2.4.6 _might_ work)
* Psycopg 2.9.0 or later (http://initd.org/psycopg/)
* Twistar 1.1 or later (https://pypi.python.org/pypi/twistar/ & http://findingscience.com/twistar/ )
* Twistar 2.0 or later (https://pypi.python.org/pypi/twistar/ & http://findingscience.com/twistar/ )
* PostgreSQL (need 9.5 or later if using connection id assignment)
* PostgreSQL (need 12 or later if using connection id assignment)
* pyOpenSSL 0.14 (when running with SSL/TLS)
* pyOpenSSL 17.5 or later (when running with SSL/TLS)
Python and Twisted should be included in the package system in most recent
Linux distributions.
Older Twisted versions might work, Twisted 15.x and earlier won't work with
OpenSSH 7.0 or later. If you see connection lost for ssh in the log, most
likely your Twisted version is too old.
If you see connection lost for ssh in the log, most likely your Twisted version is too old.
Furthermore, for SSH based backends (Brocade, Force10, and Juniper), the
packages pyasn1 and python-crypto are also required.
......
......@@ -37,3 +37,4 @@ NORDUnet License (3-clause BSD). See LICENSE for more details.
[NORDUnet](http://www.nordu.net) (2011-2015)
......@@ -11,6 +11,7 @@ dbhost=${POSTGRES_HOST}
database=${POSTGRES_DB}
dbuser=${POSTGRES_USER}
dbpassword=${POSTGRES_PASSWORD}
allowed_admins=${ALLOWED_ADMINS}
tls=${TLS_ENABLED}
......
OpenNSA 3 Configuration Migration
=================================
# OpenNSA 3 Configuration Migration
With the port of OpenNSA from Python 2 to Python 3, and the subsequent release
of OpenNSA 3, support for multiple backends was added. For this, some changes
......@@ -12,7 +12,7 @@ The changes are:
Example of old style:
```
```ini
[service]
network=aruba.net
nrmmap=aruba.nrm
......@@ -22,7 +22,7 @@ nrmmap=aruba.nrm
Equivalent config in new style:
```
```ini
[service]
domain=aruba.net
......@@ -32,7 +32,7 @@ nrmmap=aruba.nrm
An example with multiple backends shows why the change was needed:
```
```ini
[service]
domain=aruba.net
......
File moved
......@@ -2,6 +2,7 @@ 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
```
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'
......
......@@ -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 diff is collapsed.
......@@ -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
......@@ -10,7 +10,6 @@ import configparser
from opennsa import constants as cnt
# defaults
DEFAULT_CONFIG_FILE = '/etc/opennsa.conf'
DEFAULT_LOG_FILE = '/var/log/opennsa.log'
......@@ -22,7 +21,6 @@ DEFAULT_VERIFY = True
# This will work on most mordern linux distros
DEFAULT_CERTIFICATE_DIR = '/etc/ssl/certs'
# config blocks and options
BLOCK_SERVICE = 'service'
BLOCK_DUD = 'dud'
......@@ -38,9 +36,10 @@ BLOCK_JUNOSSPACE = 'junosspace'
BLOCK_OESS = 'oess'
BLOCK_CUSTOM_BACKEND = 'custombackend'
# service block
DOMAIN = 'domain' # mandatory
NETWORK_NAME = 'network' # legacy, used to be mandatory
DOMAIN = 'domain' # mandatory
NETWORK_NAME = 'network' # legacy, used to be mandatory
LOG_FILE = 'logfile'
HOST = 'host'
PORT = 'port'
......@@ -53,17 +52,18 @@ PLUGIN = 'plugin'
SERVICE_ID_START = 'serviceid_start'
# database
DATABASE = 'database' # mandatory
DATABASE_USER = 'dbuser' # mandatory
DATABASE = 'database' # mandatory
DATABASE_USER = 'dbuser' # mandatory
DATABASE_PASSWORD = 'dbpassword' # can be none (os auth)
DATABASE_HOST = 'dbhost' # can be none (local db)
DATABASE_HOST = 'dbhost' # can be none (local db)
# tls
KEY = 'key' # mandatory, if tls is set
KEY = 'key' # mandatory, if tls is set
CERTIFICATE = 'certificate' # mandatory, if tls is set
CERTIFICATE_DIR = 'certdir' # mandatory (but dir can be empty)
CERTIFICATE_DIR = 'certdir' # mandatory (but dir can be empty)
VERIFY_CERT = 'verify'
ALLOWED_HOSTS = 'allowedhosts' # comma seperated list
ALLOWED_ADMINS = 'allowed_admins' # list of requester nsaId with administration level access
# generic stuff
_SSH_HOST = 'host'
......@@ -113,7 +113,6 @@ PICA8OVS_SSH_PUBLIC_KEY = _SSH_PUBLIC_KEY
PICA8OVS_SSH_PRIVATE_KEY = _SSH_PRIVATE_KEY
PICA8OVS_DB_IP = 'dbip'
# NCS VPN Backend
NCS_SERVICES_URL = 'url'
NCS_USER = 'user'
......@@ -166,199 +165,243 @@ class EnvInterpolation(configparser.BasicInterpolation):
return os.path.expandvars(value)
def readConfig(filename):
cfg = configparser.ConfigParser(interpolation=EnvInterpolation())
cfg.add_section(BLOCK_SERVICE)
cfg.read([filename])
return cfg
def readVerifyConfig(cfg):
class Config(object):
"""
Read a config and verify that things are correct. Will also fill in
default values where applicable.
Singleton instance of configuration class. Loads the config and persists it to class object.
This is supposed to be used during application creation (before service
start) to ensure that simple configuration errors do not pop up efter
daemonization.
Returns a "verified" config, which is a dictionary.
Also, provides utility function around the loaded configuration
"""
_instance = None
def __init__(self):
raise RuntimeError("Call instance() instead, singleton class")
@classmethod
def instance(cls):
if cls._instance is None:
print('Creating new instance')
cls._instance = cls.__new__(cls)
cls._instance.cfg = None
cls._instance.vc = None
# Put any initialization here.
return cls._instance
def read_config(self, filename):
"""
Load the configuration from a given file
"""
if self._instance.cfg is None:
cfg = configparser.ConfigParser(interpolation=EnvInterpolation())
cfg.add_section(BLOCK_SERVICE)
cfg.read([filename])
self._instance.cfg = cfg
return self._instance.cfg, self._read_verify_config()
def _read_verify_config(self):
"""
Returns a dictionary of the loaded config once verified
"""
if self._instance.vc is None:
self._instance.vc = self._load_config_dict()
return self._instance.vc
def config_dict(self):
"""
Returns the loaded dict if one exists, or an empty one otherwise.
"""
return self._instance.vc if self._instance.vc is not None else {}
@property
def allowed_admins(self):
"""
Property returns array of allowed admins
"""
return self.config_dict().get(ALLOWED_ADMINS, '')
def is_admin_override(self, urn):
"""
Check if the URN matches a valid admin. Allowing all queries to execute
"""
admins = self.allowed_admins
for entry in self.allowed_admins:
if entry == urn:
return True
return False
def _load_database_config(self, vc):
# vc = self._instance.vc
cfg = self._instance.cfg
# database
try:
vc[DATABASE] = cfg.get(BLOCK_SERVICE, DATABASE)
except configparser.NoOptionError:
raise ConfigurationError(
'No database specified in configuration file (mandatory)')
vc = {}
# Check for deprecated / old invalid stuff
try:
vc[DATABASE_USER] = cfg.get(BLOCK_SERVICE, DATABASE_USER)
except configparser.NoOptionError:
raise ConfigurationError(
'No database user specified in configuration file (mandatory)')
try:
cfg.get(BLOCK_SERVICE, NRM_MAP_FILE)
raise ConfigurationError(
'NRM Map file should be specified under backend')
except configparser.NoOptionError:
pass
vc[DATABASE_PASSWORD] = cfg.get(BLOCK_SERVICE, DATABASE_PASSWORD, fallback=None)
vc[DATABASE_HOST] = cfg.get(BLOCK_SERVICE, DATABASE_HOST, fallback='localhost')
vc[SERVICE_ID_START] = cfg.get(BLOCK_SERVICE, SERVICE_ID_START, fallback=None)
# check / extract
def _load_config_dict(self) -> dict:
"""
Read a config and verify that things are correct. Will also fill in
default values where applicable.
try:
vc[DOMAIN] = cfg.get(BLOCK_SERVICE, DOMAIN)
except configparser.NoOptionError:
raise ConfigurationError(
'No domain name specified in configuration file (mandatory, see docs/migration)')
This is supposed to be used during application creation (before service
start) to ensure that simple configuration errors do not pop up efter
daemonization.
try:
cfg.get(BLOCK_SERVICE, NETWORK_NAME)
raise ConfigurationError(
'Network name no longer used, use domain (see docs/migration)')
except configparser.NoOptionError:
pass
Returns a "verified" config, which is a dictionary.
"""
cfg = self._instance.cfg
vc = {}
try:
vc[LOG_FILE] = cfg.get(BLOCK_SERVICE, LOG_FILE)
except configparser.NoOptionError:
vc[LOG_FILE] = DEFAULT_LOG_FILE
# Check for deprecated / old invalid stuff
try:
nrm_map_file = cfg.get(BLOCK_SERVICE, NRM_MAP_FILE)
if not os.path.exists(nrm_map_file):
try:
cfg.get(BLOCK_SERVICE, NRM_MAP_FILE)
raise ConfigurationError(
'Specified NRM mapping file does not exist (%s)' % nrm_map_file)
vc[NRM_MAP_FILE] = nrm_map_file
except configparser.NoOptionError:
vc[NRM_MAP_FILE] = None
try:
vc[REST] = cfg.getboolean(BLOCK_SERVICE, REST)
except configparser.NoOptionError:
vc[REST] = False
try:
peers_raw = cfg.get(BLOCK_SERVICE, PEERS)
vc[PEERS] = [Peer(purl.strip(), 1) for purl in peers_raw.split('\n')]
except configparser.NoOptionError:
vc[PEERS] = None
try:
vc[HOST] = cfg.get(BLOCK_SERVICE, HOST)
except configparser.NoOptionError:
vc[HOST] = None
try:
vc[TLS] = cfg.getboolean(BLOCK_SERVICE, TLS)
except configparser.NoOptionError:
vc[TLS] = DEFAULT_TLS
try:
vc[PORT] = cfg.getint(BLOCK_SERVICE, PORT)
except configparser.NoOptionError:
vc[PORT] = DEFAULT_TLS_PORT if vc[TLS] else DEFAULT_TCP_PORT
try:
policies = cfg.get(BLOCK_SERVICE, POLICY).split(',')
for policy in policies:
if not policy in (cnt.REQUIRE_USER, cnt.REQUIRE_TRACE, cnt.AGGREGATOR, cnt.ALLOW_HAIRPIN):
raise ConfigurationError('Invalid policy: %s' % policy)
vc[POLICY] = policies
except configparser.NoOptionError:
vc[POLICY] = []
try:
vc[PLUGIN] = cfg.get(BLOCK_SERVICE, PLUGIN)
except configparser.NoOptionError:
vc[PLUGIN] = None
# database
try:
vc[DATABASE] = cfg.get(BLOCK_SERVICE, DATABASE)
except configparser.NoOptionError:
raise ConfigurationError(
'No database specified in configuration file (mandatory)')
try:
vc[DATABASE_USER] = cfg.get(BLOCK_SERVICE, DATABASE_USER)
except configparser.NoOptionError:
raise ConfigurationError(
'No database user specified in configuration file (mandatory)')
try:
vc[DATABASE_PASSWORD] = cfg.get(BLOCK_SERVICE, DATABASE_PASSWORD)
except configparser.NoOptionError:
vc[DATABASE_PASSWORD] = None
try:
vc[DATABASE_HOST] = cfg.get(BLOCK_SERVICE, DATABASE_HOST)
except configparser.NoOptionError:
vc[DATABASE_HOST] = None
try:
vc[SERVICE_ID_START] = cfg.get(BLOCK_SERVICE, SERVICE_ID_START)
except configparser.NoOptionError:
vc[SERVICE_ID_START] = None
# we always extract certdir and verify as we need that for performing https requests
try:
certdir = cfg.get(BLOCK_SERVICE, CERTIFICATE_DIR)
if not os.path.exists(certdir):
'NRM Map file should be specified under backend')
except configparser.NoOptionError:
pass
# check / extract
try:
vc[DOMAIN] = cfg.get(BLOCK_SERVICE, DOMAIN)
except configparser.NoOptionError:
raise ConfigurationError(
'Specified certdir does not exist (%s)' % certdir)
vc[CERTIFICATE_DIR] = certdir
except configparser.NoOptionError:
vc[CERTIFICATE_DIR] = DEFAULT_CERTIFICATE_DIR
try:
vc[VERIFY_CERT] = cfg.getboolean(BLOCK_SERVICE, VERIFY_CERT)
except configparser.NoOptionError:
vc[VERIFY_CERT] = DEFAULT_VERIFY
# tls
if vc[TLS]:
'No domain name specified in configuration file (mandatory, see docs/migration)')
try:
hostkey = cfg.get(BLOCK_SERVICE, KEY)
hostcert = cfg.get(BLOCK_SERVICE, CERTIFICATE)
cfg.get(BLOCK_SERVICE, NETWORK_NAME)
raise ConfigurationError(
'Network name no longer used, use domain (see docs/migration)')
except configparser.NoOptionError:
pass
if not os.path.exists(hostkey):
raise ConfigurationError(
'Specified hostkey does not exist (%s)' % hostkey)
if not os.path.exists(hostcert):
vc[LOG_FILE] = cfg.get(BLOCK_SERVICE, LOG_FILE, fallback=DEFAULT_LOG_FILE)
try:
nrm_map_file = cfg.get(BLOCK_SERVICE, NRM_MAP_FILE)
if not os.path.exists(nrm_map_file):
raise ConfigurationError(
'Specified hostcert does not exist (%s)' % hostcert)
'Specified NRM mapping file does not exist (%s)' % nrm_map_file)
vc[NRM_MAP_FILE] = nrm_map_file
except configparser.NoOptionError:
vc[NRM_MAP_FILE] = None
vc[KEY] = hostkey
vc[CERTIFICATE] = hostcert
vc[REST] = cfg.getboolean(BLOCK_SERVICE, REST, fallback=False)
try:
peers_raw = cfg.get(BLOCK_SERVICE, PEERS)
vc[PEERS] = [Peer(purl.strip(), 1) for purl in peers_raw.split('\n')]
except configparser.NoOptionError:
vc[PEERS] = None
vc[HOST] = cfg.get(BLOCK_SERVICE, HOST, fallback=None)
vc[TLS] = cfg.getboolean(BLOCK_SERVICE, TLS, fallback=DEFAULT_TLS)
vc[PORT] = cfg.getint(BLOCK_SERVICE, PORT, fallback=DEFAULT_TLS_PORT if vc[TLS] else DEFAULT_TCP_PORT)
try:
policies = cfg.get(BLOCK_SERVICE, POLICY).split(',')
for policy in policies:
if not policy in (cnt.REQUIRE_USER, cnt.REQUIRE_TRACE, cnt.AGGREGATOR, cnt.ALLOW_HAIRPIN):
raise ConfigurationError('Invalid policy: %s' % policy)
vc[POLICY] = policies
except configparser.NoOptionError:
vc[POLICY] = []
vc[PLUGIN] = cfg.get(BLOCK_SERVICE, PLUGIN, fallback=None)
self._load_database_config(vc)
self._load_certificates(vc)
## Set override of allowed Admins
allowed_hosts_admins = cfg.get(BLOCK_SERVICE, ALLOWED_ADMINS, fallback='')
vc[ALLOWED_ADMINS] = [i.strip() for i in allowed_hosts_admins.split(',') if len(i) > 0]
# backends
self._load_backends(vc)
return vc
def _load_certificates(self, vc):
cfg = self._instance.cfg
# we always extract certdir and verify as we need that for performing https requests
try:
certdir = cfg.get(BLOCK_SERVICE, CERTIFICATE_DIR)
if not os.path.exists(certdir):
raise ConfigurationError(
'Specified certdir does not exist (%s)' % certdir)
vc[CERTIFICATE_DIR] = certdir
except configparser.NoOptionError:
vc[CERTIFICATE_DIR] = DEFAULT_CERTIFICATE_DIR
try:
vc[VERIFY_CERT] = cfg.getboolean(BLOCK_SERVICE, VERIFY_CERT)
except configparser.NoOptionError:
vc[VERIFY_CERT] = DEFAULT_VERIFY
# tls
if vc[TLS]:
try:
allowed_hosts_cfg = cfg.get(BLOCK_SERVICE, ALLOWED_HOSTS)
vc[ALLOWED_HOSTS] = allowed_hosts_cfg.split(',')
except:
pass
hostkey = cfg.get(BLOCK_SERVICE, KEY)
hostcert = cfg.get(BLOCK_SERVICE, CERTIFICATE)
except configparser.NoOptionError as e:
# Not enough options for configuring tls context
raise ConfigurationError('Missing TLS option: %s' % str(e))
if not os.path.exists(hostkey):
raise ConfigurationError(
'Specified hostkey does not exist (%s)' % hostkey)
if not os.path.exists(hostcert):
raise ConfigurationError(
'Specified hostcert does not exist (%s)' % hostcert)
# backends
backends = {}
vc[KEY] = hostkey
vc[CERTIFICATE] = hostcert
for section in cfg.sections():
try:
allowed_hosts_cfg = cfg.get(BLOCK_SERVICE, ALLOWED_HOSTS)
vc[ALLOWED_HOSTS] = [i.strip() for i in allowed_hosts_cfg.split(',') if len(i) > 0]
if section == 'service':
continue
except:
pass
if ':' in section:
backend_type, name = section.split(':', 2)
else:
backend_type = section
name = ''
except configparser.NoOptionError as e:
# Not enough options for configuring tls context
raise ConfigurationError('Missing TLS option: %s' % str(e))
if name in backends:
raise ConfigurationError(
'Can only have one backend named "%s"' % name)
def _load_backends(self, vc):
"""
Verify and load backends into configuration class
"""
cfg = self._instance.cfg
backends = {}
if backend_type in (BLOCK_DUD, BLOCK_JUNIPER_EX, BLOCK_JUNIPER_VPLS, BLOCK_JUNOSMX, BLOCK_FORCE10, BLOCK_BROCADE,
BLOCK_NCSVPN, BLOCK_PICA8OVS, BLOCK_OESS, BLOCK_JUNOSSPACE, BLOCK_JUNOSEX,
BLOCK_CUSTOM_BACKEND, 'asyncfail'):
backend_conf = dict(cfg.items(section))
backend_conf['_backend_type'] = backend_type
backends[name] = backend_conf
for section in cfg.sections():
if section == 'service':
continue
if ':' in section:
backend_type, name = section.split(':', 2)
else:
backend_type = section
name = ''
if name in backends:
raise ConfigurationError(
'Can only have one backend named "%s"' % name)
vc['backend'] = backends
if backend_type in (
BLOCK_DUD, BLOCK_JUNIPER_EX, BLOCK_JUNIPER_VPLS, BLOCK_JUNOSMX, BLOCK_FORCE10, BLOCK_BROCADE,
BLOCK_NCSVPN, BLOCK_PICA8OVS, BLOCK_OESS, BLOCK_JUNOSSPACE, BLOCK_JUNOSEX,
BLOCK_CUSTOM_BACKEND, 'asyncfail'):
backend_conf = dict(cfg.items(section))
backend_conf['_backend_type'] = backend_type
backends[name] = backend_conf
return vc
vc['backend'] = backends
......@@ -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')
......@@ -99,7 +98,6 @@ def setupBackend(backend_cfg, network_name, nrm_ports, parent_requester):
def setupTLSContext(vc):
# ssl/tls contxt
if vc[config.TLS]:
from opennsa.opennsaTlsContext import opennsa2WayTlsContext
......@@ -130,10 +128,9 @@ class CS2RequesterCreator:
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()
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)
......@@ -254,7 +251,7 @@ class OpenNSAService(twistedservice.MultiService):
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
np.remote_network: 1}) # hack
for network, cost in np.vectors.items():
link_vector.updateVector(np.name, {network: cost})
# build port map for aggreator to lookup
......@@ -306,7 +303,6 @@ class OpenNSAService(twistedservice.MultiService):
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)
......@@ -369,12 +365,11 @@ class OpenNSAService(twistedservice.MultiService):
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]:
......
......@@ -7,3 +7,4 @@ 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
......@@ -3,12 +3,12 @@ from twisted.trial import unittest
import json
import tempfile
import configparser
from io import StringIO
from opennsa import config, setup
from opennsa.config import Config
from . import db
ARUBA_DUD_CONFIG_NO_DATABASE = """
[service]
domain=aruba.net
......@@ -37,11 +37,15 @@ dbpassword={db_password}
tls=false
[dud]
[backends:dummy]
name=foobar
"""
ARUBA_DUD_CONFIG = """
[service]
domain=aruba.net
host=dummy
logfile=
rest=true
port=4080
......@@ -105,31 +109,49 @@ ethernet bon bonaire.net:topology#arb(-in|-out) vlan:1780-1799
class ConfigTest(unittest.TestCase):
def _reset_instance(self):
try:
self.configIns._instance.cfg = None
self.configIns._instance.vc = None
except:
pass
def setUp(self):
tc = json.load( open(db.CONFIG_FILE) )
self.database = tc['database']
self.db_user = tc['user']
self.configIns = Config.instance()
self._reset_instance()
tc = json.load(open(db.CONFIG_FILE))
self.database = tc['database']
self.db_user = tc['user']
self.db_password = tc['password']
self.db_host = '127.0.0.1'
self.db_host = '127.0.0.1'
def _generate_temp_file(self, buffer):
"""
Helper utility to generate a temp file and write buffer to it.
"""
tmp = tempfile.NamedTemporaryFile('w+t')
tmp.write(buffer)
tmp.flush()
return tmp
def testConfigParsingNoDatabase(self):
config_file_content = ARUBA_DUD_CONFIG_NO_DATABASE
raw_cfg = configparser.SafeConfigParser()
raw_cfg.read_string(config_file_content)
expectedError = "No database specified in configuration file (mandatory)"
tmp = None
try:
cfg = config.readVerifyConfig(raw_cfg)
tmp = self._generate_temp_file(config_file_content)
cfg, vc = self.configIns.read_config(tmp.name)
nsa_service = setup.OpenNSAService(cfg)
factory, _ = nsa_service.setupServiceFactory()
self.fail('Should have raised config.ConfigurationError')
except config.ConfigurationError as e:
pass
self.assertEquals(expectedError, e.args[0])
finally:
if tmp is not None:
tmp.close()
def testConfigParsingNoNetworkName(self):
......@@ -137,17 +159,18 @@ class ConfigTest(unittest.TestCase):
db_host=self.db_host,
db_user=self.db_user,
db_password=self.db_password)
raw_cfg = configparser.SafeConfigParser()
raw_cfg.read_string(config_file_content)
tmp = None
try:
cfg = config.readVerifyConfig(raw_cfg)
nsa_service = setup.OpenNSAService(cfg)
tmp = self._generate_temp_file(config_file_content)
cfg, vc = self.configIns.read_config(tmp.name)
nsa_service = setup.OpenNSAService(self.configIns.config_dict())
factory, _ = nsa_service.setupServiceFactory()
self.fail('Should have raised config.ConfigurationError')
except config.ConfigurationError as e:
pass
finally:
if tmp is not None:
tmp.close()
def testConfigParsing(self):
......@@ -161,24 +184,28 @@ class ConfigTest(unittest.TestCase):
db_password=self.db_password,
nrm_map=aruba_ojs.name)
raw_cfg = configparser.SafeConfigParser()
raw_cfg.read_string(config_file_content)
cfg = config.readVerifyConfig(raw_cfg)
nsa_service = setup.OpenNSAService(cfg)
factory, _ = nsa_service.setupServiceFactory()
tmp = self._generate_temp_file(config_file_content)
cfg, vc = self.configIns.read_config(tmp.name)
try:
nsa_service = setup.OpenNSAService(vc)
factory, _ = nsa_service.setupServiceFactory()
finally:
tmp.close()
aruba_ojs.close()
def testInvalidLegacyConfig(self):
raw_cfg = configparser.SafeConfigParser()
raw_cfg.read_string(INVALID_LEGACY_CONFIG)
config_file_content = INVALID_LEGACY_CONFIG
tmp = self._generate_temp_file(config_file_content)
try:
cfg = config.readVerifyConfig(raw_cfg)
cfg, vc = self.configIns.read_config(tmp.name)
self.fail('Should have raised ConfigurationError')
except config.ConfigurationError:
pass
finally:
tmp.close()
def testConfigParsingMultiBackend(self):
......@@ -201,13 +228,13 @@ class ConfigTest(unittest.TestCase):
nrm_ojs=aruba_ojs.name,
nrm_san=aruba_san.name)
# parse and verify config
tmp = self._generate_temp_file(config_file_content)
cfg = configparser.SafeConfigParser()
cfg.read_string(config_file_content)
verified_config = config.readVerifyConfig(cfg)
# do the setup dance to see if all the wiring is working, but don't start anything
nsa_service = setup.OpenNSAService(verified_config)
factory, _ = nsa_service.setupServiceFactory()
try:
cfg, verified_config = self.configIns.read_config(tmp.name)
# do the setup dance to see if all the wiring is working, but don't start anything
nsa_service = setup.OpenNSAService(verified_config)
factory, _ = nsa_service.setupServiceFactory()
finally:
tmp.close()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment