mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-02 02:57:52 +00:00
Compare commits
6 Commits
candi-1.0
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6dab0477b | ||
|
|
ce4a226615 | ||
|
|
78fc0c0680 | ||
|
|
2c4f2fbb12 | ||
|
|
63545c86ed | ||
|
|
aa78d00158 |
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
venv/
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -65,3 +65,5 @@ target/
|
||||
|
||||
# MacOS DS_store
|
||||
.DS_Store
|
||||
|
||||
venv/
|
||||
|
||||
@@ -2,13 +2,16 @@ FROM python:3.7-slim
|
||||
WORKDIR /opt/application/oopt-gnpy
|
||||
RUN mkdir -p /shared/example-data \
|
||||
&& groupadd gnpy \
|
||||
&& useradd -g gnpy -m gnpy \
|
||||
&& useradd -u 1000 -g gnpy -m gnpy \
|
||||
&& apt-get update \
|
||||
&& apt-get install git -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY . /opt/application/oopt-gnpy
|
||||
WORKDIR /opt/application/oopt-gnpy
|
||||
RUN pip install . \
|
||||
RUN mkdir topology \
|
||||
&& mkdir equipment \
|
||||
&& mkdir autodesign \
|
||||
&& pip install . \
|
||||
&& chown -Rc gnpy:gnpy /opt/application/oopt-gnpy /shared/example-data
|
||||
USER gnpy
|
||||
ENTRYPOINT ["/opt/application/oopt-gnpy/.docker-entry.sh"]
|
||||
|
||||
17
README.rst
17
README.rst
@@ -139,14 +139,25 @@ You can run it through command line or Docker.
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ docker run -p 8080:8080 -dit xxxx gnpy-rest
|
||||
$ docker run -p 8080:8080 -it emmanuelledelfour/gnpy-experimental:candi-1.0 gnpy-rest
|
||||
|
||||
After starting the api server, you can lauch a request
|
||||
When starting the api server will aks for an encryption/decryption key. This key i used to encrypt equipment file when using /api/v1/equipments endpoint.
|
||||
This key is a Fernet key and can be generated this way:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
Fernet.generate_key()
|
||||
|
||||
|
||||
After typing the key, you can detach the container by typing ^P^Q.
|
||||
After starting the api server, you can launch a request
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ curl -v -X POST -H "Content-Type: application/json" -d @<PATH_TO_JSON_REQUEST_FILE> http://localhost:8080/api/v1/path-computation
|
||||
$ curl -v -X POST -H "Content-Type: application/json" -d @<PATH_TO_JSON_REQUEST_FILE> https://localhost:8080/api/v1/path-computation -k
|
||||
|
||||
TODO: api documentation, unit tests, real WSGI server with trusted certificates
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
9
gnpy/api/__init__.py
Normal file
9
gnpy/api/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# coding: utf-8
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
import gnpy.api.route.path_request_route
|
||||
import gnpy.api.route.status_route
|
||||
import gnpy.api.route.topology_route
|
||||
import gnpy.api.route.equipments_route
|
||||
1
gnpy/api/exception/__init__.py
Normal file
1
gnpy/api/exception/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# coding: utf-8
|
||||
14
gnpy/api/exception/config_error.py
Normal file
14
gnpy/api/exception/config_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class ConfigError(Exception):
|
||||
""" Exception raise for configuration file error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
14
gnpy/api/exception/equipment_error.py
Normal file
14
gnpy/api/exception/equipment_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class EquipmentError(Exception):
|
||||
""" Exception raise for equipment error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
33
gnpy/api/exception/exception_handler.py
Normal file
33
gnpy/api/exception/exception_handler.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
import re
|
||||
|
||||
import werkzeug
|
||||
|
||||
from gnpy.api.model.error import Error
|
||||
|
||||
_reaesc = re.compile(r'\x1b[^m]*m')
|
||||
|
||||
|
||||
def common_error_handler(exception):
|
||||
"""
|
||||
|
||||
:type exception: Exception
|
||||
|
||||
"""
|
||||
status_code = 500
|
||||
if not isinstance(exception, werkzeug.exceptions.HTTPException):
|
||||
exception = werkzeug.exceptions.InternalServerError()
|
||||
exception.description = "Something went wrong on our side."
|
||||
else:
|
||||
status_code = exception.code
|
||||
response = Error(message=exception.name, description=exception.description,
|
||||
code=status_code)
|
||||
|
||||
return werkzeug.Response(response=json.dumps(response.__dict__), status=status_code, mimetype='application/json')
|
||||
|
||||
|
||||
def bad_request_handler(exception):
|
||||
response = Error(message='bad request', description=_reaesc.sub('', str(exception)),
|
||||
code=400)
|
||||
return werkzeug.Response(response=json.dumps(response.__dict__), status=400, mimetype='application/json')
|
||||
14
gnpy/api/exception/path_computation_error.py
Normal file
14
gnpy/api/exception/path_computation_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class PathComputationError(Exception):
|
||||
""" Exception raise for path computation error error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
14
gnpy/api/exception/topology_error.py
Normal file
14
gnpy/api/exception/topology_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class TopologyError(Exception):
|
||||
""" Exception raise for topology error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
1
gnpy/api/model/__init__.py
Normal file
1
gnpy/api/model/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# coding: utf-8
|
||||
17
gnpy/api/model/error.py
Normal file
17
gnpy/api/model/error.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class Error:
|
||||
|
||||
def __init__(self, code: int = None, message: str = None, description: str = None):
|
||||
"""Error
|
||||
:param code: The code of this Error.
|
||||
:type code: int
|
||||
:param message: The message of this Error.
|
||||
:type message: str
|
||||
:param description: The description of this Error.
|
||||
:type description: str
|
||||
"""
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.description = description
|
||||
8
gnpy/api/model/result.py
Normal file
8
gnpy/api/model/result.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class Result:
|
||||
|
||||
def __init__(self, message: str = None, description: str = None):
|
||||
self.message = message
|
||||
self.description = description
|
||||
83
gnpy/api/rest_example.py
Normal file
83
gnpy/api/rest_example.py
Normal file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.tools.rest_example
|
||||
=======================
|
||||
|
||||
GNPy as a rest API example
|
||||
'''
|
||||
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
import werkzeug
|
||||
from flask_injector import FlaskInjector
|
||||
from injector import singleton
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import gnpy.core.exceptions as exceptions
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.exception_handler import bad_request_handler, common_error_handler
|
||||
from gnpy.api.exception.path_computation_error import PathComputationError
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.service import config_service
|
||||
from gnpy.api.service.encryption_service import EncryptionService
|
||||
from gnpy.api.service.equipment_service import EquipmentService
|
||||
from gnpy.api.service.path_request_service import PathRequestService
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _init_logger():
|
||||
handler = RotatingFileHandler('api.log', maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
|
||||
ch = logging.StreamHandler()
|
||||
logging.basicConfig(level=logging.INFO, handlers=[handler, ch],
|
||||
format="%(asctime)s %(levelname)s %(name)s(%(lineno)s) [%(threadName)s - %(thread)d] - %("
|
||||
"message)s")
|
||||
|
||||
|
||||
def _init_app(key):
|
||||
app.register_error_handler(KeyError, bad_request_handler)
|
||||
app.register_error_handler(TypeError, bad_request_handler)
|
||||
app.register_error_handler(ValueError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ConfigurationError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.DisjunctionError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.EquipmentConfigError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.NetworkTopologyError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ServiceError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.SpectrumError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ParametersError, bad_request_handler)
|
||||
app.register_error_handler(AssertionError, bad_request_handler)
|
||||
app.register_error_handler(InternalServerError, common_error_handler)
|
||||
app.register_error_handler(TopologyError, bad_request_handler)
|
||||
app.register_error_handler(PathComputationError, bad_request_handler)
|
||||
for error_code in werkzeug.exceptions.default_exceptions:
|
||||
app.register_error_handler(error_code, common_error_handler)
|
||||
config = config_service.init_config()
|
||||
config.add_section('SECRET')
|
||||
config.set('SECRET', 'equipment', key)
|
||||
app.config['properties'] = config
|
||||
|
||||
|
||||
def _configure(binder):
|
||||
binder.bind(EquipmentService,
|
||||
to=EquipmentService(EncryptionService(app.config['properties'].get('SECRET', 'equipment'))),
|
||||
scope=singleton)
|
||||
binder.bind(PathRequestService,
|
||||
to=PathRequestService(EncryptionService(app.config['properties'].get('SECRET', 'equipment'))),
|
||||
scope=singleton)
|
||||
app.config['properties'].pop('SECRET', None)
|
||||
|
||||
|
||||
def main():
|
||||
key = input('Enter encryption/decryption key: ')
|
||||
_init_logger()
|
||||
_init_app(key)
|
||||
FlaskInjector(app=app, modules=[_configure])
|
||||
|
||||
app.run(host='0.0.0.0', port=8080, ssl_context='adhoc')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
2
gnpy/api/route/__init__.py
Normal file
2
gnpy/api/route/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# coding: utf-8
|
||||
|
||||
38
gnpy/api/route/equipments_route.py
Normal file
38
gnpy/api/route/equipments_route.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# coding: utf-8
|
||||
import http
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.equipment_error import EquipmentError
|
||||
from gnpy.api.model.result import Result
|
||||
from gnpy.api.service.equipment_service import EquipmentService
|
||||
|
||||
EQUIPMENT_BASE_PATH = '/api/v1/equipments'
|
||||
EQUIPMENT_ID_PATH = EQUIPMENT_BASE_PATH + '/<equipment_id>'
|
||||
|
||||
|
||||
@app.route(EQUIPMENT_BASE_PATH, methods=['POST'])
|
||||
def create_equipment(equipment_service: EquipmentService):
|
||||
if not request.is_json:
|
||||
raise EquipmentError('Request body is not json')
|
||||
equipment_identifier = equipment_service.save_equipment(request.json)
|
||||
response = Result(message='Equipment creation ok', description=equipment_identifier)
|
||||
return json.dumps(response.__dict__), 201, {'location': EQUIPMENT_BASE_PATH + '/' + equipment_identifier}
|
||||
|
||||
|
||||
@app.route(EQUIPMENT_ID_PATH, methods=['PUT'])
|
||||
def update_equipment(equipment_id, equipment_service: EquipmentService):
|
||||
if not request.is_json:
|
||||
raise EquipmentError('Request body is not json')
|
||||
equipment_identifier = equipment_service.update_equipment(request.json, equipment_id)
|
||||
response = Result(message='Equipment update ok', description=equipment_identifier)
|
||||
return json.dumps(response.__dict__), http.HTTPStatus.OK, {
|
||||
'location': EQUIPMENT_BASE_PATH + '/' + equipment_identifier}
|
||||
|
||||
|
||||
@app.route(EQUIPMENT_ID_PATH, methods=['DELETE'])
|
||||
def delete_equipment(equipment_id, equipment_service: EquipmentService):
|
||||
equipment_service.delete_equipment(equipment_id)
|
||||
return '', http.HTTPStatus.NO_CONTENT
|
||||
63
gnpy/api/route/path_request_route.py
Normal file
63
gnpy/api/route/path_request_route.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# coding: utf-8
|
||||
import http
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from flask import request
|
||||
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.equipment_error import EquipmentError
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.service import topology_service
|
||||
from gnpy.api.service.equipment_service import EquipmentService
|
||||
from gnpy.api.service.path_request_service import PathRequestService
|
||||
from gnpy.tools.json_io import _equipment_from_json, network_from_json
|
||||
from gnpy.topology.request import ResultElement
|
||||
|
||||
PATH_COMPUTATION_BASE_PATH = '/api/v1/path-computation'
|
||||
AUTODESIGN_PATH = PATH_COMPUTATION_BASE_PATH + '/<path_computation_id>/autodesign'
|
||||
|
||||
_examples_dir = Path(__file__).parent.parent.parent / 'example-data'
|
||||
|
||||
|
||||
@app.route(PATH_COMPUTATION_BASE_PATH, methods=['POST'])
|
||||
def compute_path(equipment_service: EquipmentService, path_request_service: PathRequestService):
|
||||
data = request.json
|
||||
service = data['gnpy-api:service']
|
||||
if 'gnpy-api:topology' in data:
|
||||
topology = data['gnpy-api:topology']
|
||||
elif 'gnpy-api:topology_id' in data:
|
||||
topology = topology_service.get_topology(data['gnpy-api:topology_id'])
|
||||
else:
|
||||
raise TopologyError('No topology found in request')
|
||||
if 'gnpy-api:equipment' in data:
|
||||
equipment = data['gnpy-api:equipment']
|
||||
elif 'gnpy-api:equipment_id' in data:
|
||||
equipment = equipment_service.get_equipment(data['gnpy-api:equipment_id'])
|
||||
else:
|
||||
raise EquipmentError('No equipment found in request')
|
||||
equipment = _equipment_from_json(equipment,
|
||||
os.path.join(_examples_dir, 'std_medium_gain_advanced_config.json'))
|
||||
network = network_from_json(topology, equipment)
|
||||
|
||||
propagatedpths, reversed_propagatedpths, rqs, path_computation_id = path_request_service.path_requests_run(service,
|
||||
network,
|
||||
equipment)
|
||||
# Generate the output
|
||||
result = []
|
||||
# assumes that list of rqs and list of propgatedpths have same order
|
||||
for i, pth in enumerate(propagatedpths):
|
||||
result.append(ResultElement(rqs[i], pth, reversed_propagatedpths[i]))
|
||||
return {"result": {"response": [n.json for n in result]}}, 201, {
|
||||
'location': AUTODESIGN_PATH.replace('<path_computation_id>', path_computation_id)}
|
||||
|
||||
|
||||
@app.route(AUTODESIGN_PATH, methods=['GET'])
|
||||
def get_autodesign(path_computation_id, path_request_service: PathRequestService):
|
||||
return path_request_service.get_autodesign(path_computation_id), http.HTTPStatus.OK
|
||||
|
||||
|
||||
@app.route(AUTODESIGN_PATH, methods=['DELETE'])
|
||||
def delete_autodesign(path_computation_id, path_request_service: PathRequestService):
|
||||
path_request_service.delete_autodesign(path_computation_id)
|
||||
return '', http.HTTPStatus.NO_CONTENT
|
||||
7
gnpy/api/route/status_route.py
Normal file
7
gnpy/api/route/status_route.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# coding: utf-8
|
||||
from gnpy.api import app
|
||||
|
||||
|
||||
@app.route('/api/v1/status', methods=['GET'])
|
||||
def api_status():
|
||||
return {"version": "v1", "status": "ok"}, 200
|
||||
43
gnpy/api/route/topology_route.py
Normal file
43
gnpy/api/route/topology_route.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# coding: utf-8
|
||||
import http
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.model.result import Result
|
||||
from gnpy.api.service import topology_service
|
||||
|
||||
TOPOLOGY_BASE_PATH = '/api/v1/topologies'
|
||||
TOPOLOGY_ID_PATH = TOPOLOGY_BASE_PATH + '/<topology_id>'
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_BASE_PATH, methods=['POST'])
|
||||
def create_topology():
|
||||
if not request.is_json:
|
||||
raise TopologyError('Request body is not json')
|
||||
topology_identifier = topology_service.save_topology(request.json)
|
||||
response = Result(message='Topology creation ok', description=topology_identifier)
|
||||
return json.dumps(response.__dict__), 201, {'location': TOPOLOGY_BASE_PATH + '/' + topology_identifier}
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_ID_PATH, methods=['PUT'])
|
||||
def update_topology(topology_id):
|
||||
if not request.is_json:
|
||||
raise TopologyError('Request body is not json')
|
||||
topology_identifier = topology_service.update_topology(request.json, topology_id)
|
||||
response = Result(message='Topology update ok', description=topology_identifier)
|
||||
return json.dumps(response.__dict__), http.HTTPStatus.OK, {
|
||||
'location': TOPOLOGY_BASE_PATH + '/' + topology_identifier}
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_ID_PATH, methods=['GET'])
|
||||
def get_topology(topology_id):
|
||||
return topology_service.get_topology(topology_id), http.HTTPStatus.OK
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_ID_PATH, methods=['DELETE'])
|
||||
def delete_topology(topology_id):
|
||||
topology_service.delete_topology(topology_id)
|
||||
return '', http.HTTPStatus.NO_CONTENT
|
||||
1
gnpy/api/service/__init__.py
Normal file
1
gnpy/api/service/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# coding: utf-8
|
||||
45
gnpy/api/service/config_service.py
Normal file
45
gnpy/api/service/config_service.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# coding: utf-8
|
||||
import configparser
|
||||
import os
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from gnpy.api.exception.config_error import ConfigError
|
||||
|
||||
|
||||
def init_config(properties_file_path: str = os.path.join(os.path.dirname(__file__),
|
||||
'properties.ini')) -> configparser.ConfigParser:
|
||||
"""
|
||||
Read config from properties_file_path
|
||||
@param properties_file_path: the properties file to read
|
||||
@return: config parser
|
||||
"""
|
||||
if not os.path.exists(properties_file_path):
|
||||
raise ConfigError('Properties file does not exist ' + properties_file_path)
|
||||
config = configparser.ConfigParser()
|
||||
config.read(properties_file_path)
|
||||
return config
|
||||
|
||||
|
||||
def get_topology_dir() -> str:
|
||||
"""
|
||||
Get the base dir where topologies are saved
|
||||
@return: the directory of topologies
|
||||
"""
|
||||
return current_app.config['properties'].get('DIRECTORY', 'topology')
|
||||
|
||||
|
||||
def get_equipment_dir() -> str:
|
||||
"""
|
||||
Get the base dir where equipments are saved
|
||||
@return: the directory of equipments
|
||||
"""
|
||||
return current_app.config['properties'].get('DIRECTORY', 'equipment')
|
||||
|
||||
|
||||
def get_autodesign_dir() -> str:
|
||||
"""
|
||||
Get the base dir where autodesign are saved
|
||||
@return: the directory of equipments
|
||||
"""
|
||||
return current_app.config['properties'].get('DIRECTORY', 'autodesign')
|
||||
13
gnpy/api/service/encryption_service.py
Normal file
13
gnpy/api/service/encryption_service.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# coding: utf-8
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
|
||||
class EncryptionService:
|
||||
def __init__(self, key):
|
||||
self._fernet = Fernet(key)
|
||||
|
||||
def encrypt(self, data):
|
||||
return self._fernet.encrypt(data)
|
||||
|
||||
def decrypt(self, data):
|
||||
return self._fernet.decrypt(data)
|
||||
66
gnpy/api/service/equipment_service.py
Normal file
66
gnpy/api/service/equipment_service.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# coding: utf-
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from injector import Inject
|
||||
|
||||
from gnpy.api.exception.equipment_error import EquipmentError
|
||||
from gnpy.api.service import config_service
|
||||
from gnpy.api.service.encryption_service import EncryptionService
|
||||
|
||||
|
||||
class EquipmentService:
|
||||
|
||||
def __init__(self, encryption_service: EncryptionService):
|
||||
self.encryption = encryption_service
|
||||
|
||||
def save_equipment(self, equipment):
|
||||
"""
|
||||
Save equipment to file.
|
||||
@param equipment: json content
|
||||
@return: a UUID identifier to identify the equipment
|
||||
"""
|
||||
equipment_identifier = str(uuid.uuid4())
|
||||
# TODO: validate json content
|
||||
self._write_equipment(equipment, equipment_identifier)
|
||||
return equipment_identifier
|
||||
|
||||
def update_equipment(self, equipment, equipment_identifier):
|
||||
"""
|
||||
Update equipment with identifier equipment_identifier.
|
||||
@param equipment_identifier: the identifier of the equipment to be updated
|
||||
@param equipment: json content
|
||||
@return: a UUID identifier to identify the equipment
|
||||
"""
|
||||
# TODO: validate json content
|
||||
self._write_equipment(equipment, equipment_identifier)
|
||||
return equipment_identifier
|
||||
|
||||
def _write_equipment(self, equipment, equipment_identifier):
|
||||
equipment_dir = config_service.get_equipment_dir()
|
||||
with(open(os.path.join(equipment_dir, '.'.join([equipment_identifier, 'json'])), 'wb')) as file:
|
||||
file.write(self.encryption.encrypt(json.dumps(equipment).encode()))
|
||||
|
||||
def get_equipment(self, equipment_id: str) -> dict:
|
||||
"""
|
||||
Get the equipment with id equipment_id
|
||||
@param equipment_id:
|
||||
@return: the equipment in json format
|
||||
"""
|
||||
equipment_dir = config_service.get_equipment_dir()
|
||||
equipment_file = os.path.join(equipment_dir, '.'.join([equipment_id, 'json']))
|
||||
if not os.path.exists(equipment_file):
|
||||
raise EquipmentError('Equipment with id {} does not exist '.format(equipment_id))
|
||||
with(open(equipment_file, 'rb')) as file:
|
||||
return json.loads(self.encryption.decrypt(file.read()))
|
||||
|
||||
def delete_equipment(self, equipment_id: str):
|
||||
"""
|
||||
Delete equipment with id equipment_id
|
||||
@param equipment_id:
|
||||
"""
|
||||
equipment_dir = config_service.get_equipment_dir()
|
||||
equipment_file = os.path.join(equipment_dir, '.'.join([equipment_id, 'json']))
|
||||
if os.path.exists(equipment_file):
|
||||
os.remove(equipment_file)
|
||||
100
gnpy/api/service/path_request_service.py
Normal file
100
gnpy/api/service/path_request_service.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import gnpy.core.ansi_escapes as ansi_escapes
|
||||
from gnpy.api.exception.path_computation_error import PathComputationError
|
||||
from gnpy.api.service import config_service
|
||||
from gnpy.api.service.encryption_service import EncryptionService
|
||||
from gnpy.core.network import build_network
|
||||
from gnpy.core.utils import lin2db, automatic_nch
|
||||
from gnpy.tools.json_io import requests_from_json, disjunctions_from_json, network_to_json
|
||||
from gnpy.topology.request import (compute_path_dsjctn, requests_aggregation,
|
||||
correct_json_route_list,
|
||||
deduplicate_disjunctions, compute_path_with_disjunction)
|
||||
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PathRequestService:
|
||||
|
||||
def __init__(self, encryption_service: EncryptionService):
|
||||
self.encryption = encryption_service
|
||||
|
||||
def path_requests_run(self, service, network, equipment):
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
path_computation_identifier = str(uuid.uuid4())
|
||||
autodesign_dir = config_service.get_autodesign_dir()
|
||||
with(open(os.path.join(autodesign_dir, '.'.join([path_computation_identifier, 'json'])), 'wb')) as file:
|
||||
file.write(self.encryption.encrypt(json.dumps(network_to_json(network)).encode()))
|
||||
oms_list = build_oms_list(network, equipment)
|
||||
rqs = requests_from_json(service, equipment)
|
||||
|
||||
# check that request ids are unique. Non unique ids, may
|
||||
# mess the computation: better to stop the computation
|
||||
all_ids = [r.request_id for r in rqs]
|
||||
if len(all_ids) != len(set(all_ids)):
|
||||
for item in list(set(all_ids)):
|
||||
all_ids.remove(item)
|
||||
msg = f'Requests id {all_ids} are not unique'
|
||||
_logger.critical(msg)
|
||||
raise ValueError('Requests id ' + all_ids + ' are not unique')
|
||||
rqs = correct_json_route_list(network, rqs)
|
||||
|
||||
# pths = compute_path(network, equipment, rqs)
|
||||
dsjn = disjunctions_from_json(service)
|
||||
|
||||
# need to warn or correct in case of wrong disjunction form
|
||||
# disjunction must not be repeated with same or different ids
|
||||
dsjn = deduplicate_disjunctions(dsjn)
|
||||
|
||||
rqs, dsjn = requests_aggregation(rqs, dsjn)
|
||||
# TODO export novel set of aggregated demands in a json file
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}The following services have been requested:{ansi_escapes.reset}' + str(rqs))
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}Computing all paths with constraints{ansi_escapes.reset}')
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}Propagating on selected path{ansi_escapes.reset}')
|
||||
propagatedpths, reversed_pths, reversed_propagatedpths = compute_path_with_disjunction(network, equipment, rqs,
|
||||
pths)
|
||||
# Note that deepcopy used in compute_path_with_disjunction returns
|
||||
# a list of nodes which are not belonging to network (they are copies of the node objects).
|
||||
# so there can not be propagation on these nodes.
|
||||
|
||||
pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
|
||||
return propagatedpths, reversed_propagatedpths, rqs, path_computation_identifier
|
||||
|
||||
def get_autodesign(self, path_computation_id):
|
||||
"""
|
||||
Get the autodesign with id topology_id
|
||||
@param path_computation_id:
|
||||
@return: the autodesign in json format
|
||||
"""
|
||||
autodesign_dir = config_service.get_autodesign_dir()
|
||||
autodesign_file = os.path.join(autodesign_dir, '.'.join([path_computation_id, 'json']))
|
||||
if not os.path.exists(autodesign_file):
|
||||
raise PathComputationError('Autodesign with id {} does not exist '.format(path_computation_id))
|
||||
with(open(autodesign_file, 'rb')) as file:
|
||||
return json.loads(self.encryption.decrypt(file.read()))
|
||||
|
||||
def delete_autodesign(self, path_computation_id: str):
|
||||
"""
|
||||
Delete autodesign with id equipment_id
|
||||
@param path_computation_id:
|
||||
"""
|
||||
autodesign_dir = config_service.get_autodesign_dir()
|
||||
autodesign_file = os.path.join(autodesign_dir, '.'.join([path_computation_id, 'json']))
|
||||
if os.path.exists(autodesign_file):
|
||||
os.remove(autodesign_file)
|
||||
4
gnpy/api/service/properties.ini
Normal file
4
gnpy/api/service/properties.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[DIRECTORY]
|
||||
topology: /opt/application/oopt-gnpy/topology
|
||||
equipment: /opt/application/oopt-gnpy/equipment
|
||||
autodesign: /opt/application/oopt-gnpy/autodesign
|
||||
62
gnpy/api/service/topology_service.py
Normal file
62
gnpy/api/service/topology_service.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# coding: utf-
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.service import config_service
|
||||
|
||||
|
||||
def save_topology(topology):
|
||||
"""
|
||||
Save topology to file.
|
||||
@param topology: json content
|
||||
@return: a UUID identifier to identify the topology
|
||||
"""
|
||||
topology_identifier = str(uuid.uuid4())
|
||||
# TODO: validate json content
|
||||
_write_topology(topology, topology_identifier)
|
||||
return topology_identifier
|
||||
|
||||
|
||||
def update_topology(topology, topology_identifier):
|
||||
"""
|
||||
Update topology with identifier topology_identifier.
|
||||
@param topology_identifier: the identifier of the topology to be updated
|
||||
@param topology: json content
|
||||
@return: a UUID identifier to identify the topology
|
||||
"""
|
||||
# TODO: validate json content
|
||||
_write_topology(topology, topology_identifier)
|
||||
return topology_identifier
|
||||
|
||||
|
||||
def _write_topology(topology, topology_identifier):
|
||||
topology_dir = config_service.get_topology_dir()
|
||||
with(open(os.path.join(topology_dir, '.'.join([topology_identifier, 'json'])), 'w')) as file:
|
||||
json.dump(topology, file)
|
||||
|
||||
|
||||
def get_topology(topology_id: str) -> dict:
|
||||
"""
|
||||
Get the topology with id topology_id
|
||||
@param topology_id:
|
||||
@return: the topology in json format
|
||||
"""
|
||||
topology_dir = config_service.get_topology_dir()
|
||||
topology_file = os.path.join(topology_dir, '.'.join([topology_id, 'json']))
|
||||
if not os.path.exists(topology_file):
|
||||
raise TopologyError('Topology with id {} does not exist '.format(topology_id))
|
||||
with(open(topology_file, 'r')) as file:
|
||||
return json.load(file)
|
||||
|
||||
|
||||
def delete_topology(topology_id: str):
|
||||
"""
|
||||
Delete topology with id topology_id
|
||||
@param topology_id:
|
||||
"""
|
||||
topology_dir = config_service.get_topology_dir()
|
||||
topology_file = os.path.join(topology_dir, '.'.join([topology_id, 'json']))
|
||||
if os.path.exists(topology_file):
|
||||
os.remove(topology_file)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.tools.rest_example
|
||||
=======================
|
||||
|
||||
GNPy as a rest API example
|
||||
'''
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
import werkzeug
|
||||
from flask import Flask, request
|
||||
from numpy import mean
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import gnpy.core.ansi_escapes as ansi_escapes
|
||||
import gnpy.core.exceptions as exceptions
|
||||
from gnpy.core.network import build_network
|
||||
from gnpy.core.utils import lin2db, automatic_nch
|
||||
from gnpy.tools.json_io import requests_from_json, disjunctions_from_json, _equipment_from_json, network_from_json
|
||||
from gnpy.topology.request import (ResultElement, compute_path_dsjctn, requests_aggregation,
|
||||
correct_json_route_list,
|
||||
deduplicate_disjunctions, compute_path_with_disjunction)
|
||||
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
_examples_dir = Path(__file__).parent.parent / 'example-data'
|
||||
_reaesc = re.compile(r'\x1b[^m]*m')
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route('/api/v1/path-computation', methods=['POST'])
|
||||
def compute_path():
|
||||
data = request.json
|
||||
service = data['gnpy-api:service']
|
||||
topology = data['gnpy-api:topology']
|
||||
equipment = _equipment_from_json(data['gnpy-api:equipment'],
|
||||
os.path.join(_examples_dir, 'std_medium_gain_advanced_config.json'))
|
||||
network = network_from_json(topology, equipment)
|
||||
|
||||
propagatedpths, reversed_propagatedpths, rqs = path_requests_run(service, network, equipment)
|
||||
# Generate the output
|
||||
result = []
|
||||
# assumes that list of rqs and list of propgatedpths have same order
|
||||
for i, pth in enumerate(propagatedpths):
|
||||
result.append(ResultElement(rqs[i], pth, reversed_propagatedpths[i]))
|
||||
return {"result": {"response": [n.json for n in result]}}, 201
|
||||
|
||||
|
||||
@app.route('/api/v1/status', methods=['GET'])
|
||||
def api_status():
|
||||
return {"version": "v1", "status": "ok"}, 200
|
||||
|
||||
|
||||
def _init_logger():
|
||||
handler = RotatingFileHandler('api.log', maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
|
||||
ch = logging.StreamHandler()
|
||||
logging.basicConfig(level=logging.INFO, handlers=[handler, ch],
|
||||
format="%(asctime)s %(levelname)s %(name)s(%(lineno)s) [%(threadName)s - %(thread)d] - %("
|
||||
"message)s")
|
||||
|
||||
|
||||
def path_requests_run(service, network, equipment):
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
oms_list = build_oms_list(network, equipment)
|
||||
rqs = requests_from_json(service, equipment)
|
||||
|
||||
# check that request ids are unique. Non unique ids, may
|
||||
# mess the computation: better to stop the computation
|
||||
all_ids = [r.request_id for r in rqs]
|
||||
if len(all_ids) != len(set(all_ids)):
|
||||
for item in list(set(all_ids)):
|
||||
all_ids.remove(item)
|
||||
msg = f'Requests id {all_ids} are not unique'
|
||||
_logger.critical(msg)
|
||||
raise ValueError('Requests id ' + all_ids + ' are not unique')
|
||||
rqs = correct_json_route_list(network, rqs)
|
||||
|
||||
# pths = compute_path(network, equipment, rqs)
|
||||
dsjn = disjunctions_from_json(service)
|
||||
|
||||
# need to warn or correct in case of wrong disjunction form
|
||||
# disjunction must not be repeated with same or different ids
|
||||
dsjn = deduplicate_disjunctions(dsjn)
|
||||
|
||||
rqs, dsjn = requests_aggregation(rqs, dsjn)
|
||||
# TODO export novel set of aggregated demands in a json file
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}The following services have been requested:{ansi_escapes.reset}' + str(rqs))
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}Computing all paths with constraints{ansi_escapes.reset}')
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}Propagating on selected path{ansi_escapes.reset}')
|
||||
propagatedpths, reversed_pths, reversed_propagatedpths = compute_path_with_disjunction(network, equipment, rqs,
|
||||
pths)
|
||||
# Note that deepcopy used in compute_path_with_disjunction returns
|
||||
# a list of nodes which are not belonging to network (they are copies of the node objects).
|
||||
# so there can not be propagation on these nodes.
|
||||
|
||||
pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
|
||||
return propagatedpths, reversed_propagatedpths, rqs
|
||||
|
||||
|
||||
def common_error_handler(exception):
|
||||
"""
|
||||
|
||||
:type exception: Exception
|
||||
|
||||
"""
|
||||
status_code = 500
|
||||
if not isinstance(exception, werkzeug.exceptions.HTTPException):
|
||||
exception = werkzeug.exceptions.InternalServerError()
|
||||
exception.description = "Something went wrong on our side."
|
||||
|
||||
response = {
|
||||
'message': exception.name,
|
||||
'description': exception.description,
|
||||
'code': exception.code
|
||||
}
|
||||
|
||||
return werkzeug.Response(response=json.dumps(response), status=status_code, mimetype='application/json')
|
||||
|
||||
|
||||
def bad_request_handler(exception):
|
||||
response = {
|
||||
'message': 'bad request',
|
||||
'description': _reaesc.sub('', str(exception)),
|
||||
'code': 400
|
||||
}
|
||||
return werkzeug.Response(response=json.dumps(response), status=400, mimetype='application/json')
|
||||
|
||||
|
||||
def _init_app():
|
||||
app.register_error_handler(KeyError, bad_request_handler)
|
||||
app.register_error_handler(TypeError, bad_request_handler)
|
||||
app.register_error_handler(ValueError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ConfigurationError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.DisjunctionError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.EquipmentConfigError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.NetworkTopologyError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ServiceError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.SpectrumError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ParametersError, bad_request_handler)
|
||||
app.register_error_handler(AssertionError, bad_request_handler)
|
||||
app.register_error_handler(InternalServerError, common_error_handler)
|
||||
for error_code in werkzeug.exceptions.default_exceptions:
|
||||
app.register_error_handler(error_code, common_error_handler)
|
||||
|
||||
|
||||
def main():
|
||||
_init_logger()
|
||||
_init_app()
|
||||
app.run(host='0.0.0.0', port=8080)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
310
gnpy/yang/api-equipment.json
Normal file
310
gnpy/yang/api-equipment.json
Normal file
@@ -0,0 +1,310 @@
|
||||
{
|
||||
"Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25.0,
|
||||
"gain_min": 15.0,
|
||||
"p_max": 21.0,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
}, {
|
||||
"type_variety": "Juniper_BoosterHG",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25.0,
|
||||
"gain_min": 10.0,
|
||||
"p_max": 21.0,
|
||||
"advanced_config_from_json": "Juniper-BoosterHG.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "operator_model_example",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26.0,
|
||||
"gain_min": 15.0,
|
||||
"p_max": 23.0,
|
||||
"nf_min": 6.0,
|
||||
"nf_max": 10.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "low_noise",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27.0,
|
||||
"gain_min": 12.0,
|
||||
"p_max": 22.0,
|
||||
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "standard",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27.0,
|
||||
"gain_min": 12.0,
|
||||
"p_max": 22.0,
|
||||
"nf_coef": [-5.952e-4,-6.250e-2,-1.071,28.99],
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_high_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 35.0,
|
||||
"gain_min": 25.0,
|
||||
"p_max": 21.0,
|
||||
"nf_min": 5.5,
|
||||
"nf_max": 7.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26.0,
|
||||
"gain_min": 15.0,
|
||||
"p_max": 23.0,
|
||||
"nf_min": 6.0,
|
||||
"nf_max": 10.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16.0,
|
||||
"gain_min": 8.0,
|
||||
"p_max": 23.0,
|
||||
"nf_min": 6.5,
|
||||
"nf_max": 11.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "high_power",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16.0,
|
||||
"gain_min": 8.0,
|
||||
"p_max": 25.0,
|
||||
"nf_min": 9.0,
|
||||
"nf_max": 15.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21.0,
|
||||
"gain_min": 20.0,
|
||||
"p_max": 21.0,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "4pumps_raman",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 12.0,
|
||||
"gain_min": 12.0,
|
||||
"p_max": 21.0,
|
||||
"nf0": -1.0,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_lowgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_mediumgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_medium_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+low_gain",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+high_power",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "high_power",
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127,
|
||||
"pmd_coef": 1.265e-15
|
||||
},
|
||||
{
|
||||
"type_variety": "NZDF",
|
||||
"dispersion": 0.5e-05,
|
||||
"gamma": 0.00146,
|
||||
"pmd_coef": 1.265e-15
|
||||
},
|
||||
{
|
||||
"type_variety": "LOF",
|
||||
"dispersion": 2.2e-05,
|
||||
"gamma": 0.000843,
|
||||
"pmd_coef": 1.265e-15
|
||||
}
|
||||
],
|
||||
"RamanFiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127,
|
||||
"pmd_coef": 1.265e-15,
|
||||
"raman_efficiency": {
|
||||
"cr":[
|
||||
0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
|
||||
0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
|
||||
0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
|
||||
0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
|
||||
7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
|
||||
2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
|
||||
2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
|
||||
1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
|
||||
1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
|
||||
2E-07, 1E-07
|
||||
],
|
||||
"frequency_offset":[
|
||||
0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
|
||||
7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
|
||||
13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
|
||||
17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
|
||||
22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
|
||||
28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
|
||||
35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
|
||||
41.5e12, 42e12
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"Span":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [-2.0, 3.0, 0.5],
|
||||
"max_fiber_lineic_loss_for_raman": 0.25,
|
||||
"target_extended_gain": 2.5,
|
||||
"max_length": 150.0,
|
||||
"length_units": "km",
|
||||
"max_loss": 28.0,
|
||||
"padding": 10.0,
|
||||
"EOL": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
}
|
||||
],
|
||||
"Roadm":[{
|
||||
"target_pch_out_db": -20.0,
|
||||
"add_drop_osnr": 38.0,
|
||||
"pmd": 0.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
"baud_rate": 32e9,
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0.0,
|
||||
"power_range_db": [0.0,0.0,1.0],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"sys_margins": 2.0
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
"type_variety": "vendorA_trx-type1",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11.0,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15.0,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12.0,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 3",
|
||||
"baud_rate": 44e9,
|
||||
"OSNR": 18.0,
|
||||
"bit_rate": 300e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 62.5e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 21.0,
|
||||
"bit_rate": 400e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 4",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 16.0,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
180
gnpy/yang/api-request-with-id.json
Normal file
180
gnpy/yang/api-request-with-id.json
Normal file
@@ -0,0 +1,180 @@
|
||||
{
|
||||
"gnpy-api:service":{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": 0,
|
||||
"M": 12
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "2",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 2",
|
||||
"spacing": 100000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Carol",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": -284,
|
||||
"M": 12
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "trx Bob1",
|
||||
"destination": "trx Carol1",
|
||||
"src-tp-id": "trx Bob1",
|
||||
"dst-tp-id": "trx Carol1",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"spacing": 100000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "6",
|
||||
"source": "trx Bob1",
|
||||
"destination": "trx Carol1",
|
||||
"src-tp-id": "trx Bob1",
|
||||
"dst-tp-id": "trx Carol1",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "7",
|
||||
"source": "trx Bob1",
|
||||
"destination": "trx Carol",
|
||||
"src-tp-id": "trx Bob1",
|
||||
"dst-tp-id": "trx Carol",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": "1",
|
||||
"svec": {
|
||||
"relaxable": false,
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"gnpy-api:topology_id": "5cf39d4b-be10-4ee9-b38b-7f4db7403db7",
|
||||
"gnpy-api:equipment_id": "9ed86e34-9d41-41b2-b8e4-984ca0901d47"
|
||||
|
||||
}
|
||||
901
gnpy/yang/api-topology.json
Normal file
901
gnpy/yang/api-topology.json
Normal file
@@ -0,0 +1,901 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Alice",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Bob",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Carol",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Bob1",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Carol1",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Alice",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Bob",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Carol",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Bob1",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Carol1",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Alice → Bob)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 75.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Bob → Carol)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Bob1 → Carol1)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.5,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Carol → Dan)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 83.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Dan → Alice)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Bob → Alice)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 75.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Carol → Bob)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Carol1 → Bob1)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.5,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Dan → Carol)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 83.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Alice → Dan)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Alice to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.5,
|
||||
"delta_p": -1.5,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Bob to Carol",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Bob1 to Carol1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Carol to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Dan to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 15.600000000000001,
|
||||
"delta_p": -2.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Dan",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Bob to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.5,
|
||||
"delta_p": -1.5,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Alice to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": -2.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Carol to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Carol1 to Bob1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Alice to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.5,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Bob to Carol",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Bob1 to Carol1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Carol to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.6,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Dan to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 13.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Dan",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Bob to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.5,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Alice to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 14.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Carol to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Carol1 to Bob1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "trx Alice",
|
||||
"to_node": "roadm Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Bob",
|
||||
"to_node": "roadm Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Bob1",
|
||||
"to_node": "roadm Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Carol",
|
||||
"to_node": "roadm Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Carol1",
|
||||
"to_node": "roadm Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Alice",
|
||||
"to_node": "east edfa in Alice to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Alice",
|
||||
"to_node": "east edfa in Alice to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Alice",
|
||||
"to_node": "trx Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob",
|
||||
"to_node": "east edfa in Bob to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob1",
|
||||
"to_node": "east edfa in Bob1 to Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob",
|
||||
"to_node": "east edfa in Bob to Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob",
|
||||
"to_node": "trx Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob1",
|
||||
"to_node": "trx Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol",
|
||||
"to_node": "east edfa in Carol to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol1",
|
||||
"to_node": "east edfa in Carol1 to Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol",
|
||||
"to_node": "east edfa in Carol to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol",
|
||||
"to_node": "trx Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol1",
|
||||
"to_node": "trx Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Alice → Bob)-",
|
||||
"to_node": "west edfa in Bob to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Bob → Carol)-",
|
||||
"to_node": "west edfa in Carol to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Bob1 → Carol1)-",
|
||||
"to_node": "west edfa in Carol1 to Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Carol → Dan)-",
|
||||
"to_node": "east edfa in Dan to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Dan → Alice)-",
|
||||
"to_node": "west edfa in Alice to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Bob → Alice)-",
|
||||
"to_node": "west edfa in Alice to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Carol → Bob)-",
|
||||
"to_node": "west edfa in Bob to Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Carol1 → Bob1)-",
|
||||
"to_node": "west edfa in Bob1 to Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Dan → Carol)-",
|
||||
"to_node": "west edfa in Carol to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Alice → Dan)-",
|
||||
"to_node": "west edfa in Dan to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Alice to Bob",
|
||||
"to_node": "fiber (Alice → Bob)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Bob to Carol",
|
||||
"to_node": "fiber (Bob → Carol)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Bob1 to Carol1",
|
||||
"to_node": "fiber (Bob1 → Carol1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Carol to Dan",
|
||||
"to_node": "fiber (Carol → Dan)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Dan to Alice",
|
||||
"to_node": "fiber (Dan → Alice)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Bob to Alice",
|
||||
"to_node": "fiber (Bob → Alice)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Alice to Dan",
|
||||
"to_node": "fiber (Alice → Dan)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Carol to Bob",
|
||||
"to_node": "fiber (Carol → Bob)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Carol1 to Bob1",
|
||||
"to_node": "fiber (Carol1 → Bob1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Alice to Bob",
|
||||
"to_node": "roadm Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob to Carol",
|
||||
"to_node": "roadm Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob1 to Carol1",
|
||||
"to_node": "roadm Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob1 to Carol1",
|
||||
"to_node": "roadm Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Carol to Dan",
|
||||
"to_node": "roadm Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Dan to Alice",
|
||||
"to_node": "fiber (Dan → Carol)-"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob to Alice",
|
||||
"to_node": "roadm Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Alice to Dan",
|
||||
"to_node": "roadm Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Carol to Bob",
|
||||
"to_node": "roadm Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Carol1 to Bob1",
|
||||
"to_node": "roadm Carol1"
|
||||
}
|
||||
]
|
||||
}
|
||||
78
gnpy/yang/gnpy-api@2021-01-06.yang
Normal file
78
gnpy/yang/gnpy-api@2021-01-06.yang
Normal file
@@ -0,0 +1,78 @@
|
||||
module gnpy-api {
|
||||
yang-version 1.1;
|
||||
namespace "gnpy:gnpy-api";
|
||||
prefix gnpyapi;
|
||||
|
||||
import gnpy-network-topology {
|
||||
prefix gnpynt;
|
||||
}
|
||||
import gnpy-path-computation-simplified {
|
||||
prefix gnpypc;
|
||||
}
|
||||
import gnpy-eqpt-config {
|
||||
prefix gnpyeqpt;
|
||||
}
|
||||
import ietf-yang-types {
|
||||
prefix ietftypes;
|
||||
}
|
||||
|
||||
organization
|
||||
"Telecom Infra Project OOPT PSE Working Group";
|
||||
contact
|
||||
"WG Web: <https://github.com/Telecominfraproject/oopt-gnpy>
|
||||
contact: <mailto:ahmed.triki@orange.com>
|
||||
contact: <mailto:esther.lerouzic@orange.com>
|
||||
";
|
||||
description
|
||||
"YANG model for gnpy api input for path computation - TransportPCE preversion";
|
||||
|
||||
revision 2021-01-06 {
|
||||
description
|
||||
"draft for experimental/2020-candi.
|
||||
Add the possibility to use a topology_id or an equipment_id
|
||||
";
|
||||
reference
|
||||
"YANG model for api input for path computation with gnpy";
|
||||
}
|
||||
|
||||
container service {
|
||||
description
|
||||
"Describe the service file to connect to gnpy";
|
||||
uses gnpypc:service;
|
||||
}
|
||||
container result {
|
||||
uses gnpypc:result;
|
||||
description
|
||||
"Describe the response object to gnpy";
|
||||
}
|
||||
choice topo {
|
||||
case explicit {
|
||||
container topology {
|
||||
description
|
||||
"Describe the topology file to connect to gnpy";
|
||||
uses gnpynt:topo;
|
||||
}
|
||||
}
|
||||
case id {
|
||||
leaf topology_id {
|
||||
type ietftypes:uuid;
|
||||
mandatory true;
|
||||
}
|
||||
}
|
||||
}
|
||||
choice eqpt {
|
||||
case explicit {
|
||||
container equipment {
|
||||
description
|
||||
"Describe the equipment library to connect to gnpy";
|
||||
uses gnpyeqpt:eqpt;
|
||||
}
|
||||
}
|
||||
case id {
|
||||
leaf equipment_id {
|
||||
type ietftypes:uuid;
|
||||
mandatory true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,9 @@ scipy>=1.3.0,<2
|
||||
Sphinx>=2.4.4,<3
|
||||
sphinxcontrib-bibtex>=0.4.2,<1
|
||||
xlrd>=1.2.0,<2
|
||||
flask>=1.1.2
|
||||
flask>=1.1.2
|
||||
gnpy~=2.2.0
|
||||
cryptography~=3.3.1
|
||||
Werkzeug~=1.0.1
|
||||
setuptools~=50.3.1
|
||||
Flask-Injector
|
||||
@@ -48,4 +48,4 @@ console_scripts =
|
||||
gnpy-transmission-example = gnpy.tools.cli_examples:transmission_main_example
|
||||
gnpy-path-request = gnpy.tools.cli_examples:path_requests_run
|
||||
gnpy-convert-xls = gnpy.tools.convert:_do_convert
|
||||
gnpy-rest = gnpy.tools.rest_example:main
|
||||
gnpy-rest = gnpy.api.rest_example:main
|
||||
|
||||
Reference in New Issue
Block a user