Generator

Generators are the cornerstone of Annet. Each generator is responsible for generating part of the configuration. A part must be as small as possible. This will reduce the complexity of a generator and increase the granularity of deployment.

Partial

Useful for CLI-based configuration. Must inherit from PartialGenerator.

acl function filters out part of the configuration that this generator is responsible for. Also acl may filter more precise using Device object. run returns configuration. acl and run function are prefixed with device.hw.vendor. For example device with vendor huawei will have acl_huawei and run_huawei functions respectively.

class Ntp(PartialGenerator):
    def acl_huawei(self, _) -> str:
        return """
        ntp
        """

    def run_huawei(self, device):
        yield "ntp server disable"

Entire

For file-based generator. Useful for Linux-based configuration. Must inherit from Entire. path returns the path to the file, if the return value is an empty string, then the generator is not applicable for the device. reload returns a command to reload the configuration. It is not applied if nothing has been changed.

class Ntp(Entire):
    def path(self, device: Device) -> str:
        if device.hw.vendor == "PC":
            return "/etc/ntp.conf"

    def run(self, device: Device) -> Iterator[str]:
        return """
    restrict 127.0.0.1
    restrict ::1
    server 0.ru.pool.ntp.org iburst
    """

    def reload(self, _) -> str:
        return "systemctl restart ntpd"

JSON-fragment

For cases where one file contains configurations for many services. Must inherit from JSONFragment.

class Dns(JSONFragment):
    def path(self, device: Device) -> str:
        if device.breed == "sonic":
            return "/etc/sonic/config_db.json"

    def acl(self, _) -> str:
        return "/DNS_NAMESERVER"

    def reload(self, device: Device) -> str:
        return "sudo config load -y && sudo config save -y"

    def run(self, device: Device):
        with self.block("DNS_NAMESERVER"):
            for ip in device.ifaces.ips:
                with self.block(dns_ip):
                    yield {}

An object the generator stops producing is written back as an explicit null rather than dropped from the file. sudo config load -y merges the file into CONFIG_DB, and SONiC only deletes a table or an entry that is present with a null value — a key that is simply absent stays in redis. Hash fields are not deletable this way at all, so those are dropped as before.

Set DELETE_WITH_NULL = False if the file’s consumer replaces it wholesale and the nulls would just be noise:

class Dns(JSONFragment):
    DELETE_WITH_NULL = False