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 postimage
: 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
- [1]https://storyboard.openstack.org/#!/story/2005786
- [2]https://github.com/openstack/python-qinlingclient/blob/2c4debf64445c31fba447d814c481a617b56fbce/qinlingclient/osc/v1/function.py#L30-L75
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
- How to run a packaged function with Qinling - July 26, 2019
- How to run a simple function with Qinling - July 15, 2019
- A quickstart guide to deploying Qinling in production - July 5, 2019