diff --git a/src/tests/OpenFlow-Ryu-NBI-Request/ADD LINKS.json b/src/tests/OpenFlow-Ryu-NBI-Request/ADD LINKS.json deleted file mode 100644 index 1825145eea8d79284a6dda2804a7844a9bd873c9..0000000000000000000000000000000000000000 --- a/src/tests/OpenFlow-Ryu-NBI-Request/ADD LINKS.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "links": [ - { - "link_id": {"link_uuid": {"uuid": "h1/h1-eth0==0000000000000002/s2-eth3"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "h1"}}, "endpoint_uuid": {"uuid": "h1-eth0"}}, - {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth3"}} - ] -}, -{ - "link_id": {"link_uuid": {"uuid": "0000000000000002/s2-eth3==h1/h1-eth0"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth3"}}, - {"device_id": {"device_uuid": {"uuid": "h1"}}, "endpoint_uuid": {"uuid": "h1-eth0"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "h2/h2-eth0==0000000000000002/s2-eth4"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "h2"}}, "endpoint_uuid": {"uuid": "h2-eth0"}}, - {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth4"}} - ] -}, -{ - "link_id": {"link_uuid": {"uuid": "0000000000000002/s2-eth4==h2/h2-eth0"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth4"}}, - {"device_id": {"device_uuid": {"uuid": "h2"}}, "endpoint_uuid": {"uuid": "h2-eth0"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "h3/h3-eth0==0000000000000005/s5-eth3"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "h3"}}, "endpoint_uuid": {"uuid": "h3-eth0"}}, - {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth3"}} - ] -}, -{ - "link_id": {"link_uuid": {"uuid": "0000000000000005/s5-eth3==h3/h3-eth0"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth3"}}, - {"device_id": {"device_uuid": {"uuid": "h3"}}, "endpoint_uuid": {"uuid": "h3-eth0"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "h4/h4-eth0==0000000000000005/s5-eth4"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "h4"}}, "endpoint_uuid": {"uuid": "h4-eth0"}}, - {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth4"}} - ] -}, -{ - "link_id": {"link_uuid": {"uuid": "0000000000000005/s5-eth4==h4/h4-eth0"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth4"}}, - {"device_id": {"device_uuid": {"uuid": "h4"}}, "endpoint_uuid": {"uuid": "h4-eth0"}} - ] - } - ] -} - diff --git a/src/tests/OpenFlow-Ryu-NBI-Request/OpenFlow.json b/src/tests/OpenFlow-Ryu-NBI-Request/OpenFlow.json deleted file mode 100644 index 1c9928376b89ae199b9efe701daf080140aa1281..0000000000000000000000000000000000000000 --- a/src/tests/OpenFlow-Ryu-NBI-Request/OpenFlow.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "contexts": [ - {"context_id": {"context_uuid": {"uuid": "admin"}}} - ], - "topologies": [ - { - "topology_id": { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_uuid": {"uuid": "admin"} - } - } - ], - "devices": [ - { - "device_id": {"device_uuid": {"uuid": "RYU"}}, "device_type": "openflow-ryu-controller", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.1.7.197"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8080"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}} - ]}, - "device_drivers": ["DEVICEDRIVER_RYU"] - }, - { - "device_id": {"device_uuid": {"uuid": "h1"}}, "device_type": "emu-client", "device_drivers": [0], - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"uuid": "h1-eth0", "type": "copper/internal","mac_address":"00:00:00:00:00:01"}, - {"uuid": "int", "type": "copper/internal"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "h2"}}, "device_type": "emu-client", "device_drivers": [0], - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"uuid": "h2-eth0", "type": "copper/internal"}, - {"uuid": "int", "type": "copper/internal"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "h3"}}, "device_type": "emu-client", "device_drivers": [0], - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"uuid": "h3-eth0", "type": "copper/internal"}, - {"uuid": "int", "type": "copper/internal"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "h4"}}, "device_type": "emu-client", "device_drivers": [0], - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"uuid": "h4-eth0", "type": "copper/internal"}, - {"uuid": "int", "type": "copper/internal"} - ]}}} - ]} - } - ] -} - - - diff --git a/src/tests/OpenFlow-Ryu-NBI-Request/commands.txt b/src/tests/OpenFlow-Ryu-NBI-Request/commands.txt deleted file mode 100644 index d7a7291c21d2eef52e0498ffe4d52fac1d036667..0000000000000000000000000000000000000000 --- a/src/tests/OpenFlow-Ryu-NBI-Request/commands.txt +++ /dev/null @@ -1,3 +0,0 @@ -sudo mn -c -ryu-manager --observe-links ryu.app.ofctl_rest ryu.app.gui_topology.gui_topology -sudo python3 ~/mininet/custom_pentagon_topology.py \ No newline at end of file diff --git a/src/tests/OpenFlow-Ryu-NBI-Request/custom_pentagon_topology.py b/src/tests/OpenFlow-Ryu-NBI-Request/custom_pentagon_topology.py deleted file mode 100644 index 801e763078c87a55aeb8150708c62f9754e21b2a..0000000000000000000000000000000000000000 --- a/src/tests/OpenFlow-Ryu-NBI-Request/custom_pentagon_topology.py +++ /dev/null @@ -1,40 +0,0 @@ -from mininet.topo import Topo -from mininet.net import Mininet -from mininet.node import RemoteController -from mininet.cli import CLI -from mininet.link import TCLink - -class PentagonTopo(Topo): - def build(self): - sw1 = self.addSwitch('s1') - sw2 = self.addSwitch('s2') - sw3 = self.addSwitch('s3') - sw4 = self.addSwitch('s4') - sw5 = self.addSwitch('s5') - - h1 = self.addHost('h1', ip="10.0.0.1", mac="00:00:00:00:00:01") - h2 = self.addHost('h2', ip="10.0.0.2", mac="00:00:00:00:00:02") - h3 = self.addHost('h3', ip="10.0.0.3", mac="00:00:00:00:00:03") - h4 = self.addHost('h4', ip="10.0.0.4", mac="00:00:00:00:00:04") - - self.addLink(sw1, sw2) - self.addLink(sw2, sw3) - self.addLink(sw3, sw4) - self.addLink(sw4, sw5) - self.addLink(sw5, sw1) - - self.addLink(h1, sw2) - self.addLink(h2, sw2) - self.addLink(h3, sw5) - self.addLink(h4, sw5) - -if __name__ == '__main__': - topo = PentagonTopo() - net = Mininet(topo=topo, controller=lambda name: RemoteController(name, ip='127.0.0.1'), link=TCLink) - - net.start() - net.staticArp() - - print("Custom Pentagon Topology is up with static ARP.") - CLI(net) - net.stop() diff --git a/src/tests/ryu-openflow/Ryu.Dockerfile b/src/tests/ryu-openflow/Ryu.Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..37248f8b7d3e11942e01d2339225c88c079f3e39 --- /dev/null +++ b/src/tests/ryu-openflow/Ryu.Dockerfile @@ -0,0 +1,31 @@ +# Copyright 2022-2024 ETSI 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. + +FROM python:3.9-slim + +RUN apt-get update && apt-get install -y --no-install-recommends iproute2 tree git && rm -rf /var/lib/apt/lists/* + +RUN pip install --no-cache-dir --upgrade pip +# NOTE: Ryu 4.34 expects eventlet.wsgi.ALREADY_HANDLED, which disappears in Eventlet ≥ 0.30.3. +RUN pip install --no-cache-dir "setuptools" "wheel" "git+https://github.com/faucetsdn/ryu.git@v4.34" "eventlet<0.30.3" + +#COPY apps/ /opt/ryu-apps/ # Copy Ryu Apps, if any +WORKDIR /opt/ryu-apps + +# --- OpenFlow & Ryu REST API ports --- +EXPOSE 6653/tcp 8080/tcp + +RUN tree -a /usr/local/lib/python3.9/site-packages/ryu/app/gui_topology/ + +CMD ["ryu-manager", "--verbose", "--observe-links", "ryu.app.ofctl_rest", "ryu.app.gui_topology.gui_topology"] diff --git a/src/tests/ryu-openflow/commands.txt b/src/tests/ryu-openflow/commands.txt new file mode 100644 index 0000000000000000000000000000000000000000..17f331ffa8c6332f00c4ce6b0e093680d107fc42 --- /dev/null +++ b/src/tests/ryu-openflow/commands.txt @@ -0,0 +1,28 @@ +sudo mn -c +ryu-manager --observe-links ryu.app.ofctl_rest ryu.app.gui_topology.gui_topology +sudo python3 ~/tfs-ctrl/src/tests/ryu-openflow/custom_pentagon_topology.py + +curl -X POST \ + --header "Content-Type: application/json" \ + --data @ietf-l3vpn-service.json \ + --user "admin:admin" \ + http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services + + + +4. Build and run +# from ryu-docker/ +docker compose build # or: docker build -t ryu-local . +docker compose up -d # or: docker run -d --network host ryu-local +You should now see the usual Ryu banner in docker compose logs -f. + +5. Start Mininet on the host +sudo mn --topo single,3 \ + --controller remote,ip=127.0.0.1,port=6653 \ + --switch ovs,protocols=OpenFlow13 +127.0.0.1 works because the container is in host network mode. +If you prefer normal bridge networking, publish the port instead: + +docker run -d -p 6653:6653 -p 8080:8080 ryu-local +# …then tell Mininet ip=<host-IP> +Verify the switch talks OpenFlow 1.3 (protocols=OpenFlow13) unless your app requires 1.0. diff --git a/src/tests/ryu-openflow/custom_pentagon_topology.py b/src/tests/ryu-openflow/custom_pentagon_topology.py new file mode 100644 index 0000000000000000000000000000000000000000..9d581e40babdb091b8d90865e71618ffeb7f663e --- /dev/null +++ b/src/tests/ryu-openflow/custom_pentagon_topology.py @@ -0,0 +1,55 @@ +# Copyright 2022-2024 ETSI 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. + + +from mininet.topo import Topo +from mininet.net import Mininet +from mininet.node import RemoteController +from mininet.cli import CLI +from mininet.link import TCLink + +class PentagonTopo(Topo): + def build(self): + sw1 = self.addSwitch('s1') + sw2 = self.addSwitch('s2') + sw3 = self.addSwitch('s3') + sw4 = self.addSwitch('s4') + sw5 = self.addSwitch('s5') + + h1 = self.addHost('h1', ip='10.0.0.1', mac='00:00:00:00:00:01') + h2 = self.addHost('h2', ip='10.0.0.2', mac='00:00:00:00:00:02') + h3 = self.addHost('h3', ip='10.0.0.3', mac='00:00:00:00:00:03') + h4 = self.addHost('h4', ip='10.0.0.4', mac='00:00:00:00:00:04') + + self.addLink(sw1, sw2) + self.addLink(sw2, sw3) + self.addLink(sw3, sw4) + self.addLink(sw4, sw5) + self.addLink(sw5, sw1) + + self.addLink(h1, sw2) + self.addLink(h2, sw2) + self.addLink(h3, sw5) + self.addLink(h4, sw5) + +if __name__ == '__main__': + topo = PentagonTopo() + net = Mininet(topo=topo, controller=lambda name: RemoteController(name, ip='127.0.0.1'), link=TCLink) + + net.start() + net.staticArp() + + print('Custom Pentagon Topology is up with static ARP.') + CLI(net) + net.stop() diff --git a/src/tests/ryu-openflow/docker-compose.yml b/src/tests/ryu-openflow/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..adb37e61d6b92744529fbe7fd8028e76f503d99a --- /dev/null +++ b/src/tests/ryu-openflow/docker-compose.yml @@ -0,0 +1,20 @@ +# Copyright 2022-2024 ETSI 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. + +services: + ryu: + network_mode: host + build: + context: . + dockerfile: Ryu.Dockerfile diff --git a/src/tests/OpenFlow-Ryu-NBI-Request/ietf-l3vpn-service.json b/src/tests/ryu-openflow/ietf-l3vpn-service.json similarity index 100% rename from src/tests/OpenFlow-Ryu-NBI-Request/ietf-l3vpn-service.json rename to src/tests/ryu-openflow/ietf-l3vpn-service.json diff --git a/src/tests/ryu-openflow/tfs-topology.json b/src/tests/ryu-openflow/tfs-topology.json new file mode 100644 index 0000000000000000000000000000000000000000..e427f16ea95833a1480a84c4fe83d7539c2707ac --- /dev/null +++ b/src/tests/ryu-openflow/tfs-topology.json @@ -0,0 +1,125 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "admin"} + } + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "RYU"}}, "device_type": "openflow-ryu-controller", + "device_drivers": ["DEVICEDRIVER_RYU"], "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8080"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "h1"}}, "device_type": "emu-client", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "h1-eth0", "type": "copper/internal"}, + {"uuid": "int", "type": "copper/internal"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "h2"}}, "device_type": "emu-client", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "h2-eth0", "type": "copper/internal"}, + {"uuid": "int", "type": "copper/internal"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "h3"}}, "device_type": "emu-client", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "h3-eth0", "type": "copper/internal"}, + {"uuid": "int", "type": "copper/internal"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "h4"}}, "device_type": "emu-client", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "h4-eth0", "type": "copper/internal"}, + {"uuid": "int", "type": "copper/internal"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "h1/h1-eth0==0000000000000002/s2-eth3"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "h1"}}, "endpoint_uuid": {"uuid": "h1-eth0"}}, + {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "0000000000000002/s2-eth3==h1/h1-eth0"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth3"}}, + {"device_id": {"device_uuid": {"uuid": "h1"}}, "endpoint_uuid": {"uuid": "h1-eth0"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "h2/h2-eth0==0000000000000002/s2-eth4"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "h2"}}, "endpoint_uuid": {"uuid": "h2-eth0"}}, + {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "0000000000000002/s2-eth4==h2/h2-eth0"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "0000000000000002"}}, "endpoint_uuid": {"uuid": "s2-eth4"}}, + {"device_id": {"device_uuid": {"uuid": "h2"}}, "endpoint_uuid": {"uuid": "h2-eth0"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "h3/h3-eth0==0000000000000005/s5-eth3"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "h3"}}, "endpoint_uuid": {"uuid": "h3-eth0"}}, + {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "0000000000000005/s5-eth3==h3/h3-eth0"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth3"}}, + {"device_id": {"device_uuid": {"uuid": "h3"}}, "endpoint_uuid": {"uuid": "h3-eth0"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "h4/h4-eth0==0000000000000005/s5-eth4"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "h4"}}, "endpoint_uuid": {"uuid": "h4-eth0"}}, + {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "0000000000000005/s5-eth4==h4/h4-eth0"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "0000000000000005"}}, "endpoint_uuid": {"uuid": "s5-eth4"}}, + {"device_id": {"device_uuid": {"uuid": "h4"}}, "endpoint_uuid": {"uuid": "h4-eth0"}} + ] + } + ] +}