The xosapi Library
The xosapi
library is a python library that may be used to interact
with the XOS core. The library connects to the core, automatically downloads and
compiles protobufs, and then provides high-level ORM abstractions of the
models to the caller.
This library is used internally by the synchronizer framework. If you are writing a synchronizer, then it's advised that you work with the synchronizer framework rather than working with this library. This library may be useful for people who are developing python components that are not synchronizers.
Prerequisites
The following pip packages must be installed
sudo pip install python-consul twisted simplejson xosapi
Next we need to create the necessary certificates. The gRPC API used between the client and the core uses SSL and the certificate authority must be specified. Below is an example:
Note: This file may be dependent on the particular xos-core installation. You can find the correct file at
helm-charts/xos-core/pki/xos-CA.pem
.
cat > local_certs.crt <<EOF
-----BEGIN CERTIFICATE-----
MIID1jCCAr6gAwIBAgIJAIWmwLL7nulVMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRMwEQYDVQQHDApNZW5sbyBQYXJr
MQwwCgYDVQQKDANPTkYxFTATBgNVBAsMDFRlc3RpbmcgT25seTEaMBgGA1UEAwwR
Q09SRCBUZXN0IFJvb3QgQ0EwHhcNMTgxMjE0MTgyNTE5WhcNMTkxMjE1MTgyNTE5
WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTETMBEGA1UEBwwK
TWVubG8gUGFyazEMMAoGA1UECgwDT05GMRUwEwYDVQQLDAxUZXN0aW5nIE9ubHkx
GjAYBgNVBAMMEUNPUkQgVGVzdCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAokxdd5vyy83NPQYQK/wsz6VLrunv/3FNSbUv9dC4MC5zZyMd
oxrYCfM38rypbrB5PIVlFdndfDzoYORmlC9gxJnFUmAztyU2JIZrcxk1sQ+lBWj+
Bytwh1TKT0OSfEWjB/LV1FGLAuspJGBn2T0E35bGhhzOL8Cgm0e8akeAfs2s9akO
Xcj+4osnAkXynKl+HhCTBkcrmg1YsTB3+0ug0vM5xuHMU5tVVKpn9DinZ3enuHle
ICyiMF8JyEibjGl0cjnGhw1lPzT7lsjxuoZhr3NaIlI/zUXBDTJbJ6T6gUa1Npa/
lurbEn/9pUMQcUIOnIfzbmzVjPmd0AL9fEcAlQIDAQABo2MwYTAdBgNVHQ4EFgQU
xYhJSu6N6DF7C39G1hAvF7JOC54wHwYDVR0jBBgwFoAUxYhJSu6N6DF7C39G1hAv
F7JOC54wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN
AQELBQADggEBAJc7J+ZVhp7ti+YN2Smv/jtz79NyF1J6Eb3M/BC/A5Eo8Cp2hklC
NI00+con2Dvvbmj6lOgKXPL6C8LgxiZ5gVDtSvK8zuoIzkIDod4IovxcwLrvlIH4
BpG6Sm1d7EbwAHKFGc0qvVdRN48P884KnzW27eLtsdqrkUPuqz9Ph1JJmAzy3v5p
pKtL6zfn706pcad5NuAcoz0782T+wszHmBv0SBboLdo9NyUciJBQCjIDaSEOpqze
upzRp50aDMq3nxd7yZ3VGA52ECNQ4gWgWAHomDS22RdCHsedbUofnrl6TW88j+Aa
+4AJR9CmhoP1CnKHb5wVCBScw9T8gu3aLe0=
-----END CERTIFICATE-----
EOF
Create a configuration file that at a minimum configures logging:
cat > logging_config.yaml <<EOF
logging:
version: 1
handlers:
console:
class: logging.StreamHandler
EOF
The DNS name xos-core
must map to an IP address. If you're working
inside a container, then the name may already map correctly. If you're
not working in a container then you may have to ensure this name
maps yourself. One way to do that is to do so is by adding an entry to /etc/hosts/
:
echo "<my-core-ip-address> xos-core" | sudo tee -a /etc/hosts
The following directories must be writable:
sudo mkdir -p /var/run/xosapi
sudo chmod a+rwx /var/run/xosapi
Starting the API
Now that everything is prepared, we can launch python and try to initialize and connect the API.
import sys
import functools
from xosconfig import Config
from twisted.internet import reactor
Config.init("logging_config.yaml")
from xosapi.xos_grpc_client import SecureClient
def connected(client):
print "Connected!"
print "Stopping!"
reactor.stop()
client = SecureClient(endpoint="xos-core:30010",
username="admin@opencord.org",
password="letmein",
cacert="local_certs.crt")
client.set_reconnect_callback(functools.partial(connected, client))
client.start()
reactor.run()
If all works as it should, the above should print:
Connected!
Stopping!
Using the API
The connected client and generated ORM are available inside of the connected()
callback. Let's perform a few operations between the Connected!
and Stopping!
lines. Replace the connected()
function above with the following:
def connected(client):
print "Connected!"
orm = client.xos_orm
# List current sites
print "Current Sites:", orm.Site.objects.all()
# Create a new site
newSite = orm.Site(name="newsite",
login_base="newsite",
abbreviated_name="newsite")
newSite.save()
print "Created newsite with id:", newSite.id
# Filter site
someSites = orm.Site.objects.filter(name="newsite")
print "Filtered sites with name=newsite and found:", someSites
# Delete a site
newSite.delete()
print "Stopping!"
reactor.stop()
This output of the above should be the following:
Connected!
Current Sites: [<Site: mysite>]
2019-07-23T15:17:33.646318Z [debug ] save(): is new classname=Site syncstep=None
Created newsite with id: 2
Filtered sites with name=newsite and found: [<Site: newsite>]
Stopping!
In the above example, we have done everything from inside the connected()
callback, though this is not necessarily a requirement. You could for example
save the client
argument to connected()
to a global, then call
reactor.stop()
to terminate reactor. The reactor.run()
statement will
complete and python will execute and subsequent statements. You may continue
to use the client that you saved to the global. reactor
is only
required during the initial setup of the client. This particular pattern of
saving data to globals and then exiting reactor is how the xossynchronizer
library is implemented, as synchronizers do not use reactor
.