Hosting Externally Accessible Services

How to host services in your experiment that are internet accessible.

Overview

Nodes can host services that are accessible from the internet. The experimenter adds an ingress to a node when modeling the experiment topology and Merge will build the network plumbing required to expose the node’s service to the outside world when the experiment is created (materialized).

The ingress has two parts, a protocol and a port. The port is the network port on which the service on the node listens. The protocol is the expected protocol of the traffic. Depending on the protocol given, the ingress is expressed differently. There are four protocols supported and two ingress expressions. These are listed below.

TypeHosted ServiceExternal Access
httpweb serverreverse proxy
httpsweb serverreverse proxy
tcpport based tcpdynamic port
udpport based udpdynamic port

The http and https protocols generate reverse proxy access which is URI based. The access is via an HTTP URL. It is deterministic (based on names given in the model and during experiment realization), thus experimenters know exactly how to reach the node service once the experiment is realized. How to do this is explained below.

The tcp and udp ingresses are assigned a random gateway port at materialization time. So experimenters do not know the exact ingress details prior to experiment materialization and the external port will change for every materialization. Experimenters will need to ask Merge what the assigned external port is after materialization completes.

The format of the reverse proxy path is http://[gateway]/[rid.eid.pid]/[node]/[port] where:

NameDescriptionExample
gatewaythe fully qualified domain name of the testbed site gatewayifr0.mod.deterlab.net
ridthe name of the realizationmyreal
eidthe name of the experimentmyexperiment
pidthe name of the projectmyproject
nodethe experiment name of the node hosting the serviceserver1
portthe port on the experiment node on which the host listens8080

So given the examples in the table above, the path will be http://ifr0.mod.deterlab.net/myreal.myexperiment.myproject/server1/8080.

The tcp and udp ingresses do not have a URL path as these services are not HTTP based. The ingress specifies the port the service listens on. As there is no path, Merge assigns a random external port which is then routed to the given experiment node port. This means that the experimenter needs to check which port was assigned after experiment materialization completes.

Creating an Ingress

To create an ingress use the ingress function on a declared node in the experiment model. The ingress function takes two arguments: the protocol and port. The protocol type is declared by the mergexp package and is one of http, https, tcp, or udp. The port is a positive number and is the port on which the host’s service will listen for connections.

Here is an example of a web server ingress on port 8080 and a tcp server on port 1234:

from mergexp import *

net = Network('ingress example')

web = net.node("webserver")
web.ingress(http, 8080)

netcat = net.node("netcat")
netcat.ingress(tcp, 1234)

net.connect([web, netcat])
experiment(net)

Note that the protocol identifiers http, https, tcp, and udp are in scope as we’ve exported * from the mergexp package.

Once materialized, the http can be accessed via the proxy path. Assuming details above the following will work:

> curl http://ifr0.mod.deterlab.net/rlz.exp.proj/webserver/8080

And assume Merge has assigned port 8134 to the tcp ingress. Then that service can be reached like so:

> nc -z ifr0.mod.deterlab.net 8134

Ingress Details Location

The ingress details are given by the Portal is a few places. The Launch dashboard materialization synopsis, the Launch materialization details page, or the output of the mrg show materialization command.

The Launch dashboard gives a summary of ingresses for the materialization. http and https ingresses are direct links to the experiment node, The tcp and udp ingresses are just text. Example:

Ingresses Summary on Dashboard

The materialization details tab shows more information: Ingresses Data on Materialization Details Page

In the example above, the tcp service can be reached by accessing ifr0.mod.deterlab.net on port 8193. The Gateway Port field in the table is the random port chosen by Merge on the testbed gateway. This is the external port of the ingress that routes to your experiment node service.

The http service is at the given (and linked) URL.

The mrg command line utility also shows ingress details of a materialization. Use the mrg show materialization command. It is suggested that you give the -j option to the command to get JSON output and parse that with jq. The ingresses details are in the top level of the JSON under the “ingresses” key. Here is an example:

[glawler ~]$ mrg show materialization rlz.ingress.glawler -j | jq -r .ingresses
{
  "ingresses": [
    {
      "mzid": "rlz.ingress.glawler",
      "protocol": "http",
      "hostname": "nodea",
      "hostport": 8080,
      "hostaddr": "172.30.0.11",
      "gateway": "ifr0.mod.deterlab.net",
      "gatewayport": 80,
      "ingress": "http://ifr0.mod.deterlab.net:80/rlz.ingress.glawler/nodea/8080"
    },
    {
      "mzid": "rlz.ingress.glawler",
      "protocol": "tcp",
      "hostname": "nodeb",
      "hostport": 21234,
      "hostaddr": "172.30.0.12",
      "gateway": "ifr0.mod.deterlab.net",
      "gatewayport": 8193,
      "ingress": "ifr0.mod.deterlab.net:8193"
    }
  ]
}
[glawler ~] $ 

This example shows the same two ingresses as above.

JSON output is not required of course but is easier to read in the current mrg version. Here is the output for the same materialization, using grep to trim to just the ingress information:

[glawler@ma0 ~]$ mrg show materialization rlz.ingress.glawler | grep ^Ingresses -A 1000
Ingresses:
        nodea:8080 [http] --> http://ifr0.mod.deterlab.net:80/rlz.ingress.glawler/nodea/8080
        nodeb:21234 [tcp] --> ifr0.mod.deterlab.net:8193
[glawler@ma0 ~]$ 

Last modified April 2, 2024: update doc (6f86581)