Gaëtan Trellu, technical operations manager at Ormuco, takes you to infinity and beyond.

image

In my previous post about Qinling I explained how to run a simple function, how to get the output returned by this one and what Qinling really does behind the scenes from a high-level perspective.

Here I’ll explain how to run a packaged function including external Python libraries from PyPi (the Python Package Index), or your own repository or even directly from the code itself like a sub-package or other options.

The main difference between a simple function and a packaged function is that with a simple function you are limited with the libraries/packages installed within the runtime, from a serverless perspective.

Most of the time, the only the built-in packages available (JSON, HTTP, etc…) allow you to do the basics but will constrain your creativity — and we don’t want that!

Qinling can distinguish the difference between the two when you create the creation.

A function a bit more complex this time

Compared to previous post, this function will be a little bit more complex — but not too much, don’t worry. It’s written in Python 3, so just to reiterate, you’ll need Python 3 runtime.

This function will just return information about a CIDR, by default no argument is required but I’ll explain how to override the default one by using the openstack function create execution.

import json
from IPy import IP


def details(cidr="192.168.0.0/24", **kwargs):
    network = IP(cidr)
    version = network.version()
    iptype = network.iptype().lower()
    reverse = network.reverseName()
    prefix = network.prefixlen()
    netmask = str(network.netmask())
    broadcast = str(network.broadcast())
    length = network.len()

    payload = {"ip_version": version, "type": iptype, "reverse": reverse,
               "prefix": prefix, "netmask": netmask, "broadcast": broadcast,
               "length": length, "cidr": cidr}

    print("----------------------")
    print("Function:", details.__name__)
    print("JSON payload:", payload)
    print("----------------------\n")

    return build_json(payload)


def build_json(data):
    indentation_level = 4

    print("----------------------")
    print("Function:", build_json.__name__)
    print("JSON options")
    print("  - indentation:", indentation_level)
    print("  - sort: yes")
    print(json.dumps(data, sort_keys=True, indent=indentation_level))
    print("----------------------")

    return data

The important part in this code is line 2, the import of IPy library which doesn’t exist in the runtime. If this code is uploaded like that, then the function execution will fail.

To make this work, the library needs to be at the same level as the ip_range.py file.

$ mkdir ~/qinling
$ wget -O ~/qinling/ip_range.py https://git.io/fj0SQ
$ pip install IPy -t ~/qinling

The ~/qinling directory should looks like this after the previous commands:

$ ip_range.py  IPy-1.0.dist-info  IPy.py  __pycache__

Just a quick warning: the pip command used should be the same version as the one from the runtime, if not some surprises are expected.

The next step is to generate an archive. Qinling has a restriction on the format of the archive, it has to be a ZIP archive generated with the zip command[1].

$ cd ~/qinling/
$ zip -r9 ~/qinling/ip_range.zip ~/qinling/

Run the best function ever ^^

As mentioned above, Qinling has a mechanism to determine whether you’re running a package or not. There are four options available:

  • file: used only with a file, hello_qinling.py
  • package: used only with a ZIP archive, ip_range.zip
  • container/object: will be discussed in a different Medium post
  • image: will be discussed in a different Medium post

So, did you guess which one will be the winner this time? Well… package!

The file option is kind of a “wrapper,” based on python-qinlingclient code[2] when this option is selected then the client get the filename, remove the extension and create a ZIP archive.

$ openstack function create --name func-pkg-1 --runtime python3 --entry ip_range.details --package ~/qinling/ip_range.zip

If the wrong option is used let say --file for a package then the function will not be executed properly and an error will be raised. When the function is properly created, the execution will return something like that as output value.

$ openstack function execution create 1030e1ea-2374-40a7-bfbe-216bc5966f55
| result           | {"duration": 0.036, "output": "{
    "broadcast": "192.168.0.255",
    "cidr": "192.168.0.0/24",
    "ip_version": 4,
    "length": 256,
    "netmask": "255.255.255.0",
    "prefix": 24,
    "reverse": "0.168.192.in-addr.arpa.",
    "type": "private"
}"} |

In the function, there are few print used mostly for a learning purpose, the output will be available only via the openstack function execution log show command.

$ openstack function execution log show 5f2e7d71-7b26-4ab7-9e1a-854d8850e738
Start execution: 5f2e7d71-7b26-4ab7-9e1a-854d8850e738
----------------------
Function: details
JSON payload: {'ip_version': 4, 'type': 'private', 'reverse': '0.168.192.in-addr.arpa.', 'prefix': 24, 'netmask': '255.255.255.0', 'broadcast': '192.168.0.255', 'length': 256, 'cidr': '192.168.0.0/24'}
--------------------------------------------
Function: build_json
JSON options
  - indentation: 4
  - sort: yes
{
    "broadcast": "192.168.0.255",
    "cidr": "192.168.0.0/24",
    "ip_version": 4,
    "length": 256,
    "netmask": "255.255.255.0",
    "prefix": 24,
    "reverse": "0.168.192.in-addr.arpa.",
    "type": "private"
}
----------------------
Finished execution: 5f2e7d71-7b26-4ab7-9e1a-854d8850e738

What do you think? Pretty nice, right?

Change the default CIDR value

As mentioned previously, no argument is required to execute the function. By default, the classless inter-domain routing has been hardcoded to 192.168.0.0/24 but what if you want to change it? Maybe you want to update the code, create a function, or do something else.

The solution is to use the --input option and provide a JSON hash on this one.

$ openstack function execution create 1030e1ea-2374-40a7-bfbe-216bc5966f55 --input '{"cidr": "10.0.0.0/10"}'
| result           | {"duration": 0.035, "output": "{
    "broadcast": "10.63.255.255",
    "cidr": "10.0.0.0/10",
    "ip_version": 4,
    "length": 4194304,
    "netmask": "255.192.0.0",
    "prefix": 10,
    "reverse": "0-255.10.in-addr.arpa.",
    "type": "private"
}"} |

Now run the openstack function execution log show command to see the differences between the two CIDR.

Conclusion

I’ve just demonstrated a packaged function, how to pass argument to the function and how to get the output. My journey continues…To infinity and beyond!

Resources

 

About the author

Gaëtan Trellu is a technical operations manager at Ormuco. This post first appeared on Medium.

 

Superuser is always interested in open infra community topics, get in touch at editorATopenstack.org

 

Photo // CC BY NC