Merge "ADD N1, N2 N3 interfacing to topology generation"
authorMartin Skorupski <martin.skorupski@highstreet-technologies.com>
Mon, 7 Mar 2022 14:25:22 +0000 (14:25 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Mon, 7 Mar 2022 14:25:22 +0000 (14:25 +0000)
code/network-topology-parser/.gitignore [new file with mode: 0644]
code/network-topology-parser/README.md [new file with mode: 0644]
code/network-topology-parser/go.mod [new file with mode: 0644]
code/network-topology-parser/go.sum [new file with mode: 0644]
code/network-topology-parser/main.go [new file with mode: 0644]
code/network-topology-parser/tapi-context.go [new file with mode: 0644]
code/network-topology-parser/yaml.go [new file with mode: 0644]

diff --git a/code/network-topology-parser/.gitignore b/code/network-topology-parser/.gitignore
new file mode 100644 (file)
index 0000000..a643d9b
--- /dev/null
@@ -0,0 +1,5 @@
+
+topology-parser
+*.json
+*.yaml
+.vscode
diff --git a/code/network-topology-parser/README.md b/code/network-topology-parser/README.md
new file mode 100644 (file)
index 0000000..ccbac30
--- /dev/null
@@ -0,0 +1,25 @@
+# Network Topology Parser
+
+This golang project generates a docker-compose.yaml file to be used for simulating the network topology, by parsing the topology JSON file created by the [Network Topology Instance Generator](../network-topology-instance-generator/README.md) tool.
+
+## Prerequisites
+
+The GO runtime should be installed on the machine. Instructions [here](https://go.dev/doc/install).
+
+## Usage
+
+These commands should be run from the folder where this README is located.
+
+``` bash
+go build
+```
+
+This creates the executable *topology-parser*.
+
+``` bash
+./topology-parser <network-topology.json>
+```
+
+## Output
+
+The result of running this tool is a **docker-compose.yaml** file that can be then used in the *solution/integration/network* folder, **along with the .env file defined there**, for starting a simulated topology as described in the network-topology.json input file.
diff --git a/code/network-topology-parser/go.mod b/code/network-topology-parser/go.mod
new file mode 100644 (file)
index 0000000..2380d6d
--- /dev/null
@@ -0,0 +1,15 @@
+module topology-parser
+
+go 1.17
+
+require (
+       github.com/fatih/color v1.10.0 // indirect
+       github.com/mattn/go-colorable v0.1.8 // indirect
+       github.com/mattn/go-isatty v0.0.12 // indirect
+       golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+)
+
+require (
+       github.com/goccy/go-yaml v1.9.5
+       golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
+)
diff --git a/code/network-topology-parser/go.sum b/code/network-topology-parser/go.sum
new file mode 100644 (file)
index 0000000..475dfdd
--- /dev/null
@@ -0,0 +1,37 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
+github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
+github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0=
+github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/code/network-topology-parser/main.go b/code/network-topology-parser/main.go
new file mode 100644 (file)
index 0000000..6fdbf38
--- /dev/null
@@ -0,0 +1,218 @@
+/************************************************************************
+* Copyright 2022 highstreet technologies GmbH
+*
+* 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.
+************************************************************************/
+
+package main
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "io/ioutil"
+       "os"
+
+       "github.com/goccy/go-yaml"
+)
+
+var configYAML Config
+
+// common parts consist of the anchors that are reused for the services
+func createCommonParts() {
+       configYAML.Version = "3.8"
+
+       configYAML.CommonEnvs = make(map[string]string, 1)
+
+       configYAML.CommonEnvs["IPv6_ENABLED"] = "${IPv6_ENABLED}"
+       configYAML.CommonEnvs["SSH_CONNECTIONS"] = "${SSH_CONNECTIONS}"
+       configYAML.CommonEnvs["TLS_CONNECTIONS"] = "${TLS_CONNECTIONS}"
+       configYAML.CommonEnvs["NTS_NF_MOUNT_POINT_ADDRESSING_METHOD"] = "${NTS_NF_MOUNT_POINT_ADDRESSING_METHOD}"
+       configYAML.CommonEnvs["NTS_HOST_IP"] = "${NTS_HOST_IP}"
+       configYAML.CommonEnvs["NTS_HOST_BASE_PORT"] = "${NTS_HOST_BASE_PORT}"
+       configYAML.CommonEnvs["NTS_HOST_NETCONF_SSH_BASE_PORT"] = "${NTS_HOST_NETCONF_SSH_BASE_PORT}"
+       configYAML.CommonEnvs["NTS_HOST_NETCONF_TLS_BASE_PORT"] = "${NTS_HOST_NETCONF_TLS_BASE_PORT}"
+       configYAML.CommonEnvs["NTS_HOST_TRANSFER_FTP_BASE_PORT"] = "${NTS_HOST_TRANSFER_FTP_BASE_PORT}"
+       configYAML.CommonEnvs["NTS_HOST_TRANSFER_SFTP_BASE_PORT"] = "${NTS_HOST_TRANSFER_SFTP_BASE_PORT}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_PROTOCOL"] = "${SDN_CONTROLLER_PROTOCOL}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_IP"] = "${SDNC_OAM_IPv6}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_PORT"] = "${SDNC_REST_PORT}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_CALLHOME_IP"] = "${SDNC_OAM_IPv6}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_CALLHOME_PORT"] = "${SDN_CONTROLLER_CALLHOME_PORT}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_USERNAME"] = "${ADMIN_USERNAME}"
+       configYAML.CommonEnvs["SDN_CONTROLLER_PASSWORD"] = "${ADMIN_PASSWORD}"
+       configYAML.CommonEnvs["VES_COMMON_HEADER_VERSION"] = "${VES_COMMON_HEADER_VERSION}"
+       configYAML.CommonEnvs["VES_ENDPOINT_PROTOCOL"] = "${VES_ENDPOINT_PROTOCOL}"
+       configYAML.CommonEnvs["VES_ENDPOINT_IP"] = "${VES_COLLECTOR_OAM_IPv6}"
+       configYAML.CommonEnvs["VES_ENDPOINT_PORT"] = "${VES_ENDPOINT_PORT}"
+       configYAML.CommonEnvs["VES_ENDPOINT_AUTH_METHOD"] = "${VES_ENDPOINT_AUTH_METHOD}"
+       configYAML.CommonEnvs["VES_ENDPOINT_USERNAME"] = "${VES_ENDPOINT_USERNAME}"
+       configYAML.CommonEnvs["VES_ENDPOINT_PASSWORD"] = "${VES_ENDPOINT_PASSWORD}"
+
+       configYAML.DuEnv = make(map[string]string, 1)
+       configYAML.DuEnv["NTS_NF_STANDALONE_START_FEATURES"] = "datastore-populate ves-heartbeat ves-file-ready ves-pnf-registration web-cut-through"
+
+       configYAML.RuEnv = make(map[string]string, 1)
+       configYAML.RuEnv["NTS_NF_STANDALONE_START_FEATURES"] = "datastore-populate netconf-call-home web-cut-through"
+
+       configYAML.TopoEnv = make(map[string]string, 1)
+       configYAML.TopoEnv["NTS_NF_STANDALONE_START_FEATURES"] = "datastore-populate netconf-call-home web-cut-through"
+
+       var commonNf CommonNf
+       commonNf.StopGracePeriod = "5m"
+       commonNf.CapAdd = append(commonNf.CapAdd, "SYS_ADMIN")
+       commonNf.CapAdd = append(commonNf.CapAdd, "SYS_PTRACE")
+       configYAML.CommonNfs = commonNf
+}
+
+// creates the network information to be used by the services
+func createNetwork() {
+       configYAML.Networks = make(map[string]Network, 1)
+       defaultNetwork := configYAML.Networks["default"]
+
+       defaultNetwork.External = make(map[string]string, 1)
+       defaultNetwork.External["name"] = "oam"
+
+       configYAML.Networks["default"] = defaultNetwork
+}
+
+// creates an O-RU simulator instance as a service
+func addORUasService(name string) {
+       service := configYAML.Services[name]
+
+       service.Image = "${NEXUS3_DOCKER_REPO}nts-ng-o-ran-ru-fh:${NTS_BUILD_VERSION}"
+       service.ContainerName = "ntsim-ng-" + name
+       service.Hostname = name
+
+       commonEnv := &CommonEnv{}
+       ruEnv := &RuEnv{}
+       env := &Env{commonEnv, nil, ruEnv, nil}
+       service.Environment = *env
+
+       configYAML.Services[name] = service
+}
+
+// creates an O-DU simulator instance as a service
+func addODUasService(name string) {
+       service := configYAML.Services[name]
+
+       service.Image = "${NEXUS3_DOCKER_REPO}nts-ng-o-ran-du:${NTS_BUILD_VERSION}"
+       service.ContainerName = "ntsim-ng-" + name
+       service.Hostname = name
+
+       commonEnv := &CommonEnv{}
+       duEnv := &DuEnv{}
+       env := &Env{commonEnv, duEnv, nil, nil}
+       service.Environment = *env
+
+       configYAML.Services[name] = service
+}
+
+// iterates through the topology and creates associated services
+func createServices(topologyJSON *TapiContext) {
+       topology := topologyJSON.TapiCommonContext.TapiTopologyTopologyContext.Topology[0]
+
+       configYAML.Services = make(map[string]Service, 1)
+
+       for _, node := range topology.Node {
+               if node.ORanScTopologyFunction == "o-ran-sc-topology-common:o-ru" {
+                       name := getNodeNameFromUUID(node.UUID, topologyJSON)
+                       addORUasService(name)
+               } else if node.ORanScTopologyFunction == "o-ran-sc-topology-common:o-du" {
+                       name := getNodeNameFromUUID(node.UUID, topologyJSON)
+                       addODUasService(name)
+               }
+       }
+}
+
+// returns the type of O-RAN-SC Topology Function of the input TAPI Node
+func getTypeOfNodeUUID(nodeUUID string, topologyJSON *TapiContext) string {
+       topology := topologyJSON.TapiCommonContext.TapiTopologyTopologyContext.Topology[0]
+
+       for _, node := range topology.Node {
+               if node.UUID == nodeUUID {
+                       return node.ORanScTopologyFunction
+               }
+       }
+
+       return ""
+}
+
+// returns the Node Name of the TAPI Node with the input UUID
+func getNodeNameFromUUID(searchedUUID string, topologyJSON *TapiContext) string {
+       topology := topologyJSON.TapiCommonContext.TapiTopologyTopologyContext.Topology[0]
+
+       for _, node := range topology.Node {
+               if node.UUID == searchedUUID {
+                       for _, name := range node.Name {
+                               if name.ValueName == "topology-node-name" {
+                                       return name.Value
+                               }
+                       }
+               }
+       }
+
+       return ""
+}
+
+func main() {
+       if len(os.Args) > 1 {
+               fmt.Printf("Parsing file %v...\n", os.Args[1])
+       } else {
+               fmt.Printf("Usage: %v <input>\nwhere input is the topology filename in JSON format.\n", os.Args[0])
+               os.Exit(0)
+       }
+
+       if _, err := os.Stat(os.Args[1]); errors.Is(err, os.ErrNotExist) {
+               fmt.Printf("File %v does not exist!\n", os.Args[1])
+               os.Exit(1)
+       }
+
+       topoJSONFile, err := os.Open(os.Args[1])
+       if err != nil {
+               fmt.Println(err)
+               os.Exit(1)
+       }
+       defer topoJSONFile.Close()
+
+       byteValue, _ := ioutil.ReadAll(topoJSONFile)
+
+       var topologyObject TapiContext
+       err = json.Unmarshal(byteValue, &topologyObject)
+       if err != nil {
+               fmt.Println(err)
+               os.Exit(1)
+       }
+
+       if len(topologyObject.TapiCommonContext.TapiTopologyTopologyContext.Topology) < 1 {
+               fmt.Println("Could not find TAPI Topology object in the loaded JSON!")
+               os.Exit(1)
+       }
+
+       createCommonParts()
+       createNetwork()
+       createServices(&topologyObject)
+
+       yamlData, err := yaml.Marshal(&configYAML)
+       if err != nil {
+               fmt.Println(err)
+       }
+
+       fileName := "docker-compose.yaml"
+       err = ioutil.WriteFile(fileName, yamlData, 0644)
+       if err != nil {
+               fmt.Println(err)
+       }
+
+       fmt.Println("File docker-compose.yaml created successfully!")
+}
diff --git a/code/network-topology-parser/tapi-context.go b/code/network-topology-parser/tapi-context.go
new file mode 100644 (file)
index 0000000..bdf683c
--- /dev/null
@@ -0,0 +1,123 @@
+/************************************************************************
+* Copyright 2022 highstreet technologies GmbH
+*
+* 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.
+************************************************************************/
+
+package main
+
+// TapiContext is a view of the TAPI Context, augmented with TAPI Topology
+// https://mholt.github.io/json-to-go/ used to transform the JSON into Go struct
+type TapiContext struct {
+       TapiCommonContext struct {
+               UUID string `json:"uuid"`
+               Name []struct {
+                       ValueName string `json:"value-name"`
+                       Value     string `json:"value"`
+               } `json:"name"`
+               TapiTopologyTopologyContext struct {
+                       Topology []struct {
+                               UUID string `json:"uuid"`
+                               Name []struct {
+                                       ValueName string `json:"value-name"`
+                                       Value     string `json:"value"`
+                               } `json:"name"`
+                               LayerProtocolName []string `json:"layer-protocol-name"`
+                               Node              []struct {
+                                       UUID string `json:"uuid"`
+                                       Name []struct {
+                                               ValueName string `json:"value-name"`
+                                               Value     string `json:"value"`
+                                       } `json:"name"`
+                                       OwnedNodeEdgePoint []struct {
+                                               UUID string `json:"uuid"`
+                                               Name []struct {
+                                                       ValueName string `json:"value-name"`
+                                                       Value     string `json:"value"`
+                                               } `json:"name"`
+                                               AdministrativeState                string   `json:"administrative-state"`
+                                               OperationalState                   string   `json:"operational-state"`
+                                               LifecycleState                     string   `json:"lifecycle-state"`
+                                               LinkPortRole                       string   `json:"link-port-role"`
+                                               LayerProtocolName                  string   `json:"layer-protocol-name"`
+                                               SupportedCepLayerProtocolQualifier []string `json:"supported-cep-layer-protocol-qualifier"`
+                                               LinkPortDirection                  string   `json:"link-port-direction"`
+                                               TerminationState                   string   `json:"termination-state"`
+                                               TerminationDirection               string   `json:"termination-direction"`
+                                       } `json:"owned-node-edge-point"`
+                                       AdministrativeState string   `json:"administrative-state"`
+                                       OperationalState    string   `json:"operational-state"`
+                                       LifecycleState      string   `json:"lifecycle-state"`
+                                       LayerProtocolName   []string `json:"layer-protocol-name"`
+                                       CostCharacteristic  []struct {
+                                               CostName      string `json:"cost-name"`
+                                               CostAlgorithm string `json:"cost-algorithm"`
+                                               CostValue     string `json:"cost-value"`
+                                       } `json:"cost-characteristic"`
+                                       LatencyCharacteristic []struct {
+                                               TrafficPropertyName         string `json:"traffic-property-name"`
+                                               QueingLatencyCharacteristic string `json:"queing-latency-characteristic"`
+                                               FixedLatencyCharacteristic  string `json:"fixed-latency-characteristic"`
+                                               JitterCharacteristic        string `json:"jitter-characteristic"`
+                                               WanderCharacteristic        string `json:"wander-characteristic"`
+                                       } `json:"latency-characteristic"`
+                                       ORanScTopologyFunction    string `json:"o-ran-sc-topology:function"`
+                                       ORanScTopologyGeolocation struct {
+                                               Longitude string `json:"longitude"`
+                                               Latitude  string `json:"latitude"`
+                                               Altitude  string `json:"altitude"`
+                                       } `json:"o-ran-sc-topology:geolocation"`
+                               } `json:"node"`
+                               Link []struct {
+                                       UUID string `json:"uuid"`
+                                       Name []struct {
+                                               ValueName string `json:"value-name"`
+                                               Value     string `json:"value"`
+                                       } `json:"name"`
+                                       TransitionedLayerProtocolName []string `json:"transitioned-layer-protocol-name"`
+                                       AdministrativeState           string   `json:"administrative-state"`
+                                       OperationalState              string   `json:"operational-state"`
+                                       Direction                     string   `json:"direction"`
+                                       LifecycleState                string   `json:"lifecycle-state"`
+                                       NodeEdgePoint                 []struct {
+                                               TopologyUUID      string `json:"topology-uuid"`
+                                               NodeUUID          string `json:"node-uuid"`
+                                               NodeEdgePointUUID string `json:"node-edge-point-uuid"`
+                                       } `json:"node-edge-point"`
+                                       LatencyCharacteristic []struct {
+                                               TrafficPropertyName         string `json:"traffic-property-name"`
+                                               QueingLatencyCharacteristic string `json:"queing-latency-characteristic"`
+                                               FixedLatencyCharacteristic  string `json:"fixed-latency-characteristic"`
+                                               JitterCharacteristic        string `json:"jitter-characteristic"`
+                                               WanderCharacteristic        string `json:"wander-characteristic"`
+                                       } `json:"latency-characteristic"`
+                                       LayerProtocolName  []string `json:"layer-protocol-name"`
+                                       RiskCharacteristic []struct {
+                                               RiskCharacteristicName string   `json:"risk-characteristic-name"`
+                                               RiskIdentifierList     []string `json:"risk-identifier-list"`
+                                       } `json:"risk-characteristic"`
+                                       ValidationMechanism []struct {
+                                               ValidationMechanism             string `json:"validation-mechanism"`
+                                               ValidationRobustness            string `json:"validation-robustness"`
+                                               LayerProtocolAdjacencyValidated string `json:"layer-protocol-adjacency-validated"`
+                                       } `json:"validation-mechanism"`
+                                       CostCharacteristic []struct {
+                                               CostName      string `json:"cost-name"`
+                                               CostAlgorithm string `json:"cost-algorithm"`
+                                               CostValue     string `json:"cost-value"`
+                                       } `json:"cost-characteristic"`
+                               } `json:"link"`
+                       } `json:"topology"`
+               } `json:"tapi-topology:topology-context"`
+       } `json:"tapi-common:context"`
+}
diff --git a/code/network-topology-parser/yaml.go b/code/network-topology-parser/yaml.go
new file mode 100644 (file)
index 0000000..bdee218
--- /dev/null
@@ -0,0 +1,91 @@
+/************************************************************************
+* Copyright 2022 highstreet technologies GmbH
+*
+* 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.
+************************************************************************/
+
+package main
+
+// Config is the entire docker-compose YAML object
+type Config struct {
+    Version  string
+    CommonEnvs map[string]string `yaml:"x-common_env,anchor=common_env"`
+    DuEnv map[string]string `yaml:"x-du_env,anchor=du_env"`
+    RuEnv map[string]string `yaml:"x-ru_env,anchor=ru_env"`
+    TopoEnv map[string]string `yaml:"x-topo_env,anchor=topo_env"`
+    CommonNfs CommonNf `yaml:"x-nf,anchor=common_nf"`
+    Networks map[string]Network
+    Volumes  map[string]Volume `yaml:",omitempty"`
+    Services map[string]Service
+}
+
+// CommonNf is the common network function alias
+type CommonNf struct {
+    StopGracePeriod     string `yaml:"stop_grace_period"`
+    CapAdd              []string `yaml:"cap_add"`
+}
+
+// Network is the network YAML object
+type Network struct {
+    Driver          string `yaml:",omitempty"`
+    DriverOpts      map[string]string `yaml:"driver_opts,omitempty"`
+    External        map[string]string `yaml:",omitempty"`
+}
+
+// Volume is the volume YAML object
+type Volume struct {
+    Driver, External string
+    DriverOpts       map[string]string `yaml:"driver_opts"`
+}
+
+// Service is the service YAML object
+type Service struct {
+    *Service `yaml:",inline,alias=common_nf"`
+    ContainerName                     string `yaml:"container_name"`
+    Image                             string
+    Networks, Ports, Volumes, Command, Links []string `yaml:",omitempty"`
+    VolumesFrom                       []string `yaml:"volumes_from,omitempty"`
+    DependsOn                         []string `yaml:"depends_on,omitempty"`
+    CapAdd                            []string `yaml:"cap_add,omitempty"`
+    Build                             struct{ Context, Dockerfile string } `yaml:",omitempty"`
+    Environment                       Env `yaml:"environment"`
+    Hostname                          string
+}
+
+// Env is the environment YAML object
+type Env struct {
+    *CommonEnv `yaml:",omitempty,inline,alias=common_env"`
+    *DuEnv `yaml:",omitempty,inline,alias=du_env"`
+    *RuEnv `yaml:",omitempty,inline,alias=ru_env"`
+    *TopoEnv `yaml:",omitempty,inline,alias=topo_env"`
+}
+
+// CommonEnv is the common_env anchor
+type CommonEnv struct {
+    *CommonEnv `yaml:",omitempty,inline,alias=common_env"`
+}
+
+// DuEnv is the du_env anchor
+type DuEnv struct {
+    *DuEnv `yaml:",omitempty,inline,alias=du_env"`
+}
+
+// RuEnv is the ru_env anchor
+type RuEnv struct {
+    *RuEnv `yaml:",omitempty,inline,alias=ru_env"`
+}
+
+// TopoEnv is the topo_env anchor
+type TopoEnv struct {
+    *TopoEnv `yaml:",omitempty,inline,alias=topo_env"`
+}