Microovn

mj ponsonby Publisher

Platform:

Ubuntu
24.04
Channel Revision Published Runs on
latest/candidate 115 07 Apr 2026
Ubuntu 24.04
latest/candidate 123 07 Apr 2026
Ubuntu 24.04
latest/candidate 113 07 Apr 2026
Ubuntu 24.04
latest/candidate 114 07 Apr 2026
Ubuntu 24.04
latest/candidate 111 07 Apr 2026
Ubuntu 24.04
latest/edge 145 20 Apr 2026
Ubuntu 24.04
latest/edge 144 20 Apr 2026
Ubuntu 24.04
latest/edge 143 20 Apr 2026
Ubuntu 24.04
latest/edge 142 20 Apr 2026
Ubuntu 24.04
latest/edge 123 02 Apr 2026
Ubuntu 24.04
25.03/stable 90 31 Mar 2026
Ubuntu 24.04
25.03/stable 88 31 Mar 2026
Ubuntu 24.04
25.03/stable 89 31 Mar 2026
Ubuntu 24.04
25.03/stable 91 31 Mar 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
24.03/stable 94 31 Mar 2026
Ubuntu 24.04
24.03/stable 92 31 Mar 2026
Ubuntu 24.04
24.03/stable 93 31 Mar 2026
Ubuntu 24.04
24.03/stable 95 31 Mar 2026
Ubuntu 24.04
24.03/stable 40 05 Mar 2026
Ubuntu 24.04
24.03/candidate 148 22 Apr 2026
Ubuntu 24.04
24.03/candidate 146 22 Apr 2026
Ubuntu 24.04
24.03/candidate 151 22 Apr 2026
Ubuntu 24.04
24.03/candidate 152 22 Apr 2026
Ubuntu 24.04
24.03/candidate 40 04 Feb 2026
Ubuntu 24.04
24.03/edge 154 22 Apr 2026
Ubuntu 24.04
24.03/edge 151 22 Apr 2026
Ubuntu 24.04
24.03/edge 152 22 Apr 2026
Ubuntu 24.04
24.03/edge 148 22 Apr 2026
Ubuntu 24.04
24.03/edge 146 22 Apr 2026
Ubuntu 24.04
juju deploy microovn --channel candidate

"""
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