Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • tfs/controller
1 result
Show changes
Commits on Source (62)
Showing
with 570 additions and 102 deletions
...@@ -18,7 +18,7 @@ metadata: ...@@ -18,7 +18,7 @@ metadata:
name: tfs-ingress name: tfs-ingress
annotations: annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2 nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/limit-rps: '5' nginx.ingress.kubernetes.io/limit-rps: '10'
nginx.ingress.kubernetes.io/limit-connections: '10' nginx.ingress.kubernetes.io/limit-connections: '10'
nginx.ingress.kubernetes.io/proxy-connect-timeout: '10' nginx.ingress.kubernetes.io/proxy-connect-timeout: '10'
nginx.ingress.kubernetes.io/proxy-send-timeout: '10' nginx.ingress.kubernetes.io/proxy-send-timeout: '10'
......
#!/bin/bash
# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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.
PROJECTDIR=`pwd`
cd $PROJECTDIR/src
RCFILE=$PROJECTDIR/coverage/.coveragerc
# Run unitary tests and analyze coverage of code at same time
# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0
coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO -o log_cli=true --verbose \
device/tests/test_unitary_openconfig_ocnos.py
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import time
import json import json
import anytree, copy, logging, pytz, queue, re, threading import anytree, copy, logging, pytz, queue, re, threading
#import lxml.etree as ET #import lxml.etree as ET
...@@ -237,6 +238,8 @@ def edit_config( ...@@ -237,6 +238,8 @@ def edit_config(
test_option=test_option, error_option=error_option, format=format) test_option=test_option, error_option=error_option, format=format)
if commit_per_rule: if commit_per_rule:
netconf_handler.commit() # configuration commit netconf_handler.commit() # configuration commit
if 'table_connections' in resource_key:
time.sleep(5) # CPU usage might exceed critical level after route redistribution, BGP daemon needs time to reload
#results[i] = True #results[i] = True
results.append(True) results.append(True)
......
...@@ -20,7 +20,7 @@ from .Tools import add_value_from_tag ...@@ -20,7 +20,7 @@ from .Tools import add_value_from_tag
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
XPATH_ACL_SET = "//ocacl:acl/ocacl:acl-sets/ocacl:acl-set" XPATH_ACL_SET = "//ocacl:acl/ocacl:acl-sets/ocacl:acl-set"
XPATH_A_ACL_ENTRY = ".//ocacl:acl-entries/ocacl:ecl-entry" XPATH_A_ACL_ENTRY = ".//ocacl:acl-entries/ocacl:acl-entry"
XPATH_A_IPv4 = ".//ocacl:ipv4/ocacl:config" XPATH_A_IPv4 = ".//ocacl:ipv4/ocacl:config"
XPATH_A_TRANSPORT = ".//ocacl:transport/ocacl:config" XPATH_A_TRANSPORT = ".//ocacl:transport/ocacl:config"
XPATH_A_ACTIONS = ".//ocacl:actions/ocacl:config" XPATH_A_ACTIONS = ".//ocacl:actions/ocacl:config"
...@@ -34,29 +34,31 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: ...@@ -34,29 +34,31 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
response = [] response = []
acl = {} acl = {}
name = {}
for xml_acl in xml_data.xpath(XPATH_ACL_SET, namespaces=NAMESPACES): for xml_acl in xml_data.xpath(XPATH_ACL_SET, namespaces=NAMESPACES):
#LOGGER.info('xml_acl = {:s}'.format(str(ET.tostring(xml_acl)))) #LOGGER.info('xml_acl = {:s}'.format(str(ET.tostring(xml_acl))))
acl_name = xml_acl.find('ocacl:name', namespaces=NAMESPACES) acl_name = xml_acl.find('ocacl:name', namespaces=NAMESPACES)
if acl_name is None or acl_name.text is None: continue if acl_name is None or acl_name.text is None: continue
add_value_from_tag(acl, 'name', acl_name) add_value_from_tag(name, 'name', acl_name)
acl_type = xml_acl.find('ocacl:type', namespaces=NAMESPACES) acl_type = xml_acl.find('ocacl:type', namespaces=NAMESPACES)
add_value_from_tag(acl, 'type', acl_type) add_value_from_tag(acl, 'type', acl_type)
for xml_acl_entries in xml_acl.xpath(XPATH_A_ACL_ENTRY, namespaces=NAMESPACES): for xml_acl_entries in xml_acl.xpath(XPATH_A_ACL_ENTRY, namespaces=NAMESPACES):
acl_id = xml_acl_entries.find('ocacl:sequence_id', namespaces=NAMESPACES) acl_id = xml_acl_entries.find('ocacl:sequence-id', namespaces=NAMESPACES)
add_value_from_tag(acl, 'sequence_id', acl_id) add_value_from_tag(acl, 'sequence-id', acl_id)
LOGGER.info('xml_acl_id = {:s}'.format(str(ET.tostring(acl_id))))
for xml_ipv4 in xml_acl_entries.xpath(XPATH_A_IPv4, namespaces=NAMESPACES): for xml_ipv4 in xml_acl_entries.xpath(XPATH_A_IPv4, namespaces=NAMESPACES):
ipv4_source = xml_ipv4.find('ocacl:source_address', namespaces=NAMESPACES) ipv4_source = xml_ipv4.find('ocacl:source-address', namespaces=NAMESPACES)
add_value_from_tag(acl, 'source_address' , ipv4_source) add_value_from_tag(acl, 'source-address' , ipv4_source)
ipv4_destination = xml_ipv4.find('ocacl:destination_address', namespaces=NAMESPACES) ipv4_destination = xml_ipv4.find('ocacl:destination-address', namespaces=NAMESPACES)
add_value_from_tag(acl, 'destination_address' , ipv4_destination) add_value_from_tag(acl, 'destination-address' , ipv4_destination)
ipv4_protocol = xml_ipv4.find('ocacl:protocol', namespaces=NAMESPACES) ipv4_protocol = xml_ipv4.find('ocacl:protocol', namespaces=NAMESPACES)
add_value_from_tag(acl, 'protocol' , ipv4_protocol) add_value_from_tag(acl, 'protocol' , ipv4_protocol)
...@@ -64,30 +66,30 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: ...@@ -64,30 +66,30 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
ipv4_dscp = xml_ipv4.find('ocacl:dscp', namespaces=NAMESPACES) ipv4_dscp = xml_ipv4.find('ocacl:dscp', namespaces=NAMESPACES)
add_value_from_tag(acl, 'dscp' , ipv4_dscp) add_value_from_tag(acl, 'dscp' , ipv4_dscp)
ipv4_hop_limit = xml_ipv4.find('ocacl:hop_limit', namespaces=NAMESPACES) ipv4_hop_limit = xml_ipv4.find('ocacl:hop-limit', namespaces=NAMESPACES)
add_value_from_tag(acl, 'hop_limit' , ipv4_hop_limit) add_value_from_tag(acl, 'hop-limit' , ipv4_hop_limit)
for xml_transport in xml_acl_entries.xpath(XPATH_A_TRANSPORT, namespaces=NAMESPACES): for xml_transport in xml_acl_entries.xpath(XPATH_A_TRANSPORT, namespaces=NAMESPACES):
transport_source = xml_transport.find('ocacl:source_port', namespaces=NAMESPACES) transport_source = xml_transport.find('ocacl:source-port', namespaces=NAMESPACES)
add_value_from_tag(acl, 'source_port' ,transport_source) add_value_from_tag(acl, 'source-port' ,transport_source)
transport_destination = xml_transport.find('ocacl:destination_port', namespaces=NAMESPACES) transport_destination = xml_transport.find('ocacl:destination-port', namespaces=NAMESPACES)
add_value_from_tag(acl, 'destination_port' ,transport_destination) add_value_from_tag(acl, 'destination-port' ,transport_destination)
transport_tcp_flags = xml_transport.find('ocacl:tcp_flags', namespaces=NAMESPACES) transport_tcp_flags = xml_transport.find('ocacl:tcp-flags', namespaces=NAMESPACES)
add_value_from_tag(acl, 'tcp_flags' ,transport_tcp_flags) add_value_from_tag(acl, 'tcp-flags' ,transport_tcp_flags)
for xml_action in xml_acl_entries.xpath(XPATH_A_ACTIONS, namespaces=NAMESPACES): for xml_action in xml_acl_entries.xpath(XPATH_A_ACTIONS, namespaces=NAMESPACES):
action = xml_action.find('ocacl:forwarding_action', namespaces=NAMESPACES) action = xml_action.find('ocacl:forwarding-action', namespaces=NAMESPACES)
add_value_from_tag(acl, 'forwarding_action' ,action) add_value_from_tag(acl, 'forwarding-action' ,action)
log_action = xml_action.find('ocacl:log_action', namespaces=NAMESPACES) log_action = xml_action.find('ocacl:log-action', namespaces=NAMESPACES)
add_value_from_tag(acl, 'log_action' ,log_action) add_value_from_tag(acl, 'log-action' ,log_action)
resource_key = '/acl/acl-set[{:s}][{:s}]/acl-entry[{:s}]'.format( resource_key = '/acl/acl-set[{:s}][{:s}]/acl-entry[{:s}]'.format(
acl['name'], acl['type'], acl['sequence-id']) name['name'], acl['type'], acl['sequence-id'])
response.append((resource_key,acl)) response.append((resource_key,acl))
for xml_interface in xml_data.xpath(XPATH_INTERFACE, namespaces=NAMESPACES): for xml_interface in xml_data.xpath(XPATH_INTERFACE, namespaces=NAMESPACES):
...@@ -99,25 +101,25 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: ...@@ -99,25 +101,25 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
for xml_ingress in xml_interface.xpath(XPATH_I_INGRESS, namespaces=NAMESPACES): for xml_ingress in xml_interface.xpath(XPATH_I_INGRESS, namespaces=NAMESPACES):
i_name = xml_ingress.find('ocacl:set_name_ingress', namespaces=NAMESPACES) i_name = xml_ingress.find('ocacl:set-name-ingress', namespaces=NAMESPACES)
add_value_from_tag(interface, 'ingress_set_name' , i_name) add_value_from_tag(interface, 'ingress-set-name' , i_name)
i_type = xml_ingress.find('ocacl:type_ingress', namespaces=NAMESPACES) i_type = xml_ingress.find('ocacl:type-ingress', namespaces=NAMESPACES)
add_value_from_tag(interface, 'ingress_type' , i_type) add_value_from_tag(interface, 'ingress-type' , i_type)
resource_key = '/acl/interfaces/ingress[{:s}][{:s}]'.format( resource_key = '/acl/interfaces/ingress[{:s}][{:s}]'.format(
acl['name'], acl['type']) name['name'], acl['type'])
response.append((resource_key,interface)) response.append((resource_key,interface))
for xml_egress in xml_interface.xpath(XPATH_I_EGRESS, namespaces=NAMESPACES): for xml_egress in xml_interface.xpath(XPATH_I_EGRESS, namespaces=NAMESPACES):
e_name = xml_egress.find('ocacl:set_name_egress', namespaces=NAMESPACES) e_name = xml_egress.find('ocacl:set-name-egress', namespaces=NAMESPACES)
add_value_from_tag(interface, 'egress_set_name' , e_name) add_value_from_tag(interface, 'egress-set-name' , e_name)
e_type = xml_egress.find('ocacl:type_egress', namespaces=NAMESPACES) e_type = xml_egress.find('ocacl:type-egress', namespaces=NAMESPACES)
add_value_from_tag(interface, 'egress_type' , e_type) add_value_from_tag(interface, 'egress-type' , e_type)
resource_key = '/acl/interfaces/egress[{:s}][{:s}]'.format( resource_key = '/acl/interfaces/egress[{:s}][{:s}]'.format(
acl['name'], acl['type']) name['name'], acl['type'])
response.append((resource_key,interface)) response.append((resource_key,interface))
return response return response
...@@ -75,6 +75,10 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: ...@@ -75,6 +75,10 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
component_location = xml_component.find('ocp:state/ocp:location', namespaces=NAMESPACES) component_location = xml_component.find('ocp:state/ocp:location', namespaces=NAMESPACES)
if not component_location is None: if not component_location is None:
add_value_from_tag(inventory['attributes'], 'location', component_location) add_value_from_tag(inventory['attributes'], 'location', component_location)
component_id = xml_component.find('ocp:state/ocp:id', namespaces=NAMESPACES)
if not component_id is None:
add_value_from_tag(inventory['attributes'], 'id', component_id)
component_type = xml_component.find('ocp:state/ocp:type', namespaces=NAMESPACES) component_type = xml_component.find('ocp:state/ocp:type', namespaces=NAMESPACES)
if component_type is not None: if component_type is not None:
...@@ -109,7 +113,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: ...@@ -109,7 +113,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
component_mfg_name = xml_component.find('ocp:state/ocp:mfg-name', namespaces=NAMESPACES) component_mfg_name = xml_component.find('ocp:state/ocp:mfg-name', namespaces=NAMESPACES)
if not component_mfg_name is None: if not component_mfg_name is None:
add_value_from_tag(inventory['attributes'], 'manufacturer-name', component_mfg_name) add_value_from_tag(inventory['attributes'], 'mfg-name', component_mfg_name)
component_removable = xml_component.find('ocp:state/ocp:removable', namespaces=NAMESPACES) component_removable = xml_component.find('ocp:state/ocp:removable', namespaces=NAMESPACES)
if not component_removable is None: if not component_removable is None:
......
...@@ -23,6 +23,8 @@ XPATH_NETWORK_INSTANCES = "//ocni:network-instances/ocni:network-instance" ...@@ -23,6 +23,8 @@ XPATH_NETWORK_INSTANCES = "//ocni:network-instances/ocni:network-instance"
XPATH_NI_PROTOCOLS = ".//ocni:protocols/ocni:protocol" XPATH_NI_PROTOCOLS = ".//ocni:protocols/ocni:protocol"
XPATH_NI_TABLE_CONNECTS = ".//ocni:table-connections/ocni:table-connection" XPATH_NI_TABLE_CONNECTS = ".//ocni:table-connections/ocni:table-connection"
XPATH_NI_INTERFACE = ".//ocni:interfaces/ocni:interface"
XPATH_NI_IIP_AP = ".//ocni:inter-instance-policies/ocni:apply-policy" XPATH_NI_IIP_AP = ".//ocni:inter-instance-policies/ocni:apply-policy"
XPATH_NI_IIP_AP_IMPORT = ".//ocni:config/ocni:import-policy" XPATH_NI_IIP_AP_IMPORT = ".//ocni:config/ocni:import-policy"
XPATH_NI_IIP_AP_EXPORT = ".//ocni:config/ocni:export-policy" XPATH_NI_IIP_AP_EXPORT = ".//ocni:config/ocni:export-policy"
...@@ -136,6 +138,21 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: ...@@ -136,6 +138,21 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
table_connection['address_family']) table_connection['address_family'])
response.append((resource_key, table_connection)) response.append((resource_key, table_connection))
for xml_interface in xml_network_instance.xpath(XPATH_NI_INTERFACE, namespaces=NAMESPACES):
LOGGER.info('xml_interfaces = {:s}'.format(str(ET.tostring(xml_interface))))
interface = {}
name_iface = xml_interface.find('ocni:config/ocni:interface', namespaces=NAMESPACES)
if name_iface is None or name_iface.text is None: continue
add_value_from_tag(interface, 'name_iface', name_iface)
name_subiface = xml_interface.find('ocni:config/ocni:subinterface', namespaces=NAMESPACES)
add_value_from_tag(interface, 'name_subiface', name_subiface)
resource_key = '/network_instance[{:s}]/interface[{:s}]'.format(
network_instance['name'], interface['name_iface'])
response.append((resource_key, interface))
for xml_iip_ap in xml_network_instance.xpath(XPATH_NI_IIP_AP, namespaces=NAMESPACES): for xml_iip_ap in xml_network_instance.xpath(XPATH_NI_IIP_AP, namespaces=NAMESPACES):
#LOGGER.info('xml_iip_ap = {:s}'.format(str(ET.tostring(xml_iip_ap)))) #LOGGER.info('xml_iip_ap = {:s}'.format(str(ET.tostring(xml_iip_ap))))
......
...@@ -61,7 +61,8 @@ def generate_templates(resource_key: str, resource_value: str, delete: bool,vend ...@@ -61,7 +61,8 @@ def generate_templates(resource_key: str, resource_value: str, delete: bool,vend
elif "inter_instance_policies" in resource_key: elif "inter_instance_policies" in resource_key:
result_templates.append(associate_RP_to_NI(data)) result_templates.append(associate_RP_to_NI(data))
elif "protocols" in resource_key: elif "protocols" in resource_key:
if vendor == "ADVA": result_templates.append(add_protocol_NI(data, vendor, delete)) if vendor is None or vendor == "ADVA":
result_templates.append(add_protocol_NI(data, vendor, delete))
elif "table_connections" in resource_key: elif "table_connections" in resource_key:
result_templates.append(create_table_conns(data, delete)) result_templates.append(create_table_conns(data, delete))
elif "interface" in resource_key: elif "interface" in resource_key:
......
...@@ -54,7 +54,7 @@ def create_If_SubIf(data,vendor, DEL): ...@@ -54,7 +54,7 @@ def create_If_SubIf(data,vendor, DEL):
with tag('enabled'):text('true') with tag('enabled'):text('true')
with tag('subinterfaces'): with tag('subinterfaces'):
with tag('subinterface'): with tag('subinterface'):
if vendor == 'ADVA': if vendor is None or vendor == 'ADVA':
with tag('index'): text('0') with tag('index'): text('0')
with tag('config'): with tag('config'):
with tag('index'): text('0') with tag('index'): text('0')
...@@ -65,8 +65,10 @@ def create_If_SubIf(data,vendor, DEL): ...@@ -65,8 +65,10 @@ def create_If_SubIf(data,vendor, DEL):
with tag('single-tagged'): with tag('single-tagged'):
with tag('config'): with tag('config'):
with tag('vlan-id'):text(data['vlan_id']) with tag('vlan-id'):text(data['vlan_id'])
if "l3ipvlan" in data['type']: if "l3ipvlan" in data['type'] and 'address_ip' in data:
with tag('ipv4', xmlns="http://openconfig.net/yang/interfaces/ip"): with tag('ipv4', xmlns="http://openconfig.net/yang/interfaces/ip"):
if 'mtu' in data:
with tag('mtu'):text(data['mtu'])
with tag('addresses'): with tag('addresses'):
with tag('address'): with tag('address'):
with tag('ip'):text(data['address_ip']) with tag('ip'):text(data['address_ip'])
......
...@@ -64,10 +64,12 @@ def create_NI(parameters,vendor,DEL): ...@@ -64,10 +64,12 @@ def create_NI(parameters,vendor,DEL):
elif "L3VRF" in parameters['type']: elif "L3VRF" in parameters['type']:
with tag('config'): with tag('config'):
with tag('name'):text(parameters['name']) with tag('name'):text(parameters['name'])
if vendor == "ADVA": if "router_id" in parameters:
with tag('router-id'):text(parameters['router_id'])
if vendor is None or vendor == 'ADVA':
with tag('type', 'xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types"'):text('oc-ni-types:',parameters['type']) with tag('type', 'xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types"'):text('oc-ni-types:',parameters['type'])
with tag('route-distinguisher'):text(parameters['route_distinguisher']) with tag('route-distinguisher'):text(parameters['route_distinguisher'])
if vendor == "ADVA": if vendor is None or vendor == 'ADVA':
with tag('encapsulation'): with tag('encapsulation'):
with tag('config'): with tag('config'):
with tag('encapsulation-type', 'xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types"') :text('oc-ni-types:MPLS') with tag('encapsulation-type', 'xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types"') :text('oc-ni-types:MPLS')
...@@ -123,14 +125,29 @@ def add_protocol_NI(parameters,vendor, DEL): ...@@ -123,14 +125,29 @@ def add_protocol_NI(parameters,vendor, DEL):
with tag('config'): with tag('config'):
with tag('identifier', 'xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'):text('oc-pol-types:',parameters['identifier']) with tag('identifier', 'xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'):text('oc-pol-types:',parameters['identifier'])
with tag('name') :text(parameters['protocol_name']) with tag('name') :text(parameters['protocol_name'])
with tag('enabled'): text('true')
if "BGP" in parameters['identifier']: if "BGP" in parameters['identifier']:
with tag('bgp'): with tag('bgp'):
with tag('name'): text(parameters['as'])
with tag('global'): with tag('global'):
with tag('config'): with tag('config'):
with tag('as') :text(parameters['as']) with tag('as') :text(parameters['as'])
if "router-id" in parameters: if "router_id" in parameters:
with tag('router-id'):text(parameters['router-id']) with tag('router-id'):text(parameters['router_id'])
if vendor == "ADVA": if 'neighbors' in parameters:
with tag('neighbors'):
for neighbor in parameters['neighbors']:
with tag('neighbor'):
with tag('neighbor-address'): text(neighbor['ip_address'])
with tag('afi-safis'):
with tag('afi-safi', 'xmlns:oc-bgp-types="http://openconfig.net/yang/bgp-types"'):
with tag('afi-safi-name'): text('oc-bgp-types:IPV4_UNICAST')
with tag('enabled'): text('true')
with tag('config'):
with tag('neighbor-address'): text(neighbor['ip_address'])
with tag('enabled'): text('true')
with tag('peer-as'): text(parameters['as'])
if vendor is None or vendor == 'ADVA':
with tag('tables'): with tag('tables'):
with tag('table'): with tag('table'):
with tag('protocol', 'xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'):text('oc-pol-types:',parameters['identifier']) with tag('protocol', 'xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'):text('oc-pol-types:',parameters['identifier'])
...@@ -177,6 +194,9 @@ def associate_If_to_NI(parameters, DEL): ...@@ -177,6 +194,9 @@ def associate_If_to_NI(parameters, DEL):
else: else:
with tag('network-instance'): with tag('network-instance'):
with tag('name'):text(parameters['name']) with tag('name'):text(parameters['name'])
with tag('config'):
with tag('name'):text(parameters['name'])
with tag('type', 'xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types"'):text('oc-ni-types:',parameters['type'])
with tag('interfaces'): with tag('interfaces'):
with tag('interface'): with tag('interface'):
with tag('id'):text(parameters['id']) with tag('id'):text(parameters['id'])
...@@ -315,7 +335,7 @@ def create_table_conns(parameters,DEL): ...@@ -315,7 +335,7 @@ def create_table_conns(parameters,DEL):
with tag('table-connection','xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete"'): with tag('table-connection','xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete"'):
with tag('src-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['src_protocol']) with tag('src-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['src_protocol'])
with tag('dst-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['dst_protocol']) with tag('dst-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['dst_protocol'])
with tag('address-family', 'xmlns:oc-types="http://openconfig.net/yang/openconfig-types"'):text('oc-types:',parameters['dst_protocol']) with tag('address-family', 'xmlns:oc-types="http://openconfig.net/yang/openconfig-types"'):text('oc-types:',parameters['address_family'])
else: else:
with tag('table-connections'): with tag('table-connections'):
with tag('table-connection'): with tag('table-connection'):
...@@ -326,6 +346,8 @@ def create_table_conns(parameters,DEL): ...@@ -326,6 +346,8 @@ def create_table_conns(parameters,DEL):
with tag('src-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['src_protocol']) with tag('src-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['src_protocol'])
with tag('dst-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['dst_protocol']) with tag('dst-protocol','xmlns:oc-pol-types="http://openconfig.net/yang/policy-types"'): text('oc-pol-types:',parameters['dst_protocol'])
with tag('address-family', 'xmlns:oc-types="http://openconfig.net/yang/openconfig-types"'):text('oc-types:',parameters['address_family']) with tag('address-family', 'xmlns:oc-types="http://openconfig.net/yang/openconfig-types"'):text('oc-types:',parameters['address_family'])
# for OCNOS: check if needed
#with tag('dst-instance', 'xmlns="http://www.ipinfusion.com/yang/ocnos/ipi-oc-ni-augments"'):text('65000')
if len(parameters['default_import_policy']) != 0: if len(parameters['default_import_policy']) != 0:
with tag('default-import-policy'):text(parameters['default_import_policy']) with tag('default-import-policy'):text(parameters['default_import_policy'])
result = indent( result = indent(
......
...@@ -133,14 +133,14 @@ data_2 = {'ext_community_member' : '65001:101', ...@@ -133,14 +133,14 @@ data_2 = {'ext_community_member' : '65001:101',
'ext_community_set_name' : 'set_srv_101_a'} 'ext_community_set_name' : 'set_srv_101_a'}
print('\nRouting Policy Statement - CREATE\n') print('\nRouting Policy Statement - CREATE\n')
print(rp_statement(data_1, False)) print(create_rp_statement(data_1, False))
print('\nRouting Policy Statement - DELETE\n') print('\nRouting Policy Statement - DELETE\n')
print(rp_statement(data_1, True)) print(create_rp_statement(data_1, True))
print('\nRouting Policy Defined Set - CREATE\n') print('\nRouting Policy Defined Set - CREATE\n')
print(rp_defined_set(data_2, False)) print(create_rp_def(data_2, False))
print('\nRouting Policy Defined Set - DELETE\n') print('\nRouting Policy Defined Set - DELETE\n')
print(rp_defined_set(data_2, True)) print(create_rp_def(data_2, True))
''' '''
''' '''
......
...@@ -121,7 +121,8 @@ def compose_config( # template generation ...@@ -121,7 +121,8 @@ def compose_config( # template generation
templates.append(JINJA_ENV.get_template('acl/acl-set/acl-entry/edit_config.xml')) templates.append(JINJA_ENV.get_template('acl/acl-set/acl-entry/edit_config.xml'))
templates.append(JINJA_ENV.get_template('acl/interfaces/ingress/edit_config.xml')) templates.append(JINJA_ENV.get_template('acl/interfaces/ingress/edit_config.xml'))
data : Dict[str, Any] = json.loads(resource_value) data : Dict[str, Any] = json.loads(resource_value)
operation = 'delete' if delete else 'merge' operation = 'delete' if delete else 'merge' # others
#operation = 'delete' if delete else '' # ipinfusion?
return [ return [
'<config>{:s}</config>'.format( '<config>{:s}</config>'.format(
......
<interfaces xmlns="http://openconfig.net/yang/interfaces" <interfaces xmlns="http://openconfig.net/yang/interfaces">
xmlns:oc-ip="http://openconfig.net/yang/interfaces/ip" >
<interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}> <interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
<name>{{name}}</name> <name>{{name}}</name>
{% if operation is defined and operation != 'delete' %} {% if operation is defined and operation != 'delete' %}
...@@ -31,17 +30,20 @@ ...@@ -31,17 +30,20 @@
</vlan> </vlan>
{% endif %} {% endif %}
{% if address_ip is defined %} {% if address_ip is defined %}
<oc-ip:ipv4> <ipv4 xmlns="http://openconfig.net/yang/interfaces/ip">
<oc-ip:addresses> <config>
<oc-ip:address> {% if mtu is defined %}<mtu>{{mtu}}</mtu>{% endif%}
<oc-ip:ip>{{address_ip}}</oc-ip:ip> </config>
<oc-ip:config> <addresses>
<oc-ip:ip>{{address_ip}}</oc-ip:ip> <address>
<oc-ip:prefix-length>{{address_prefix}}</oc-ip:prefix-length> <ip>{{address_ip}}</ip>
</oc-ip:config> <config>
</oc-ip:address> <ip>{{address_ip}}</ip>
</oc-ip:addresses> <prefix-length>{{address_prefix}}</prefix-length>
</oc-ip:ipv4> </config>
</address>
</addresses>
</ipv4>
{% endif %} {% endif %}
</subinterface> </subinterface>
</subinterfaces> </subinterfaces>
......
<network-instances xmlns="http://openconfig.net/yang/network-instance"> <network-instances xmlns="http://openconfig.net/yang/network-instance">
<network-instance> <network-instance>
<name>{{name}}</name> <name>{{name}}</name>
<config>
<name>{{name}}</name>
<type xmlns:oc-ni-types="http://openconfig.net/yang/network-instance-types">oc-ni-types:{{type}}</type>
</config>
<interfaces> <interfaces>
<interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}> <interface{% if operation is defined %} xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="{{operation}}"{% endif %}>
<id>{{id}}</id> <id>{{id}}</id>
......
...@@ -9,15 +9,37 @@ ...@@ -9,15 +9,37 @@
<config> <config>
<identifier xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{identifier}}</identifier> <identifier xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{identifier}}</identifier>
<name>{{protocol_name}}</name> <name>{{protocol_name}}</name>
<enabled>true</enabled>
</config> </config>
{% if identifier=='BGP' %} {% if identifier=='BGP' %}
<bgp> <bgp>
<name>{{as}}</name>
<global> <global>
<config> <config>
<as>{{as}}</as> <as>{{as}}</as>
<router-id>{{router_id}}</router-id> <router-id>{{router_id}}</router-id>
</config> </config>
</global> </global>
{% if neighbors is defined %}
<neighbors>
{% for neighbor in neighbors %}
<neighbor>
<neighbor-address>{{neighbor['ip_address']}}</neighbor-address>
<afi-safis>
<afi-safi xmlns:oc-bgp-types="http://openconfig.net/yang/bgp-types">
<afi-safi-name>oc-bgp-types:IPV4_UNICAST</afi-safi-name>
<enabled>true</enabled>
</afi-safi>
</afi-safis>
<config>
<neighbor-address>{{neighbor['ip_address']}}</neighbor-address>
<enabled>true</enabled>
<peer-as>{{as}}</peer-as>
</config>
</neighbor>
{% endfor %}
</neighbors>
{% endif %}
</bgp> </bgp>
{% endif %} {% endif %}
{% endif %} {% endif %}
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
<src-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{src_protocol}}</src-protocol> <src-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{src_protocol}}</src-protocol>
<dst-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{dst_protocol}}</dst-protocol> <dst-protocol xmlns:oc-pol-types="http://openconfig.net/yang/policy-types">oc-pol-types:{{dst_protocol}}</dst-protocol>
<address-family xmlns:oc-types="http://openconfig.net/yang/openconfig-types">oc-types:{{address_family}}</address-family> <address-family xmlns:oc-types="http://openconfig.net/yang/openconfig-types">oc-types:{{address_family}}</address-family>
{% if False %}
<dst-instance xmlns="http://www.ipinfusion.com/yang/ocnos/ipi-oc-ni-augments">{{as}}</dst-instance>
{% endif %}
{% if default_import_policy is defined %}<default-import-policy>{{default_import_policy}}</default-import-policy>{% endif %} {% if default_import_policy is defined %}<default-import-policy>{{default_import_policy}}</default-import-policy>{% endif %}
</config> </config>
{% endif %} {% endif %}
......
# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 json, logging, os, pytest, time
from typing import Dict, Tuple
os.environ['DEVICE_EMULATED_ONLY'] = 'YES'
# pylint: disable=wrong-import-position
from device.service.drivers.openconfig.OpenConfigDriver import OpenConfigDriver
#from device.service.driver_api._Driver import (
# RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES, RESOURCE_ROUTING_POLICIES, RESOURCE_SERVICES
#)
logging.basicConfig(level=logging.DEBUG)
#logging.getLogger('ncclient.operations.rpc').setLevel(logging.INFO)
#logging.getLogger('ncclient.transport.parser').setLevel(logging.INFO)
LOGGER = logging.getLogger(__name__)
##### DRIVERS FIXTURE ##################################################################################################
DEVICES = {
'CSGW1': {'address': '10.1.1.86', 'port': 830, 'settings': {
'username': 'ocnos', 'password': 'ocnos',
'vendor': None, 'force_running': False, 'hostkey_verify': False, 'look_for_keys': False, 'allow_agent': False,
'commit_per_rule': True, 'device_params': {'name': 'default'}, 'manager_params': {'timeout' : 120}
}},
'CSGW2': {'address': '10.1.1.87', 'port': 830, 'settings': {
'username': 'ocnos', 'password': 'ocnos',
'vendor': None, 'force_running': False, 'hostkey_verify': False, 'look_for_keys': False, 'allow_agent': False,
'commit_per_rule': True, 'device_params': {'name': 'default'}, 'manager_params': {'timeout' : 120}
}},
}
@pytest.fixture(scope='session')
def drivers() -> Dict[str, OpenConfigDriver]:
_drivers : Dict[str, OpenConfigDriver] = dict()
for device_name, driver_params in DEVICES.items():
driver = OpenConfigDriver(driver_params['address'], driver_params['port'], **(driver_params['settings']))
driver.Connect()
_drivers[device_name] = driver
yield _drivers
time.sleep(1)
for _,driver in _drivers.items():
driver.Disconnect()
def network_instance(ni_name, ni_type, ni_router_id=None, ni_route_distinguisher=None) -> Tuple[str, Dict]:
path = '/network_instance[{:s}]'.format(ni_name)
data = {'name': ni_name, 'type': ni_type}
if ni_router_id is not None: data['router_id'] = ni_router_id
if ni_route_distinguisher is not None: data['route_distinguisher'] = ni_route_distinguisher
return path, json.dumps(data)
def network_instance_add_protocol_bgp(ni_name, ni_type, ni_router_id, ni_bgp_as, neighbors=[]) -> Tuple[str, Dict]:
path = '/network_instance[{:s}]/protocols[BGP]'.format(ni_name)
data = {
'name': ni_name, 'type': ni_type, 'router_id': ni_router_id, 'identifier': 'BGP',
'protocol_name': ni_bgp_as, 'as': ni_bgp_as
}
if len(neighbors) > 0:
data['neighbors'] = [
{'ip_address': neighbor_ip_address, 'remote_as': neighbor_remote_as}
for neighbor_ip_address, neighbor_remote_as in neighbors
]
return path, json.dumps(data)
def network_instance_add_protocol_direct(ni_name, ni_type) -> Tuple[str, Dict]:
path = '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(ni_name)
data = {
'name': ni_name, 'type': ni_type, 'identifier': 'DIRECTLY_CONNECTED',
'protocol_name': 'DIRECTLY_CONNECTED'
}
return path, json.dumps(data)
def network_instance_add_protocol_static(ni_name, ni_type) -> Tuple[str, Dict]:
path = '/network_instance[{:s}]/protocols[STATIC]'.format(ni_name)
data = {
'name': ni_name, 'type': ni_type, 'identifier': 'STATIC',
'protocol_name': 'STATIC'
}
return path, json.dumps(data)
#def network_instance_static_route(ni_name, prefix, next_hop, next_hop_index=0) -> Tuple[str, Dict]:
# path = '/network_instance[{:s}]/static_route[{:s}]'.format(ni_name, prefix)
# data = {'name': ni_name, 'prefix': prefix, 'next_hop': next_hop, 'next_hop_index': next_hop_index}
# return path, json.dumps(data)
def network_instance_add_table_connection(
ni_name, src_protocol, dst_protocol, address_family, default_import_policy, bgp_as=None
) -> Tuple[str, Dict]:
path = '/network_instance[{:s}]/table_connections[{:s}][{:s}][{:s}]'.format(
ni_name, src_protocol, dst_protocol, address_family
)
data = {
'name': ni_name, 'src_protocol': src_protocol, 'dst_protocol': dst_protocol,
'address_family': address_family, 'default_import_policy': default_import_policy,
}
if bgp_as is not None: data['as'] = bgp_as
return path, json.dumps(data)
def interface(
name, index, description=None, if_type=None, vlan_id=None, mtu=None, ipv4_address_prefix=None, enabled=None
) -> Tuple[str, Dict]:
path = '/interface[{:s}]/subinterface[{:d}]'.format(name, index)
data = {'name': name, 'index': index}
if description is not None: data['description'] = description
if if_type is not None: data['type' ] = if_type
if vlan_id is not None: data['vlan_id' ] = vlan_id
if mtu is not None: data['mtu' ] = mtu
if enabled is not None: data['enabled' ] = enabled
if ipv4_address_prefix is not None:
ipv4_address, ipv4_prefix = ipv4_address_prefix
data['address_ip' ] = ipv4_address
data['address_prefix'] = ipv4_prefix
return path, json.dumps(data)
def network_instance_interface(ni_name, ni_type, if_name, if_index) -> Tuple[str, Dict]:
path = '/network_instance[{:s}]/interface[{:s}.{:d}]'.format(ni_name, if_name, if_index)
data = {'name': ni_name, 'type': ni_type, 'id': if_name, 'interface': if_name, 'subinterface': if_index}
return path, json.dumps(data)
def test_configure(drivers : Dict[str, OpenConfigDriver]):
#resources_to_get = []
#resources_to_get = [RESOURCE_ENDPOINTS]
#resources_to_get = [RESOURCE_INTERFACES]
#resources_to_get = [RESOURCE_NETWORK_INSTANCES]
#resources_to_get = [RESOURCE_ROUTING_POLICIES]
#resources_to_get = [RESOURCE_SERVICES]
#LOGGER.info('resources_to_get = {:s}'.format(str(resources_to_get)))
#results_getconfig = driver.GetConfig(resources_to_get)
#LOGGER.info('results_getconfig = {:s}'.format(str(results_getconfig)))
csgw1_resources_to_set = [
network_instance('ecoc24', 'L3VRF', '192.168.150.1', '65001:1'),
network_instance_add_protocol_direct('ecoc24', 'L3VRF'),
network_instance_add_protocol_static('ecoc24', 'L3VRF'),
network_instance_add_protocol_bgp('ecoc24', 'L3VRF', '192.168.150.1', '65001', neighbors=[
('192.168.150.2', '65001')
]),
network_instance_add_table_connection('ecoc24', 'DIRECTLY_CONNECTED', 'BGP', 'IPV4', 'ACCEPT_ROUTE', bgp_as='65001'),
network_instance_add_table_connection('ecoc24', 'STATIC', 'BGP', 'IPV4', 'ACCEPT_ROUTE', bgp_as='65001'),
interface('ce1', 0, if_type='ethernetCsmacd', mtu=1500),
network_instance_interface('ecoc24', 'L3VRF', 'ce1', 0),
interface('ce1', 0, if_type='ethernetCsmacd', mtu=1500, ipv4_address_prefix=('192.168.10.1', 24), enabled=True),
interface('xe5', 0, if_type='ethernetCsmacd', mtu=1500),
network_instance_interface('ecoc24', 'L3VRF', 'xe5', 0),
interface('xe5', 0, if_type='ethernetCsmacd', mtu=1500, ipv4_address_prefix=('192.168.150.1', 24), enabled=True),
]
LOGGER.info('CSGW1 resources_to_set = {:s}'.format(str(csgw1_resources_to_set)))
results_setconfig = drivers['CSGW1'].SetConfig(csgw1_resources_to_set)
LOGGER.info('CSGW1 results_setconfig = {:s}'.format(str(results_setconfig)))
csgw2_resources_to_set = [
network_instance('ecoc24', 'L3VRF', '192.168.150.2', '65001:1'),
network_instance_add_protocol_direct('ecoc24', 'L3VRF'),
network_instance_add_protocol_static('ecoc24', 'L3VRF'),
network_instance_add_protocol_bgp('ecoc24', 'L3VRF', '192.168.150.2', '65001', neighbors=[
('192.168.150.1', '65001')
]),
network_instance_add_table_connection('ecoc24', 'DIRECTLY_CONNECTED', 'BGP', 'IPV4', 'ACCEPT_ROUTE', bgp_as='65001'),
network_instance_add_table_connection('ecoc24', 'STATIC', 'BGP', 'IPV4', 'ACCEPT_ROUTE', bgp_as='65001'),
interface('ce1', 0, if_type='ethernetCsmacd', mtu=1500),
network_instance_interface('ecoc24', 'L3VRF', 'ce1', 0),
interface('ce1', 0, if_type='ethernetCsmacd', mtu=1500, ipv4_address_prefix=('192.168.20.1', 24), enabled=True),
interface('xe5', 0, if_type='ethernetCsmacd', mtu=1500),
network_instance_interface('ecoc24', 'L3VRF', 'xe5', 0),
interface('xe5', 0, if_type='ethernetCsmacd', mtu=1500, ipv4_address_prefix=('192.168.150.2', 24), enabled=True),
]
LOGGER.info('CSGW2 resources_to_set = {:s}'.format(str(csgw2_resources_to_set)))
results_setconfig = drivers['CSGW2'].SetConfig(csgw2_resources_to_set)
LOGGER.info('CSGW2 results_setconfig = {:s}'.format(str(results_setconfig)))
csgw1_resources_to_delete = [
network_instance_interface('ecoc24', 'L3VRF', 'ce1', 0),
network_instance_interface('ecoc24', 'L3VRF', 'xe5', 0),
#interface('ce1', 0),
#interface('xe5', 0),
network_instance('ecoc24', 'L3VRF'),
]
LOGGER.info('CSGW1 resources_to_delete = {:s}'.format(str(csgw1_resources_to_delete)))
results_deleteconfig = drivers['CSGW1'].DeleteConfig(csgw1_resources_to_delete)
LOGGER.info('CSGW1 results_deleteconfig = {:s}'.format(str(results_deleteconfig)))
csgw2_resources_to_delete = [
network_instance_interface('ecoc24', 'L3VRF', 'ce1', 0),
network_instance_interface('ecoc24', 'L3VRF', 'xe5', 0),
#interface('ce1', 0),
#interface('xe5', 0),
network_instance('ecoc24', 'L3VRF'),
]
LOGGER.info('CSGW2 resources_to_delete = {:s}'.format(str(csgw2_resources_to_delete)))
results_deleteconfig = drivers['CSGW2'].DeleteConfig(csgw2_resources_to_delete)
LOGGER.info('CSGW2 results_deleteconfig = {:s}'.format(str(results_deleteconfig)))
...@@ -18,9 +18,11 @@ from common.Constants import ServiceNameEnum ...@@ -18,9 +18,11 @@ from common.Constants import ServiceNameEnum
from common.Settings import ( from common.Settings import (
ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_log_level, get_metrics_port, ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_log_level, get_metrics_port,
wait_for_environment_variables) wait_for_environment_variables)
from .NbiService import NbiService from .NbiService import NbiService
from .rest_server.RestServer import RestServer from .rest_server.RestServer import RestServer
from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api
from .rest_server.nbi_plugins.ietf_hardware import register_ietf_hardware
from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
from .rest_server.nbi_plugins.ietf_l3vpn import register_ietf_l3vpn from .rest_server.nbi_plugins.ietf_l3vpn import register_ietf_l3vpn
from .rest_server.nbi_plugins.ietf_network import register_ietf_network from .rest_server.nbi_plugins.ietf_network import register_ietf_network
...@@ -63,6 +65,7 @@ def main(): ...@@ -63,6 +65,7 @@ def main():
rest_server = RestServer() rest_server = RestServer()
register_etsi_bwm_api(rest_server) register_etsi_bwm_api(rest_server)
register_ietf_hardware(rest_server)
register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint
register_ietf_l3vpn(rest_server) # Registering L3VPN entrypoint register_ietf_l3vpn(rest_server) # Registering L3VPN entrypoint
register_ietf_network(rest_server) register_ietf_network(rest_server)
......
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
# limitations under the License. # limitations under the License.
import copy, deepmerge, json, logging import copy, deepmerge, json, logging
from typing import Dict
from common.Constants import DEFAULT_CONTEXT_NAME from common.Constants import DEFAULT_CONTEXT_NAME
from werkzeug.exceptions import UnsupportedMediaType
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from flask_restful import Resource, request from flask_restful import Resource, request
from service.client.ServiceClient import ServiceClient from service.client.ServiceClient import ServiceClient
...@@ -37,15 +39,20 @@ class BwInfo(_Resource): ...@@ -37,15 +39,20 @@ class BwInfo(_Resource):
return bw_allocations return bw_allocations
def post(self): def post(self):
bwinfo = request.get_json() if not request.is_json:
service = bwInfo_2_service(self.client, bwinfo) raise UnsupportedMediaType('JSON payload is required')
request_data: Dict = request.get_json()
service = bwInfo_2_service(self.client, request_data)
stripped_service = copy.deepcopy(service) stripped_service = copy.deepcopy(service)
stripped_service.ClearField('service_endpoint_ids') stripped_service.ClearField('service_endpoint_ids')
stripped_service.ClearField('service_constraints') stripped_service.ClearField('service_constraints')
stripped_service.ClearField('service_config') stripped_service.ClearField('service_config')
response = format_grpc_to_json(self.service_client.CreateService(stripped_service)) try:
response = format_grpc_to_json(self.service_client.UpdateService(service)) response = format_grpc_to_json(self.service_client.CreateService(stripped_service))
response = format_grpc_to_json(self.service_client.UpdateService(service))
except Exception as e: # pylint: disable=broad-except
return e
return response return response
......
...@@ -14,22 +14,41 @@ ...@@ -14,22 +14,41 @@
import json import json
import logging import logging
import re
import time import time
from decimal import ROUND_HALF_EVEN, Decimal from decimal import ROUND_HALF_EVEN, Decimal
from flask.json import jsonify from flask.json import jsonify
from common.proto.context_pb2 import ( from common.proto.context_pb2 import (
ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, Constraint, Constraint_SLA_Capacity, ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, ServiceStatusEnum, Constraint, Constraint_SLA_Capacity,
ConfigRule, ConfigRule_Custom, ConfigActionEnum) ConfigRule, ConfigRule_Custom, ConfigActionEnum)
from common.tools.grpc.Tools import grpc_message_to_json from common.tools.grpc.Tools import grpc_message_to_json
from common.tools.grpc.ConfigRules import update_config_rule_custom
from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.Service import json_service_id from common.tools.object_factory.Service import json_service_id
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/vlan[{:d}]/settings'
DEVICE_SETTINGS_KEY = '/device[{:s}]/settings'
RE_CONFIG_RULE_IF_SUBIF = re.compile(r'^\/interface\[([^\]]+)\]\/subinterface\[([^\]]+)\]$')
MEC_CONSIDERED_FIELDS = ['requestType', 'sessionFilter', 'fixedAllocation', 'allocationDirection', 'fixedBWPriority']
ALLOCATION_DIRECTION_DESCRIPTIONS = {
'00' : 'Downlink (towards the UE)',
'01' : 'Uplink (towards the application/session)',
'10' : 'Symmetrical'}
VLAN_TAG = 0
PREFIX_LENGTH = 24
BGP_AS = 65000
POLICY_AZ = 'srv_{:d}_a'.format(VLAN_TAG)
POLICY_ZA = 'srv_{:d}_b'.format(VLAN_TAG)
BGP_NEIGHBOR_IP_A = '192.168.150.1'
BGP_NEIGHBOR_IP_Z = '192.168.150.2'
ROUTER_ID_A = '200.1.1.1'
ROUTER_ID_Z = '200.1.1.2'
ROUTE_DISTINGUISHER = '{:5d}:{:03d}'.format(BGP_AS, VLAN_TAG)
def service_2_bwInfo(service: Service) -> dict: def service_2_bwInfo(service: Service) -> dict:
response = {} response = {}
# allocationDirection = '??' # String: 00 = Downlink (towards the UE); 01 = Uplink (towards the application/session); 10 = Symmetrical
response['appInsId'] = service.service_id.service_uuid.uuid # String: Application instance identifier response['appInsId'] = service.service_id.service_uuid.uuid # String: Application instance identifier
for constraint in service.service_constraints: for constraint in service.service_constraints:
if constraint.WhichOneof('constraint') == 'sla_capacity': if constraint.WhichOneof('constraint') == 'sla_capacity':
...@@ -40,12 +59,19 @@ def service_2_bwInfo(service: Service) -> dict: ...@@ -40,12 +59,19 @@ def service_2_bwInfo(service: Service) -> dict:
break break
for config_rule in service.service_config.config_rules: for config_rule in service.service_config.config_rules:
resource_value_json = json.loads(config_rule.custom.resource_value)
if config_rule.custom.resource_key != '/request':
continue
for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'sourceIp', 'sourcePort', 'dstPort', 'protocol', 'sessionFilter']: for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'sourceIp', 'sourcePort', 'dstPort', 'protocol', 'sessionFilter']:
if config_rule.custom.resource_key == key: if key not in resource_value_json:
if key != 'sessionFilter': continue
response[key] = config_rule.custom.resource_value
else: if key == 'sessionFilter':
response[key] = json.loads(config_rule.custom.resource_value) response[key] = [resource_value_json[key]]
elif key == 'requestType':
response[key] = str(resource_value_json[key])
else:
response[key] = resource_value_json[key]
unixtime = time.time() unixtime = time.time()
response['timeStamp'] = { # Time stamp to indicate when the corresponding information elements are sent response['timeStamp'] = { # Time stamp to indicate when the corresponding information elements are sent
...@@ -55,47 +81,108 @@ def service_2_bwInfo(service: Service) -> dict: ...@@ -55,47 +81,108 @@ def service_2_bwInfo(service: Service) -> dict:
return response return response
def bwInfo_2_service(client, bwInfo: dict) -> Service: def bwInfo_2_service(client, bw_info: dict) -> Service:
# add description to allocationDirection code
if 'sessionFilter' in bw_info:
bw_info['sessionFilter'] = bw_info['sessionFilter'][0] # Discard other items in sessionFilter field
service = Service() service = Service()
for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'timeStamp', 'sessionFilter']:
if key not in bwInfo: service_config_rules = service.service_config.config_rules
continue
config_rule = ConfigRule()
config_rule.action = ConfigActionEnum.CONFIGACTION_SET request_cr_key = '/request'
config_rule_custom = ConfigRule_Custom() request_cr_value = {k:bw_info[k] for k in MEC_CONSIDERED_FIELDS}
config_rule_custom.resource_key = key
if key != 'sessionFilter': config_rule = ConfigRule()
config_rule_custom.resource_value = str(bwInfo[key]) config_rule.action = ConfigActionEnum.CONFIGACTION_SET
else: config_rule_custom = ConfigRule_Custom()
config_rule_custom.resource_value = json.dumps(bwInfo[key]) config_rule_custom.resource_key = request_cr_key
config_rule.custom.CopyFrom(config_rule_custom) config_rule_custom.resource_value = json.dumps(request_cr_value)
service.service_config.config_rules.append(config_rule) config_rule.custom.CopyFrom(config_rule_custom)
service_config_rules.append(config_rule)
if 'sessionFilter' in bwInfo:
a_ip = bwInfo['sessionFilter'][0]['sourceIp'] if 'sessionFilter' in bw_info:
z_ip = bwInfo['sessionFilter'][0]['dstAddress'] a_ip = bw_info['sessionFilter']['sourceIp']
z_ip = bw_info['sessionFilter']['dstAddress']
devices = client.ListDevices(Empty()).devices devices = client.ListDevices(Empty()).devices
ip_interface_name_dict = {}
for device in devices: for device in devices:
device_endpoint_uuids = {ep.name:ep.endpoint_id.endpoint_uuid.uuid for ep in device.device_endpoints}
skip_device = True
for cr in device.device_config.config_rules: for cr in device.device_config.config_rules:
if cr.WhichOneof('config_rule') == 'custom' and cr.custom.resource_key == '_connect/settings': if cr.WhichOneof('config_rule') != 'custom':
for ep in json.loads(cr.custom.resource_value)['endpoints']: continue
if 'ip' in ep and (ep['ip'] == a_ip or ep['ip'] == z_ip): match_subif = RE_CONFIG_RULE_IF_SUBIF.match(cr.custom.resource_key)
ep_id = EndPointId() if not match_subif:
ep_id.endpoint_uuid.uuid = ep['uuid'] continue
ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid address_ip = json.loads(cr.custom.resource_value).get('address_ip')
service.service_endpoint_ids.append(ep_id) short_port_name = match_subif.groups(0)[0]
ip_interface_name_dict[address_ip] = short_port_name
if address_ip not in [a_ip, z_ip]:
continue
port_name = 'PORT-' + short_port_name # `PORT-` added as prefix
ep_id = EndPointId()
ep_id.endpoint_uuid.uuid = device_endpoint_uuids[port_name]
ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid
service.service_endpoint_ids.append(ep_id)
# add interface config rules
endpoint_settings_key = ENDPOINT_SETTINGS_KEY.format(device.name, port_name, VLAN_TAG)
if address_ip in a_ip:
router_id = ROUTER_ID_A
policy_az = POLICY_AZ
policy_za = POLICY_ZA
neighbor_bgp_interface_address_ip = BGP_NEIGHBOR_IP_Z
self_bgp_interface_address_ip = BGP_NEIGHBOR_IP_A
else:
router_id = ROUTER_ID_Z
policy_az = POLICY_ZA
policy_za = POLICY_AZ
neighbor_bgp_interface_address_ip= BGP_NEIGHBOR_IP_A
self_bgp_interface_address_ip = BGP_NEIGHBOR_IP_Z
endpoint_field_updates = {
'address_ip': (address_ip, True),
'address_prefix' : (PREFIX_LENGTH, True),
'sub_interface_index': (0, True),
}
LOGGER.debug(f'BEFORE UPDATE -> device.device_config.config_rules: {service_config_rules}')
update_config_rule_custom(service_config_rules, endpoint_settings_key, endpoint_field_updates)
LOGGER.debug(f'AFTER UPDATE -> device.device_config.config_rules: {service_config_rules}')
skip_device = False
if skip_device:
continue
device_field_updates = {
'bgp_as':(BGP_AS, True),
'route_distinguisher': (ROUTE_DISTINGUISHER, True),
'router_id': (router_id, True),
'policy_AZ': (policy_az, True),
'policy_ZA': (policy_za, True),
'neighbor_bgp_interface_address_ip': (neighbor_bgp_interface_address_ip, True),
'self_bgp_interface_name': (ip_interface_name_dict[self_bgp_interface_address_ip], True),
'self_bgp_interface_address_ip': (self_bgp_interface_address_ip, True),
'bgp_interface_address_prefix': (PREFIX_LENGTH, True)
}
device_settings_key = DEVICE_SETTINGS_KEY.format(device.name)
LOGGER.debug(f'BEFORE UPDATE -> device.device_config.config_rules: {service_config_rules}')
update_config_rule_custom(service_config_rules, device_settings_key, device_field_updates)
LOGGER.debug(f'AFTER UPDATE -> device.device_config.config_rules: {service_config_rules}')
settings_cr_key = '/settings'
settings_cr_value = {}
update_config_rule_custom(service_config_rules, settings_cr_key, settings_cr_value)
service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED
service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM
if 'appInsId' in bwInfo: if 'appInsId' in bw_info:
service.service_id.service_uuid.uuid = bwInfo['appInsId'] service.service_id.service_uuid.uuid = bw_info['appInsId']
service.service_id.context_id.context_uuid.uuid = 'admin' service.service_id.context_id.context_uuid.uuid = 'admin'
service.name = bwInfo['appInsId'] service.name = bw_info['appInsId']
if 'fixedAllocation' in bwInfo: if 'fixedAllocation' in bw_info:
capacity = Constraint_SLA_Capacity() capacity = Constraint_SLA_Capacity()
capacity.capacity_gbps = float(bwInfo['fixedAllocation']) / 1.e9 capacity.capacity_gbps = float(bw_info['fixedAllocation']) / 1.e9
constraint = Constraint() constraint = Constraint()
constraint.sla_capacity.CopyFrom(capacity) constraint.sla_capacity.CopyFrom(capacity)
service.service_constraints.append(constraint) service.service_constraints.append(constraint)
......
# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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.tools.context_queries.Device import get_device
from context.client.ContextClient import ContextClient
from ..tools.Authentication import HTTP_AUTH
from ..tools.HttpStatusCodes import HTTP_OK, HTTP_SERVERERROR
from .YangHandler import YangHandler
LOGGER = logging.getLogger(__name__)
class Hardware(Resource):
@HTTP_AUTH.login_required
def get(self, device_uuid : str):
LOGGER.debug('Device UUID: {:s}'.format(str(device_uuid)))
LOGGER.debug('Request: {:s}'.format(str(request)))
try:
context_client = ContextClient()
device = get_device(
context_client, device_uuid, rw_copy=False,
include_endpoints=False, include_config_rules=False, include_components=True
)
if device is None:
raise Exception('Device({:s}) not found in database'.format(str(device_uuid)))
yang_handler = YangHandler()
hardware_reply = yang_handler.compose(device)
yang_handler.destroy()
response = jsonify(hardware_reply)
response.status_code = HTTP_OK
except Exception as e: # pylint: disable=broad-except
MSG = 'Something went wrong Retrieving Hardware of Device({:s})'
LOGGER.exception(MSG.format(str(device_uuid)))
response = jsonify({'error': str(e)})
response.status_code = HTTP_SERVERERROR
return response
\ No newline at end of file