MicroOVN Charm

mj ponsonby Publisher

Platform:

Ubuntu
26.04 24.04
Channel Revision Published Runs on
latest/stable 160 09 Jul 2026
Ubuntu 24.04
latest/stable 159 09 Jul 2026
Ubuntu 24.04
latest/stable 158 09 Jul 2026
Ubuntu 24.04
latest/stable 157 09 Jul 2026
Ubuntu 24.04
latest/stable 156 09 Jul 2026
Ubuntu 24.04
latest/candidate 160 15 Jun 2026
Ubuntu 24.04
latest/candidate 159 15 Jun 2026
Ubuntu 24.04
latest/candidate 158 15 Jun 2026
Ubuntu 24.04
latest/candidate 157 15 Jun 2026
Ubuntu 24.04
latest/candidate 156 15 Jun 2026
Ubuntu 24.04
latest/edge 160 11 May 2026
Ubuntu 24.04
latest/edge 159 11 May 2026
Ubuntu 24.04
latest/edge 158 11 May 2026
Ubuntu 24.04
latest/edge 157 11 May 2026
Ubuntu 24.04
latest/edge 156 11 May 2026
Ubuntu 24.04
24.03/stable 181 15 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 180 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 178 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 174 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 173 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 172 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 170 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 168 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 167 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/stable 166 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 181 15 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 180 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 178 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 174 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 173 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 172 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 170 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 168 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 167 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/candidate 166 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 186 17 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 185 16 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 184 16 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 183 16 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 182 16 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 180 30 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 173 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 170 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 168 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
24.03/edge 167 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 179 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 177 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 176 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 175 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 171 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 169 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 164 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 163 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 162 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/stable 161 13 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 179 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 177 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 176 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 175 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 171 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 169 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 164 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 163 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 162 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/candidate 161 09 Jul 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 179 30 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 177 30 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 176 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 175 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 171 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 169 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 164 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 163 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 162 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
26.03/edge 161 29 Jun 2026
Ubuntu 26.04 Ubuntu 24.04
25.03/stable 155 09 Jul 2026
Ubuntu 24.04
25.03/stable 153 15 Jun 2026
Ubuntu 24.04
25.03/stable 150 15 Jun 2026
Ubuntu 24.04
25.03/stable 149 15 Jun 2026
Ubuntu 24.04
25.03/stable 147 15 Jun 2026
Ubuntu 24.04
25.03/candidate 155 15 Jun 2026
Ubuntu 24.04
25.03/candidate 149 22 Apr 2026
Ubuntu 24.04
25.03/candidate 147 22 Apr 2026
Ubuntu 24.04
25.03/candidate 153 22 Apr 2026
Ubuntu 24.04
25.03/candidate 150 22 Apr 2026
Ubuntu 24.04
25.03/edge 155 22 Apr 2026
Ubuntu 24.04
25.03/edge 153 22 Apr 2026
Ubuntu 24.04
25.03/edge 150 22 Apr 2026
Ubuntu 24.04
25.03/edge 149 22 Apr 2026
Ubuntu 24.04
25.03/edge 147 22 Apr 2026
Ubuntu 24.04
juju deploy microovn --channel 24.03/stable

"""
This is a charm library for the ovsdb interface. This contains two classes to
ease development of charms using this interface, one for provides and one for
requires.

The provides part of this takes the ovsdb connection strings from the microovn
environment file.

The requires part communicates with the relation data and gets these strings to
be easily returned and used for interaction with the ovsdb databases.
"""

# The unique Charmhub library identifier, never change it
LIBID = "599e7729d8cf403db3f6afb6d7c64c92"

# Increment this major API version when introducing breaking changes
LIBAPI = 0

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 1

ENV_FILE = "/var/snap/microovn/common/data/env/ovn.env"
CONNECT_ENV_NAME = "OVN_{0}_CONNECT"
CONNECT_STR_KEY = "db_{0}_connection_str"

from dataclasses import dataclass
import logging

from ops import CharmBase, StoredState, EventBase
from ops.framework import Object
from ops.model import (
    Application,
    ModelError,
    Relation,
    SecretNotFoundError,
    Unit,
)

logger = logging.getLogger(__name__)

@dataclass
class OVSDBConnectionString:
    """Class for storing the northbound and southbound connection strings"""
    nb: str
    sb: str


class OVSDBRequires(Object):
    _stored = StoredState()

    def __init__(
        self,
        charm: CharmBase,
        relation_name: str,
    ):
        super().__init__(charm, relation_name)
        self.charm = charm
        self.relation_name = relation_name

    """
    get_connection_strings takes the northbound and southbound connection strings
    from the relation data.

    it returns None on failure, and it returns a instance of
    OVSDBConnectionString containing both northbound and southbound strings
    """
    def get_connection_strings(self) -> OVSDBConnectionString:
        if not (relation := self.charm.model.get_relation(self.relation_name)):
            return None

        ovsdb_app_data = relation.data[relation.app]
        nb_connect = ovsdb_app_data.get(CONNECT_STR_KEY.format("nb"))
        sb_connect = ovsdb_app_data.get(CONNECT_STR_KEY.format("sb"))
        if nb_connect and sb_connect:
            return OVSDBConnectionString(nb=nb_connect, sb=sb_connect)
        else:
            return None

class OVSDBProvides(Object):
    _stored = StoredState()

    def __init__(
        self,
        charm: CharmBase,
        relation_name: str,
    ):
        super().__init__(charm, relation_name)
        self.charm = charm
        self.relation_name = relation_name
        self.framework.observe(
            self.charm.on[relation_name].relation_changed,
            self._on_ovsdb_relation_changed,
        )
        self.framework.observe(
            self.charm.on[relation_name].relation_created,
            self._on_ovsdb_relation_changed,
        )

    def _on_ovsdb_relation_changed(self, _: EventBase):
        self.update_relation_data()

    def update_relation_data(self):
        if not (self.charm.unit.is_leader() and self.charm._stored.in_cluster):
            return

        if not (relation := self.charm.model.get_relation(self.relation_name)):
            return

        connect_str = self.get_connection_strings()
        if connect_str:
            relation.data[self.charm.app][CONNECT_STR_KEY.format("nb")] = \
                connect_str.nb
            relation.data[self.charm.app][CONNECT_STR_KEY.format("sb")] = \
                connect_str.sb
            logger.info("connection strings updated")

    """
    get_connection_strings takes the northbound and southbound connection strings
    from the relation data.

    it returns None on failure, and it returns a instance of
    OVSDBConnectionString containing both northbound and southbound strings
    """
    def get_connection_strings(self) -> OVSDBConnectionString:
        nb_connect = None
        sb_connect = None
        try:
            with open(ENV_FILE, "r") as f:
                for line in f:
                    line = line.strip()
                    if line.startswith(CONNECT_ENV_NAME.format("NB")):
                        nb_connect = line.split("=", 1)[1].strip('"')
                    if line.startswith(CONNECT_ENV_NAME.format("SB")):
                        sb_connect = line.split("=", 1)[1].strip('"')
        except FileNotFoundError:
            logger.error(
                "OVN env file not found, is this unit in the microovn cluster?")
            raise FileNotFoundError("{0} not found".format(ENV_FILE))

        if nb_connect and sb_connect:
            return OVSDBConnectionString(nb=nb_connect, sb=sb_connect)
        else:
            return None