microovn

Microovn

Channel Revision Published Runs on
latest/edge 49 06 Dec 2025
Ubuntu 24.04
latest/edge 48 06 Dec 2025
Ubuntu 24.04
latest/edge 47 06 Dec 2025
Ubuntu 24.04
latest/edge 46 06 Dec 2025
Ubuntu 24.04
latest/edge 28 08 Nov 2025
Ubuntu 24.04
25.03/edge 53 10 Dec 2025
Ubuntu 24.04
25.03/edge 52 10 Dec 2025
Ubuntu 24.04
25.03/edge 51 10 Dec 2025
Ubuntu 24.04
25.03/edge 50 10 Dec 2025
Ubuntu 24.04
25.03/edge 23 02 Nov 2025
Ubuntu 24.04
24.03/edge 61 16 Dec 2025
Ubuntu 24.04
24.03/edge 60 16 Dec 2025
Ubuntu 24.04
24.03/edge 59 16 Dec 2025
Ubuntu 24.04
24.03/edge 58 16 Dec 2025
Ubuntu 24.04
24.03/edge 40 02 Dec 2025
Ubuntu 24.04
juju deploy microovn --channel edge
Show information

Platform:

Ubuntu
24.04

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