Happy is a tool created by Nest labs for lightweight orchestration of simulated network topologies. Happy is useful for the development and testing of IoT home area networks.

With Happy, you can:

In this Codelab, you'll learn how to get started with Happy, as well as the basics of Weave for IoT devices. The Weave implementation you'll use is OpenWeave, an open-source version released by Nest.

What you'll learn

What you'll need

For this Codelab, we'll use Google Cloud Shell to run a Linux Docker container with Happy and Weave already built for you.

Log in to Google Cloud Console

Click the I/O Codelabs extension in the top-right of a Chrome browser tab to obtain a temporary account:

These temporary accounts have existing projects that are set up with billing so that there are no costs associated for you with running this codelab.

Use these credentials to log into the machine or to open a new Google Cloud Console window https://console.cloud.google.com/. Accept the new account Terms of Service and any updates to Terms of Service.

Here's what you should see once logged in:

When presented with this console landing page, please select the only project available. Alternatively, from the console home page, click on Select a Project:

Activate Google Cloud Shell

From the GCP Console click the Cloud Shell icon on the top right toolbar:

It should only take a few moments to provision and connect to the environment:

This virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on the Google Cloud, greatly enhancing network performance and authentication.

Once connected to Cloud Shell, click the Expand to new window button so that command output is easier to see:

Pull the happy-codelab Docker container. It may take a few moments to start downloading.

$ gcloud docker --verbosity=error -- pull gcr.io/open-weave/happy-codelab:latest

Connect to the bash shell within the Docker container:

$ docker run --rm --privileged -ti gcr.io/open-weave/happy-codelab:latest bash

You now should have a prompt similar to this:

root@c0f3912a74ff:/#

Be aware that even though most commands in this Codelab are shown with a $ prompt, they are all entered at this Docker bash # prompt.

Let's create the following three-node topology with Happy.

This topology is an example of a simple Home Area Network (HAN). In this HAN, two nodes are connected together in a Thread network, and one of those nodes connects to a third via Wi-Fi. This node can also be connected to a wireless router in the home to provide internet connectivity for the entire HAN. More about this later.

First, create the three nodes:

$ happy-node-add 01ThreadNode
$ happy-node-add 02BorderRouter
$ happy-node-add 03WiFiNode

Let's make sure they exist:

$ happy-node-list
01ThreadNode
02BorderRouter
03WiFiNode

Now let's create some networks:

$ happy-network-add ThreadNetwork thread
$ happy-network-add WiFiNetwork wifi

Verify that the networks exist:

$ happy-network-list
ThreadNetwork
WiFiNetwork

Check the Happy state:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP
    WiFiNetwork         wifi      UP

NODES      Name    Interface    Type                                          IPs
   01ThreadNode
 02BorderRouter
     03WiFiNode

It's not enough just to bring a network up—we have to add nodes to the networks. Following our topology diagram, add each node to the appropriate network(s):

$ happy-node-join 01ThreadNode ThreadNetwork
$ happy-node-join 02BorderRouter ThreadNetwork
$ happy-node-join 02BorderRouter WiFiNetwork
$ happy-node-join 03WiFiNode WiFiNetwork

Note that 02BorderRouter was added to both ThreadNetwork and WiFiNetwork. That's because as a Border Router within our HAN, this node connects the two individual networks together.

Check the Happy state. Each node's interfaces are up:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP
    WiFiNetwork         wifi      UP

NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread
 02BorderRouter        wpan0  thread
                       wlan0    wifi
     03WiFiNode        wlan0    wifi

Our topology now looks like this:

The last step in bringing up our Happy network is to assign IP addresses to each interface on each node. Specify the IP prefix for a network, and Happy automatically assigns IP addresses for you.

Because the Thread protocol uses IPv6, add an IPv6 prefix to the Thread network:

$ happy-network-address ThreadNetwork 2001:db8:1:2::

Check the Happy state. The Thread interfaces on each Thread node have IP addresses:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP                       2001:0db8:0001:0002/64

    WiFiNetwork         wifi      UP

NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread   2001:0db8:0001:0002:3e36:13ff:fe33:732e/64

 02BorderRouter        wpan0  thread   2001:0db8:0001:0002:a651:3eff:fe92:6dbc/64

                       wlan0    wifi
     03WiFiNode        wlan0    wifi

For the WiFi network, add both IPv4 and IPv6 prefixes:

$ happy-network-address WiFiNetwork 2001:db8:a:b::
$ happy-network-address WiFiNetwork 10.0.1.0

Check the Happy state once more. All interfaces have assigned IP addresses, with two for each Wi-Fi interface:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP                       2001:0db8:0001:0002/64

    WiFiNetwork         wifi      UP                       2001:0db8:000a:000b/64
                                                                        10.0.1/24


NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread   2001:0db8:0001:0002:3e36:13ff:fe33:732e/64

 02BorderRouter        wpan0  thread   2001:0db8:0001:0002:a651:3eff:fe92:6dbc/64

                       wlan0    wifi                                  10.0.1.2/24
                                       2001:0db8:000a:000b:426c:38ff:fe90:01e6/64

     03WiFiNode        wlan0    wifi   2001:0db8:000a:000b:9aae:2bff:fe71:62fa/64
                                                                      10.0.1.3/24

Here is our updated topology:

Now that our Happy network is up and running, let's test its connectivity by pinging the other nodes from 01ThreadNode:

$ happy-ping 01ThreadNode 02BorderRouter
[Ping] ping from 01ThreadNode to 02BorderRouter on address
    10.0.1.2 -> 100% packet loss
[Ping] ping from 01ThreadNode to 02BorderRouter on address
    2001:0db8:0001:0002:a651:3eff:fe92:6dbc -> 0% packet loss
[Ping] ping from 01ThreadNode to 02BorderRouter on address
    2001:0db8:000a:000b:426c:38ff:fe90:01e6 -> 100% packet loss

$ happy-ping 01ThreadNode 03WiFiNode
[Ping] ping from 01ThreadNode to 03WiFiNode on address
    2001:0db8:000a:000b:9aae:2bff:fe71:62fa -> 100% packet loss
[Ping] ping from 01ThreadNode to 03WiFiNode on address
    10.0.1.3 -> 100% packet loss

The happy-ping command tries to ping every IP address for every interface on the target node. We can ignore the IPv4 addresses because Thread only uses IPv6.

Note that only one IPv6 ping was successful: the one on 02BorderRouter's wpan0 interface, which is the only address 01ThreadNode can directly reach:

The other IPv6 addresses failed because forwarding has not been enabled between wpan0 and wlan0 on 02BorderRouter. Thus, 01ThreadNode has no idea 03WiFiNode exists, or how to reach it. Happy has brought up the simulated network, but has not enabled all routing and forwarding between nodes.

Add routes

To route IPv6 traffic across the HAN, add the proper routes to each node in each network, in both directions (so the ping knows how to return to the source node).

For each node, you'll need to know:

For our three node network, that gives us the following:

from Source Network

to Target Network

via Gateway

ThreadNetwork

WiFiNetwork

02BorderRouter wlan0

2001:db8:1:2::/64 prefix

WiFiNetwork

ThreadNetwork

02BorderRouter wpan0

2001:db8:a:b::/64 prefix

This can be done individually for each node with happy-node-route, but it's easier to do it for all nodes in each network with happy-network-route.

$ happy-network-route -a -i ThreadNetwork -t default -v 02BorderRouter -p 2001:db8:1:2::/64
$ happy-network-route -a -i WiFiNetwork -t default -v 02BorderRouter -p 2001:db8:a:b::/64

For an explanation of the command-line flags, use happy-network-route -h.

The happy-network-route command also turns on IPv4 and IPv6 forwarding for each node, as needed. This allows traffic to route from one interface to another within a node.

Now retry the ping:

$ happy-ping 01ThreadNode 02BorderRouter
[Ping] ping from 01ThreadNode to 02BorderRouter on address
    10.0.1.2 -> 100% packet loss
[Ping] ping from 01ThreadNode to 02BorderRouter on address
    2001:0db8:0001:0002:a651:3eff:fe92:6dbc -> 0% packet loss
[Ping] ping from 01ThreadNode to 02BorderRouter on address
    2001:0db8:000a:000b:426c:38ff:fe90:01e6 -> 0% packet loss

Both IPv6 pings work! With forwarding on, it knows how to reach the wlan0 interface. The IPv4 ping still fails, because we only configured IPv6 routes and forwarding (also because Thread doesn't run over IPv4).

Since we added network routes to both sides, let's ping across networks:

$ happy-ping 01ThreadNode 03WiFiNode
[Ping] ping from 01ThreadNode to 03WiFiNode on address
    2001:0db8:000a:000b:9aae:2bff:fe71:62fa -> 0% packet loss
[Ping] ping from 01ThreadNode to 03WiFiNode on address
    10.0.1.3 -> 100% packet loss

The IPv6 ping works as expected. You now have a fully-functional, simulated IPv6 HAN.

To enable a more secure and reliable way of connecting everything together, let's add Weave on top of the HAN.

Weave is a network application layer that provides the secure and reliable communications backbone for Nest products. We can add Weave functionality with OpenWeave, the open-source version of Weave.

An implementation of Weave is called a "fabric". A Weave fabric is a network that comprises all HAN nodes, the Nest Service, and any mobile devices participating in the HAN. It sits on top of the HAN and enables easier routing across the different underlying network link technologies (for example, Thread or Wi-Fi).

Create the Weave fabric for your HAN, using fab1 as the Fabric ID, then configure all nodes for Weave:

$ weave-fabric-add fab1
$ weave-node-configure

Now that Weave is configured, check the Happy state:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP                       2001:0db8:0001:0002/64

    WiFiNetwork         wifi      UP                       2001:0db8:000a:000b/64
                                                                        10.0.1/24


NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread   2001:0db8:0001:0002:3e36:13ff:fe33:732e/64
                                       fd00:0000:fab1:0006:6bca:9502:eb69:11e7/64

 02BorderRouter        wpan0  thread   fd00:0000:fab1:0006:6a6a:f236:eb69:11e7/64
                                       2001:0db8:0001:0002:a651:3eff:fe92:6dbc/64

                       wlan0    wifi   fd00:0000:fab1:0001:6a6a:f236:eb69:11e7/64
                                                                      10.0.1.2/24
                                       2001:0db8:000a:000b:426c:38ff:fe90:01e6/64

     03WiFiNode        wlan0    wifi   2001:0db8:000a:000b:9aae:2bff:fe71:62fa/64
                                                                      10.0.1.3/24
                                       fd00:0000:fab1:0001:6b82:6e60:eb69:11e7/64

Each node has been added to the Weave fabric, and each interface has a new IPv6 address starting with fd00. To get more information on the Weave fabric, use the weave-state command:

$ weave-state

State Name: weave

NODES           Name       Weave Node Id    Pairing Code
        01ThreadNode    69ca9502eb6911e7          8ZJB5Q
      02BorderRouter    686af236eb6911e7          B5YV3P
          03WiFiNode    69826e60eb6911e7          L3VT3A

FABRIC     Fabric Id           Global Prefix
                fab1     fd00:0000:fab1::/48

Here is our updated topology, with Weave values in blue:

Weave fabric

There's a lot of new information in the Weave and Happy states. Let's start with the fabric from weave-state:

FABRIC     Fabric Id           Global Prefix
                fab1     fd00:0000:fab1::/48

Weave uses an IPv6 prefix of fd00::/48 for each node. Addresses in this block are called Unique Local Addresses and are designated for use within private networks such as a HAN. Combining that with the Fabric ID generates the Weave Global Prefix shown above.

Weave nodes

Each node in the Weave fabric is assigned a unique Node ID, along with a Pairing Code:

NODES           Name       Weave Node Id    Pairing Code
        01ThreadNode    69ca9502eb6911e7          8ZJB5Q
      02BorderRouter    686af236eb6911e7          B5YV3P
          03WiFiNode    69826e60eb6911e7          L3VT3A

The Node ID globally identifies a node in the Weave fabric. The Pairing Code is used as a "joiner credential" during the pairing process, and typically would be printed alongside a product's QR code.

For example, if you look at the QR code on a Nest Protect or a Nest Cam, you'll notice a 6-character string, often referred to as the Entry Key. This is the Weave Pairing Code.

Weave uses a combination of the Global Prefix, the Fabric ID, and the Node ID to create Weave-specific IPv6 addresses for each node and interface in the fabric.

Weave addresses

Note that there are four new IPv6 addresses in the Happy topology, all starting with our Weave Global Prefix of fd00:0000:fab1::/48.

NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread   2001:0db8:0001:0002:3e36:13ff:fe33:732e/64
                                       fd00:0000:fab1:0006:6bca:9502:eb69:11e7/64

 02BorderRouter        wpan0  thread   fd00:0000:fab1:0006:6a6a:f236:eb69:11e7/64
                                       2001:0db8:0001:0002:a651:3eff:fe92:6dbc/64

                       wlan0    wifi   fd00:0000:fab1:0001:6a6a:f236:eb69:11e7/64
                                                                      10.0.1.2/24
                                       2001:0db8:000a:000b:426c:38ff:fe90:01e6/64

     03WiFiNode        wlan0    wifi   2001:0db8:000a:000b:9aae:2bff:fe71:62fa/64
                                                                      10.0.1.3/24
                                       fd00:0000:fab1:0001:6b82:6e60:eb69:11e7/64

Weave protocols use these addresses to communicate across the Weave fabric, rather than the standard IPv6 addresses assigned to each node.

Weave network gateway

Weave nodes on a Thread network need to know where to exit that network. A Weave network gateway—typically on a Thread Border Router—provides this functionality.

In our sample topology, let's designate the BorderRouter node as the Weave network gateway:

$ weave-network-gateway ThreadNetwork 02BorderRouter

This command adds a route from all Thread nodes to the Weave fabric subnet (fd:0:fab1::/48) via the BorderRouter node's Thread interface (wpan0), which enables each Thread node to reach any Weave node beyond the Thread network. This is analogous to the happy-network-route command we used earlier, but specific to Weave fabric routes.

What makes Happy so powerful is how it easily manages all setup and teardown of a simulated topology.

Save your Happy topology for later use:

$ happy-state -s codelab.json

This places a JSON file with the complete topology in your root ~/ folder. The JSON file is a copy of your current Happy state, which is found at ~/.happy_state.json.

Once saved, delete the current topology:

$ happy-state-delete

This deletes all network namespaces and related configurations found in the ~/.happy-state.json file. Check happy-state and weave-state to confirm an empty configuration:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes

NODES      Name    Interface    Type                                          IPs


$ weave-state

State Name: weave

NODES           Name       Weave Node Id    Pairing Code

FABRIC     Fabric Id           Global Prefix

To reload a saved configuration, use one of two commands:

So if your topology includes Weave, always use the weave-state-load command so that the Weave fabric and associated configuration is applied.

Reload the saved Happy topology:

$ weave-state-load codelab.json

Check all states to confirm a successful loading:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP                       2001:0db8:0001:0002/64

    WiFiNetwork         wifi      UP                       2001:0db8:000a:000b/64
                                                                        10.0.1/24


NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread   2001:0db8:0001:0002:eef6:a0ff:feca:6697/64
                                       fd00:0000:fab1:0006:6bca:9502:eb69:11e7/64

 02BorderRouter        wpan0  thread   fd00:0000:fab1:0006:6a6a:f236:eb69:11e7/64
                                       2001:0db8:0001:0002:5e53:bbff:fe05:484b/64

                       wlan0    wifi   2001:0db8:000a:000b:2e61:fdff:fed9:4fbc/64
                                       fd00:0000:fab1:0001:6a6a:f236:eb69:11e7/64
                                                                      10.0.1.2/24

     03WiFiNode        wlan0    wifi   fd00:0000:fab1:0001:6b82:6e60:eb69:11e7/64
                                                                      10.0.1.3/24
                                       2001:0db8:000a:000b:5e8e:c9ff:fed2:bdd1/64


$ weave-state

State Name: weave

NODES           Name       Weave Node Id    Pairing Code
        01ThreadNode    69ca9502eb6911e7          8ZJB5Q
      02BorderRouter    686af236eb6911e7          B5YV3P
          03WiFiNode    69826e60eb6911e7          L3VT3A

FABRIC     Fabric Id           Global Prefix
                fab1     fd00:0000:fab1::/48

Happy uses Linux network namespaces to simulate complex topologies. Typically, a network configuration applies across the entire Linux OS. Network namespaces allow you to partition network configurations so that each namespace has its own set of interfaces and routing tables.

Each node and network in Happy is a network namespace, while links between them are network interfaces.

For example, using our topology:

Let's see what namespaces Happy created for this:

$ ip netns list
happy004
happy003
happy002
happy001
happy000

If you check the netns section of the Happy state JSON file, you can see what nodes and networks each namespace corresponds to:

$ happy-state -j | grep "netns" -A 5
"netns": {
    "01ThreadNode": "000",
    "02BorderRouter": "001",
    "03WiFiNode": "002",
    "ThreadNetwork": "003",
    "WiFiNetwork": "004",

Run-time logs

Commands issued to nodes are basic terminal commands executed from within each node's namespace. An easy way to see this is to enable Happy run-time logs.

To view logs, you have to log into the existing Docker container. In the main Cloud Shell terminal, get the Container ID from the bash prompt.

For example, if your prompt looks like this:

root@c0f3912a74ff:/#

The Docker Container ID is the value after the @ sign: c0f3912a74ff

Copy this value.

Select + to open a second Cloud Shell terminal window:

Login to the existing Docker container using the Container ID you copied:

$ docker exec -it <container-id> bash

For example:

$ docker exec -it c0f3912a74ff bash

The terminal prompt should be the same as the original Cloud Shell one:

root@c0f3912a74ff:/#

Run happy-state and weave-state to confirm. If connected to the same Docker container, these commands output the topology you've been working on:

$ happy-state

State Name:  happy

NETWORKS   Name         Type   State                                     Prefixes
  ThreadNetwork       thread      UP                       2001:0db8:0001:0002/64

    WiFiNetwork         wifi      UP                       2001:0db8:000a:000b/64
                                                                        10.0.1/24


NODES      Name    Interface    Type                                          IPs
   01ThreadNode        wpan0  thread   2001:0db8:0001:0002:eef6:a0ff:feca:6697/64
                                       fd00:0000:fab1:0006:6bca:9502:eb69:11e7/64

 02BorderRouter        wpan0  thread   fd00:0000:fab1:0006:6a6a:f236:eb69:11e7/64
                                       2001:0db8:0001:0002:5e53:bbff:fe05:484b/64

                       wlan0    wifi   2001:0db8:000a:000b:2e61:fdff:fed9:4fbc/64
                                       fd00:0000:fab1:0001:6a6a:f236:eb69:11e7/64
                                                                      10.0.1.2/24

     03WiFiNode        wlan0    wifi   fd00:0000:fab1:0001:6b82:6e60:eb69:11e7/64
                                                                      10.0.1.3/24
                                       2001:0db8:000a:000b:5e8e:c9ff:fed2:bdd1/64


$ weave-state

State Name: weave

NODES           Name       Weave Node Id    Pairing Code
        01ThreadNode    69ca9502eb6911e7          8ZJB5Q
      02BorderRouter    686af236eb6911e7          B5YV3P
          03WiFiNode    69826e60eb6911e7          L3VT3A

FABRIC     Fabric Id           Global Prefix
                fab1     fd00:0000:fab1::/48

Now, turn on Happy logs, they will run continuously on this second terminal window:

$ happy-state -l

Go back to the first window and run a Happy ping:

$ happy-ping 01ThreadNode 02BorderRouter

Check the most recent log entries in the second terminal window. You should see a line like this in the logs:

DEBUG [Driver:CallCmd():416] Happy [happy]: > ip netns exec happy000 ping6 -c 1 2001:0db8:0001:0002:5e53:bbff:fe05:484b

The happy-ping command is nothing more than Happy running the ping6 command in the happy000 namespace (01ThreadNode).

Enter a node

Use happy-shell to run non-Happy commands as if logged into one of the nodes (network namespaces):

$ happy-shell 01ThreadNode
root@01ThreadNode:#

Simulated devices are run within each namespace, and they only have access to the network configuration specified through Happy.

Check the interface configuration for the node. This will be different than your OS-wide configuration and should reflect what's listed in the Happy state:

root@01ThreadNode:# ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:1 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:152 (152.0 B)  TX bytes:152 (152.0 B)

wpan0     Link encap:Ethernet  HWaddr ec:f6:a0:ca:66:97
          inet6 addr: fd00:0:fab1:6:6bca:9502:eb69:11e7/64 Scope:Global
          inet6 addr: 2001:db8:1:2:eef6:a0ff:feca:6697/64 Scope:Global
          inet6 addr: fe80::eef6:a0ff:feca:6697/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:32 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:2832 (2.8 KB)  TX bytes:2348 (2.3 KB)

Use exit to leave the node's namespace:

root@01ThreadNode:# exit

You now know:

Further reading

Check openweave.io for a variety of references!