Skip to content
Snippets Groups Projects
Commit 82fee8a3 authored by Pablo Armingol's avatar Pablo Armingol
Browse files

code cleanup

parent 7d4ee1e6
No related branches found
No related tags found
2 merge requests!235Release TeraFlowSDN 3.0,!151Resolve "(TID) New BGP-LS Speaker component"
Showing
with 3 additions and 8428 deletions
This diff is collapsed.
This diff is collapsed.
......@@ -36,7 +36,7 @@ spec:
- containerPort: 9192
env:
- name: LOG_LEVEL
value: "DEBUG"
value: "INFO"
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:20030"]
......
This diff is collapsed.
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
DEFAULT_MTU = 1512
DEFAULT_ADDRESS_FAMILIES = ['IPV4']
DEFAULT_BGP_AS = 65000
DEFAULT_BGP_ROUTE_TARGET = '{:d}:{:d}'.format(DEFAULT_BGP_AS, 333)
# TODO: improve definition of bearer mappings
# Bearer mappings:
# device_uuid:endpoint_uuid => (
# device_uuid, endpoint_uuid, router_id, route_dist, sub_if_index,
# address_ip, address_prefix, remote_router, circuit_id)
BEARER_MAPPINGS = {
# OFC'22
'R1-EMU:13/1/2': ('R1-EMU', '13/1/2', '10.10.10.1', '65000:100', 400, '3.3.2.1', 24, None, None),
'R2-EMU:13/1/2': ('R2-EMU', '13/1/2', '12.12.12.1', '65000:120', 450, '3.4.2.1', 24, None, None),
'R3-EMU:13/1/2': ('R3-EMU', '13/1/2', '20.20.20.1', '65000:200', 500, '3.3.1.1', 24, None, None),
'R4-EMU:13/1/2': ('R4-EMU', '13/1/2', '22.22.22.1', '65000:220', 550, '3.4.1.1', 24, None, None),
# OECC/PSC'22 - domain 1
'R1@D1:3/1' : ('R1@D1', '3/1', '10.0.1.1', '65001:101', 100, '1.1.3.1', 24, None, None),
'R1@D1:3/2' : ('R1@D1', '3/2', '10.0.1.1', '65001:101', 100, '1.1.3.2', 24, None, None),
'R1@D1:3/3' : ('R1@D1', '3/3', '10.0.1.1', '65001:101', 100, '1.1.3.3', 24, None, None),
'R2@D1:3/1' : ('R2@D1', '3/1', '10.0.1.2', '65001:102', 100, '1.2.3.1', 24, None, None),
'R2@D1:3/2' : ('R2@D1', '3/2', '10.0.1.2', '65001:102', 100, '1.2.3.2', 24, None, None),
'R2@D1:3/3' : ('R2@D1', '3/3', '10.0.1.2', '65001:102', 100, '1.2.3.3', 24, None, None),
'R3@D1:3/1' : ('R3@D1', '3/1', '10.0.1.3', '65001:103', 100, '1.3.3.1', 24, None, None),
'R3@D1:3/2' : ('R3@D1', '3/2', '10.0.1.3', '65001:103', 100, '1.3.3.2', 24, None, None),
'R3@D1:3/3' : ('R3@D1', '3/3', '10.0.1.3', '65001:103', 100, '1.3.3.3', 24, None, None),
'R4@D1:3/1' : ('R4@D1', '3/1', '10.0.1.4', '65001:104', 100, '1.4.3.1', 24, None, None),
'R4@D1:3/2' : ('R4@D1', '3/2', '10.0.1.4', '65001:104', 100, '1.4.3.2', 24, None, None),
'R4@D1:3/3' : ('R4@D1', '3/3', '10.0.1.4', '65001:104', 100, '1.4.3.3', 24, None, None),
# OECC/PSC'22 - domain 2
'R1@D2:3/1' : ('R1@D2', '3/1', '10.0.2.1', '65002:101', 100, '2.1.3.1', 24, None, None),
'R1@D2:3/2' : ('R1@D2', '3/2', '10.0.2.1', '65002:101', 100, '2.1.3.2', 24, None, None),
'R1@D2:3/3' : ('R1@D2', '3/3', '10.0.2.1', '65002:101', 100, '2.1.3.3', 24, None, None),
'R2@D2:3/1' : ('R2@D2', '3/1', '10.0.2.2', '65002:102', 100, '2.2.3.1', 24, None, None),
'R2@D2:3/2' : ('R2@D2', '3/2', '10.0.2.2', '65002:102', 100, '2.2.3.2', 24, None, None),
'R2@D2:3/3' : ('R2@D2', '3/3', '10.0.2.2', '65002:102', 100, '2.2.3.3', 24, None, None),
'R3@D2:3/1' : ('R3@D2', '3/1', '10.0.2.3', '65002:103', 100, '2.3.3.1', 24, None, None),
'R3@D2:3/2' : ('R3@D2', '3/2', '10.0.2.3', '65002:103', 100, '2.3.3.2', 24, None, None),
'R3@D2:3/3' : ('R3@D2', '3/3', '10.0.2.3', '65002:103', 100, '2.3.3.3', 24, None, None),
'R4@D2:3/1' : ('R4@D2', '3/1', '10.0.2.4', '65002:104', 100, '2.4.3.1', 24, None, None),
'R4@D2:3/2' : ('R4@D2', '3/2', '10.0.2.4', '65002:104', 100, '2.4.3.2', 24, None, None),
'R4@D2:3/3' : ('R4@D2', '3/3', '10.0.2.4', '65002:104', 100, '2.4.3.3', 24, None, None),
# ECOC'22
'DC1-GW:CS1-GW1': ('CS1-GW1', '10/1', '5.5.1.1', None, 0, None, None, '5.5.2.1', 111),
'DC1-GW:CS1-GW2': ('CS1-GW2', '10/1', '5.5.1.2', None, 0, None, None, '5.5.2.2', 222),
'DC2-GW:CS2-GW1': ('CS2-GW1', '10/1', '5.5.2.1', None, 0, None, None, '5.5.1.1', 111),
'DC2-GW:CS2-GW2': ('CS2-GW2', '10/1', '5.5.2.2', None, 0, None, None, '5.5.1.2', 222),
# NetworkX'22
'R1:1/2': ('R1', '1/2', '5.1.1.2', None, 0, None, None, None, None),
'R1:1/3': ('R1', '1/3', '5.1.1.3', None, 0, None, None, None, None),
'R2:1/2': ('R2', '1/2', '5.2.1.2', None, 0, None, None, None, None),
'R2:1/3': ('R2', '1/3', '5.2.1.3', None, 0, None, None, None, None),
'R3:1/2': ('R3', '1/2', '5.3.1.2', None, 0, None, None, None, None),
'R3:1/3': ('R3', '1/3', '5.3.1.3', None, 0, None, None, None, None),
'R4:1/2': ('R4', '1/2', '5.4.1.2', None, 0, None, None, None, None),
'R4:1/3': ('R4', '1/3', '5.4.1.3', None, 0, None, None, None, None),
}
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from flask import request
from flask.json import jsonify
from flask_restful import Resource
from common.proto.context_pb2 import SliceStatusEnum
from common.tools.context_queries.Slice import get_slice
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from .tools.Authentication import HTTP_AUTH
from .tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
LOGGER = logging.getLogger(__name__)
class Topology_Service(Resource):
@HTTP_AUTH.login_required
def get(self, vpn_id : str):
LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id)))
LOGGER.debug('Request: {:s}'.format(str(request)))
try:
context_client = ContextClient()
target = get_slice(context_client, vpn_id, rw_copy=True)
if target is None:
raise Exception('VPN({:s}) not found in database'.format(str(vpn_id)))
if target.slice_id.slice_uuid.uuid != vpn_id: # pylint: disable=no-member
raise Exception('Slice retrieval failed. Wrong Slice Id was returned')
slice_ready_status = SliceStatusEnum.SLICESTATUS_ACTIVE
slice_status = target.slice_status.slice_status # pylint: disable=no-member
response = jsonify({})
response.status_code = HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Something went wrong Retrieving VPN({:s})'.format(str(vpn_id)))
response = jsonify({'error': str(e)})
response.status_code = HTTP_SERVERERROR
return response
@HTTP_AUTH.login_required
def delete(self, vpn_id : str):
LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id)))
LOGGER.debug('Request: {:s}'.format(str(request)))
try:
context_client = ContextClient()
target = get_slice(context_client, vpn_id)
if target is None:
LOGGER.warning('VPN({:s}) not found in database. Nothing done.'.format(str(vpn_id)))
else:
if target.slice_id.slice_uuid.uuid != vpn_id: # pylint: disable=no-member
raise Exception('Slice retrieval failed. Wrong Slice Id was returned')
slice_client = SliceClient()
slice_client.DeleteSlice(target.slice_id)
response = jsonify({})
response.status_code = HTTP_NOCONTENT
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Something went wrong Deleting VPN({:s})'.format(str(vpn_id)))
response = jsonify({'error': str(e)})
response.status_code = HTTP_SERVERERROR
return response
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import Dict, List
from common.tools.grpc.Tools import grpc_message_list_to_json_string, grpc_message_to_json_string
from compute.service.rest_server.nbi_plugins.debug_api.Tools import format_grpc_to_json, grpc_topology_id
from context.client.ContextClient import ContextClient
from flask import request
from flask.json import jsonify
from flask_restful import Resource
from werkzeug.exceptions import UnsupportedMediaType
from common.Constants import DEFAULT_CONTEXT_NAME
from common.proto.context_pb2 import Empty, SliceStatusEnum, Slice
from slice.client.SliceClient import SliceClient
from .schemas.vpn_service import SCHEMA_VPN_SERVICE
from .tools.Authentication import HTTP_AUTH
from .tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR
from .tools.Validator import validate_message
LOGGER = logging.getLogger(__name__)
class Topology_Services(Resource):
@HTTP_AUTH.login_required
def get(self):
context_client=ContextClient()
context_client.connect()
ctx_ids=context_client.ListContextIds(Empty())
topos=[]
for ctx in ctx_ids.context_ids:
topo_ids=context_client.ListTopologyIds(ctx)
context_uuid=ctx
for ids in topo_ids.topology_ids:
topos.append(context_client.GetTopology(ids))
topology_uuid=ids
LOGGER.info("nbi topo %s",topos)
LOGGER.info("nbi topo JSON %s",grpc_message_list_to_json_string(topos))
response=format_grpc_to_json(context_client.GetTopologyDetails(topology_uuid))
context_client.close()
return response
@HTTP_AUTH.login_required
def post(self):
if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
request_data : Dict = request.json
LOGGER.debug('Request: {:s}'.format(str(request_data)))
validate_message(SCHEMA_VPN_SERVICE, request_data)
vpn_services : List[Dict] = request_data['ietf-l2vpn-svc:vpn-service']
for vpn_service in vpn_services:
try:
# pylint: disable=no-member
slice_request = Slice()
slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME
slice_request.slice_id.slice_uuid.uuid = vpn_service['vpn-id']
slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED
slice_client = SliceClient()
slice_client.CreateSlice(slice_request)
response = jsonify({})
response.status_code = HTTP_CREATED
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Something went wrong Creating Service {:s}'.format(str(request)))
response = jsonify({'error': str(e)})
response.status_code = HTTP_SERVERERROR
return response
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import Dict, Optional
from flask import request
from flask.json import jsonify
from flask.wrappers import Response
from flask_restful import Resource
from werkzeug.exceptions import UnsupportedMediaType
from common.proto.context_pb2 import Slice
from common.tools.context_queries.Slice import get_slice
from common.tools.grpc.ConfigRules import update_config_rule_custom
from common.tools.grpc.Constraints import (
update_constraint_custom_dict, update_constraint_endpoint_location, update_constraint_endpoint_priority,
update_constraint_sla_availability)
from common.tools.grpc.EndPointIds import update_endpoint_ids
from common.tools.grpc.Tools import grpc_message_to_json_string
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from .schemas.site_network_access import SCHEMA_SITE_NETWORK_ACCESS
from .tools.Authentication import HTTP_AUTH
from .tools.HttpStatusCodes import HTTP_NOCONTENT, HTTP_SERVERERROR
from .tools.Validator import validate_message
from .Constants import (
BEARER_MAPPINGS, DEFAULT_ADDRESS_FAMILIES, DEFAULT_BGP_AS, DEFAULT_BGP_ROUTE_TARGET, DEFAULT_MTU)
LOGGER = logging.getLogger(__name__)
def process_site_network_access(context_client : ContextClient, site_id : str, site_network_access : Dict) -> Slice:
vpn_id = site_network_access['vpn-attachment']['vpn-id']
encapsulation_type = site_network_access['connection']['encapsulation-type']
cvlan_id = site_network_access['connection']['tagged-interface'][encapsulation_type]['cvlan-id']
bearer_reference = site_network_access['bearer']['bearer-reference']
access_priority : Optional[int] = site_network_access.get('availability', {}).get('access-priority')
single_active : bool = len(site_network_access.get('availability', {}).get('single-active', [])) > 0
all_active : bool = len(site_network_access.get('availability', {}).get('all-active', [])) > 0
diversity_constraints = site_network_access.get('access-diversity', {}).get('constraints', {}).get('constraint', [])
raise_if_differs = True
diversity_constraints = {
constraint['constraint-type']:([
target[0]
for target in constraint['target'].items()
if len(target[1]) == 1
][0], raise_if_differs)
for constraint in diversity_constraints
}
mapping = BEARER_MAPPINGS.get(bearer_reference)
if mapping is None:
msg = 'Specified Bearer({:s}) is not configured.'
raise Exception(msg.format(str(bearer_reference)))
(
device_uuid, endpoint_uuid, router_id, route_dist, sub_if_index,
address_ip, address_prefix, remote_router, circuit_id
) = mapping
target = get_slice(context_client, vpn_id, rw_copy=True)
if target is None: raise Exception('VPN({:s}) not found in database'.format(str(vpn_id)))
endpoint_ids = target.slice_endpoint_ids # pylint: disable=no-member
config_rules = target.slice_config.config_rules # pylint: disable=no-member
constraints = target.slice_constraints # pylint: disable=no-member
endpoint_id = update_endpoint_ids(endpoint_ids, device_uuid, endpoint_uuid)
service_settings_key = '/settings'
update_config_rule_custom(config_rules, service_settings_key, {
'mtu' : (DEFAULT_MTU, True),
'address_families': (DEFAULT_ADDRESS_FAMILIES, True),
'bgp_as' : (DEFAULT_BGP_AS, True),
'bgp_route_target': (DEFAULT_BGP_ROUTE_TARGET, True),
})
endpoint_settings_key = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
field_updates = {}
if router_id is not None: field_updates['router_id' ] = (router_id, True)
if route_dist is not None: field_updates['route_distinguisher'] = (route_dist, True)
if sub_if_index is not None: field_updates['sub_interface_index'] = (sub_if_index, True)
if cvlan_id is not None: field_updates['vlan_id' ] = (cvlan_id, True)
if address_ip is not None: field_updates['address_ip' ] = (address_ip, True)
if address_prefix is not None: field_updates['address_prefix' ] = (address_prefix, True)
if remote_router is not None: field_updates['remote_router' ] = (remote_router, True)
if circuit_id is not None: field_updates['circuit_id' ] = (circuit_id, True)
update_config_rule_custom(config_rules, endpoint_settings_key, field_updates)
if len(diversity_constraints) > 0:
update_constraint_custom_dict(constraints, 'diversity', diversity_constraints)
update_constraint_endpoint_location(constraints, endpoint_id, region=site_id)
if access_priority is not None: update_constraint_endpoint_priority(constraints, endpoint_id, access_priority)
if single_active or all_active:
# assume 1 disjoint path per endpoint/location included in service/slice
location_endpoints = {}
for constraint in constraints:
if constraint.WhichOneof('constraint') != 'endpoint_location': continue
str_endpoint_id = grpc_message_to_json_string(constraint.endpoint_location.endpoint_id)
str_location_id = grpc_message_to_json_string(constraint.endpoint_location.location)
location_endpoints.setdefault(str_location_id, set()).add(str_endpoint_id)
num_endpoints_per_location = {len(endpoints) for endpoints in location_endpoints.values()}
num_disjoint_paths = min(num_endpoints_per_location)
update_constraint_sla_availability(constraints, num_disjoint_paths, all_active, 0.0)
return target
def process_list_site_network_access(
context_client : ContextClient, slice_client : SliceClient, site_id : str, request_data : Dict
) -> Response:
LOGGER.debug('Request: {:s}'.format(str(request_data)))
validate_message(SCHEMA_SITE_NETWORK_ACCESS, request_data)
errors = []
for site_network_access in request_data['ietf-l2vpn-svc:site-network-access']:
sna_request = process_site_network_access(context_client, site_id, site_network_access)
LOGGER.debug('sna_request = {:s}'.format(grpc_message_to_json_string(sna_request)))
try:
slice_client.UpdateSlice(sna_request)
except Exception as e: # pylint: disable=broad-except
msg = 'Something went wrong Updating VPN {:s}'
LOGGER.exception(msg.format(grpc_message_to_json_string(sna_request)))
errors.append({'error': str(e)})
response = jsonify(errors)
response.status_code = HTTP_NOCONTENT if len(errors) == 0 else HTTP_SERVERERROR
return response
class Topology_SiteNetworkAccesses(Resource):
@HTTP_AUTH.login_required
def post(self, site_id : str):
if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
LOGGER.debug('Site_Id: {:s}'.format(str(site_id)))
context_client = ContextClient()
slice_client = SliceClient()
return process_list_site_network_access(context_client, slice_client, site_id, request.json)
@HTTP_AUTH.login_required
def put(self, site_id : str):
if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
LOGGER.debug('Site_Id: {:s}'.format(str(site_id)))
context_client = ContextClient()
slice_client = SliceClient()
return process_list_site_network_access(context_client, slice_client, site_id, request.json)
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# RFC 8466 - L2VPN Service Model (L2SM)
# Ref: https://datatracker.ietf.org/doc/html/rfc8466
from flask_restful import Resource
from compute.service.rest_server.RestServer import RestServer
from .Topology_Services import Topology_Services
from .Topology_Service import Topology_Service
from .Topology_SiteNetworkAccesses import Topology_SiteNetworkAccesses
URL_PREFIX = '/data/topology'
def _add_resource(rest_server : RestServer, resource : Resource, *urls, **kwargs):
urls = [(URL_PREFIX + url) for url in urls]
rest_server.add_resource(resource, *urls, **kwargs)
def register_ietf_topology(rest_server : RestServer):
_add_resource(rest_server, Topology_Services,
'/bgpls')
_add_resource(rest_server, Topology_Service,
'/vpn-services/vpn-service=<vpn_id>', '/vpn-services/vpn-service=<vpn_id>/')
_add_resource(rest_server, Topology_SiteNetworkAccesses,
'/sites/site=<site_id>/site-network-accesses', '/sites/site=<site_id>/site-network-accesses/')
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# String pattern for UUIDs such as '3fd942ee-2dc3-41d1-aeec-65aa85d117b2'
REGEX_UUID = r'[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12}'
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Example request:
# request = {'ietf-l2vpn-svc:site-network-access': [{
# 'network-access-id': '3fd942ee-2dc3-41d1-aeec-65aa85d117b2',
# 'vpn-attachment': {'vpn-id': '954b1b53-4a8c-406d-9eff-750ec2c9a258',
# 'site-role': 'any-to-any-role'},
# 'connection': {'encapsulation-type': 'dot1q-vlan-tagged', 'tagged-interface': {
# 'dot1q-vlan-tagged': {'cvlan-id': 1234}}},
# 'bearer': {'bearer-reference': '1a'}
# }]}
from .Common import REGEX_UUID
SCHEMA_SITE_NETWORK_ACCESS = {
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'type': 'object',
'required': ['ietf-l2vpn-svc:site-network-access'],
'properties': {
'ietf-l2vpn-svc:site-network-access': {
'type': 'array',
'minItems': 1,
'maxItems': 1, # by now we do not support multiple site-network-access in the same message
'items': {
'type': 'object',
'required': ['network-access-id', 'vpn-attachment', 'connection', 'bearer'],
'properties': {
'network-access-id': {'type': 'string', 'pattern': REGEX_UUID},
'vpn-attachment': {
'type': 'object',
'required': ['vpn-id', 'site-role'],
'properties': {
'vpn-id': {'type': 'string', 'pattern': REGEX_UUID},
'site-role': {'type': 'string', 'minLength': 1},
},
},
'connection': {
'type': 'object',
'required': ['encapsulation-type', 'tagged-interface'],
'properties': {
'encapsulation-type': {'enum': ['dot1q-vlan-tagged']},
'tagged-interface': {
'type': 'object',
'required': ['dot1q-vlan-tagged'],
'properties': {
'dot1q-vlan-tagged': {
'type': 'object',
'required': ['cvlan-id'],
'properties': {
'cvlan-id': {'type': 'integer', 'minimum': 1, 'maximum': 4094},
},
},
},
},
},
},
'bearer': {
'type': 'object',
'required': ['bearer-reference'],
'properties': {
'bearer-reference': {'type': 'string', 'minLength': 1},
},
},
},
},
},
},
}
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Example request:
# request = {'ietf-l2vpn-svc:vpn-service': [{
# 'vpn-id': 'c6270231-f1de-4687-b2ed-7b58f9105775',
# 'vpn-svc-type': 'vpws',
# 'svc-topo': 'any-to-any',
# 'customer-name': 'osm'
# }]}
from .Common import REGEX_UUID
SCHEMA_VPN_SERVICE = {
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'type': 'object',
'required': ['ietf-l2vpn-svc:vpn-service'],
'properties': {
'ietf-l2vpn-svc:vpn-service': {
'type': 'array',
'minItems': 1,
'maxItems': 1, # by now we do not support multiple vpn-service in the same message
'items': {
'type': 'object',
'required': ['vpn-id', 'vpn-svc-type', 'svc-topo', 'customer-name'],
'properties': {
'vpn-id': {'type': 'string', 'pattern': REGEX_UUID},
'vpn-svc-type': {'enum': ['vpws', 'vpls']},
'svc-topo': {'enum': ['any-to-any']},
'customer-name': {'const': 'osm'},
},
}
}
},
}
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import check_password_hash
from compute.Config import RESTAPI_USERS
HTTP_AUTH = HTTPBasicAuth()
@HTTP_AUTH.verify_password
def verify_password(username, password):
if username not in RESTAPI_USERS: return None
if not check_password_hash(RESTAPI_USERS[username], password): return None
return username
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
HTTP_OK = 200
HTTP_CREATED = 201
HTTP_NOCONTENT = 204
HTTP_BADREQUEST = 400
HTTP_SERVERERROR = 500
HTTP_GATEWAYTIMEOUT = 504
\ No newline at end of file
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import List
from flask.json import jsonify
from jsonschema import _utils
from jsonschema.validators import validator_for
from jsonschema.protocols import Validator
from jsonschema.exceptions import ValidationError
from werkzeug.exceptions import BadRequest
from .HttpStatusCodes import HTTP_BADREQUEST
def validate_message(schema, message):
validator_class = validator_for(schema)
validator : Validator = validator_class(schema)
errors : List[ValidationError] = sorted(validator.iter_errors(message), key=str)
if len(errors) == 0: return
response = jsonify([
{'message': str(error.message), 'schema': str(error.schema), 'validator': str(error.validator),
'where': str(_utils.format_as_index(container='message', indices=error.relative_path))}
for error in errors
])
response.status_code = HTTP_BADREQUEST
raise BadRequest(response=response)
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
......@@ -63,12 +63,11 @@ def add():
for key, _ in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items():
form.operational_status.choices.append(
(DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', '')))
form.device_type.choices = []
# items for Device Type field
form.device_type.choices = []
for device_type in DeviceTypeEnum:
if device_type:
form.device_type.choices.append((device_type.value,device_type.value))
form.device_type.choices.append((device_type.value,device_type.value))
if form.validate_on_submit():
device_obj = Device()
# Device UUID:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment