--- /dev/null
+
+topology-parser
+*.json
+*.yaml
+.vscode
--- /dev/null
+# 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.
--- /dev/null
+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
+)
--- /dev/null
+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=
--- /dev/null
+/************************************************************************
+* 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!")
+}
--- /dev/null
+/************************************************************************
+* 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"`
+}
--- /dev/null
+/************************************************************************
+* 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"`
+}