Using the Tasks API#

In this tutorial, you will learn how to organize your code into tasks and integrate it with a command line interface.

For the sake of illustration, let’s consider the following scenario:

  • Get machines from vagrant

  • Apply some network constraints between your machines

  • Validate those network constraints

Installation#

$ pip install enoslib

Note

It’s a good practice to use a virtualenv or python version manager like pyenv.

Using the API#

The following enos.py implements the desired workflow.

 1import logging
 2
 3import enoslib as en
 4
 5en.init_logging(level=logging.INFO)
 6en.check()
 7
 8provider_conf = {
 9    "backend": "libvirt",
10    "resources": {
11        "machines": [
12            {
13                "roles": ["control"],
14                "flavour": "tiny",
15                "number": 1,
16            },
17            {
18                "roles": ["compute"],
19                "flavour": "tiny",
20                "number": 1,
21            },
22        ],
23        "networks": [{"cidr": "192.168.40.0/24", "roles": ["mynetwork"]}],
24    },
25}
26
27tc = {
28    "enable": True,
29    "default_delay": "20ms",
30    "default_rate": "1gbit",
31    "groups": ["control", "compute"],
32}
33
34
35# claim the resources
36conf = en.VagrantConf.from_dictionary(provider_conf)
37
38provider = en.Vagrant(conf)
39roles, networks = provider.init()
40
41roles = en.sync_info(roles, networks)
42
43netem = en.NetemHTB(tc, roles=roles)
44# apply network constraints
45netem.deploy()
46
47# validate network constraints
48netem.validate()
49
50# reset network constraints
51netem.destroy()
52
53# validate network constraints and saving in an alternative
54netem.validate(output_dir="after_reset")
  • Lines 5-18 describe the wanted resources. Here we want two machines with roles control and compute respectively. These two nodes will have one network card configured using the same network whose role is n1.

    Note

    Machine roles and network roles are transparent to the EnOSlib. The semantic is left to the application using it.

  • Lines 19-23 describe some network constraints. Those constraints will be set between the nodes of the two groups control and compute on the network n1.

  • Lines 27-34 enforce the wanted workflow.

    Note

    Under the hoods, EnOSlib leverages Ansible for many routine tasks and thus an inventory must be generated. This is exactly the purpose of enoslib.api.generate_inventory() function. When check_networks is set, EnOSlib will auto-discover the mapping between the network roles and the available network interfaces. This is convenient when it comes to deal with non uniform (or non deterministic) network cards naming.

  • You can launch the script using :

    $ python enos.py
    
  • The content of the generated inventory should looks like the following:

    [control]
    enos-0 ansible_host=192.168.121.70 ansible_ssh_user=root ansible_port=22 ansible_ssh_private_key_file=/home/msimonin/workspace/repos/enoslib/docs/tutorials/using-tasks/.vagrant/machines/enos-0/libvirt/private_key ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' n3=eth1 enos_devices="['eth1']"
    [compute]
    enos-1 ansible_host=192.168.121.170 ansible_ssh_user=root ansible_port=22 ansible_ssh_private_key_file=/home/msimonin/workspace/repos/enoslib/docs/tutorials/using-tasks/.vagrant/machines/enos-1/libvirt/private_key ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' n3=eth1 enos_devices="['eth1']"
    
  • You can check the generated reports by enoslib.api.validate_network() in _tmp_enos_.

    192.168.40.244 : 40.35 40.32 40.44 40.27 40.44 40.43 40.35 40.78 40.27 40.46
    192.168.40.245 : 0.03 0.03 0.02 0.08 0.03 0.03 0.02 0.03 0.03 0.03
    
           2 targets
           2 alive
           0 unreachable
           0 unknown addresses
    
           0 timeouts (waiting for response)
          20 ICMP Echos sent
          20 ICMP Echo Replies received
           0 other ICMP received
    
     0.02 ms (min round trip time)
     20.2 ms (avg round trip time)
     40.7 ms (max round trip time)
            9.042 sec (elapsed real time)
    

Using tasks#

import logging

import enoslib as en

en.init_logging(level=logging.INFO)
en.check()

provider_conf = {
    "backend": "libvirt",
    "resources": {
        "machines": [
            {
                "roles": ["control"],
                "flavour": "tiny",
                "number": 1,
            },
            {
                "roles": ["compute"],
                "flavour": "tiny",
                "number": 1,
            },
        ],
        "networks": [{"cidr": "192.168.40.0/24", "roles": ["mynetwork"]}],
    },
}

tc = {
    "enable": True,
    "default_delay": "20ms",
    "default_rate": "1gbit",
    "groups": ["control", "compute"],
}


@en.enostask(new=True)
def up(force=True, env=None, **kwargs):
    """Starts a new experiment"""
    conf = en.VagrantConf.from_dictionary(provider_conf)
    provider = en.Vagrant(conf)
    roles, networks = provider.init()
    roles = en.sync_info(roles, networks)
    env["roles"] = roles
    env["networks"] = networks


@en.enostask()
def emulate(env=None, **kwargs):
    roles = env["roles"]
    netem = en.NetemHTB(tc, roles=roles)
    netem.deploy()


@en.enostask()
def validate(env=None, **kwargs):
    roles = env["roles"]
    netem = en.NetemHTB(tc, roles=roles)
    netem.validate()


up()
emulate()
validate()
  • Using Tasks is a neat way to organize your program into a workflow.

  • The environment (env variable in the above) is a way to (1) store information on the current execution and (2) pass information from one task to another. It is automatically restored at the beginning of a task and saved at the end.

  • You can launch the script using :

    $ python enos.py
    

Integrating with a command line parser#

Let’s integrate our tasks with a command line parser. Here we choose click. First ensure that it is installed:

$ pip install click
  • Change the content of enos.py to the following:

    import logging
    
    import click
    
    import enoslib as en
    
    en.init_logging(level=logging.INFO)
    en.check()
    
    provider_conf = {
        "backend": "libvirt",
        "resources": {
            "machines": [
                {
                    "roles": ["control"],
                    "flavour": "tiny",
                    "number": 1,
                },
                {
                    "roles": ["compute"],
                    "flavour": "tiny",
                    "number": 1,
                },
            ],
            "networks": [{"cidr": "192.168.40.0/24", "roles": ["mynetwork"]}],
        },
    }
    
    tc = {
        "enable": True,
        "default_delay": "20ms",
        "default_rate": "1gbit",
        "groups": ["control", "compute"],
    }
    
    
    @click.group()
    def cli():
        pass
    
    
    @cli.command()
    @click.option("--force", is_flag=True, help="vagrant destroy and up")
    @en.enostask(new=True)
    def up(force, env=None, **kwargs):
        """Starts a new experiment using vagrant"""
        conf = en.VagrantConf.from_dictionary(provider_conf)
        provider = en.Vagrant(conf)
        roles, networks = provider.init(force_deploy=force)
        roles = en.sync_info(roles, networks)
        env["roles"] = roles
        env["networks"] = networks
    
    
    @cli.command()
    @en.enostask()
    def emulate(env=None, **kwargs):
        """Emulates the network."""
        roles = env["roles"]
        netem = en.NetemHTB(tc, roles=roles)
        netem.deploy()
    
    
    @cli.command()
    @en.enostask()
    def validate(env=None, **kwargs):
        """Validates the network constraints."""
        roles = env["roles"]
        netem = en.NetemHTB(tc, roles=roles)
        netem.validate()
    
    
    if __name__ == "__main__":
        cli()
    
  • For the sake of illustration, we added a flag (--force) to the command line which allows to force the recreation of the virtual machines (see enoslib.infra.provider.Provider.init()). Since every provider supports this flag we pass its value to the ìnit method.

  • You will now have access to the command line interface :

    $ python enos.py --help
    Usage: enos.py [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help  Show this message and exit.
    
    Commands:
      emulate   Emulates the network.
      up        Starts a new experiment using vagrant
      validate  Validates the network constraints.