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.