Policy package prototype

Yang

module policy {
  yang-version 1.1;
  namespace "http://example.com/policy";
  prefix policy;

  import tailf-common { prefix tailf; }
  import tailf-ncs { prefix ncs; }

  description "Per-device prefix-set + route-policy with comma-separated prefixes.";
  revision 2025-07-18 { description "Final version using prefix-set-template.xml"; }

  list policy {
    key name;
    uses ncs:service-data;
    ncs:servicepoint "policy-servicepoint";

    leaf name { type string; }

    list device {
      key device-name;
      min-elements 1;

      leaf device-name {
        type leafref { path "/ncs:devices/ncs:device/ncs:name"; }
      }

      list prefix-set {
        key name;
        leaf name { type string; }

        leaf prefixes {
          type string;
          mandatory true;
          tailf:info "Comma-separated prefixes (e.g. 10.0.0.0/24,10.1.0.0/24)";
        }
      }

      list route-policy {
        key name;
        min-elements 1;
        description "At least one route-policy required per device.";

        leaf name { type string; }

        leaf prefix-set-ref {
          type leafref { path "../../prefix-set/name"; }
          mandatory true;
        }
      }
    }
  }
}

python

# -*- mode: python; python-indent: 4 -*-
import ncs
from ncs.application import Service, Application

class ServiceCallbacks(Service):
    @Service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info(f"Service create: {service.name}")
        tmpl = ncs.template.Template(service)

        for dev in service.device:
            dev_name = dev.device_name
            self.log.info(f"Processing device: {dev_name}")

            # === prefix-set: split by comma, apply existing template ===
            for ps in dev.prefix_set:
                ps_name = ps.name
                prefixes = [p.strip() for p in ps.prefixes.split(',') if p.strip()]
                for prefix in prefixes:
                    vars_ps = ncs.template.Variables()
                    vars_ps.add('DEVICE_NAME', dev_name)
                    vars_ps.add('PS_NAME', ps_name)
                    vars_ps.add('PREFIX', prefix)
                    tmpl.apply('prefix-set-template', vars_ps) 

            # === route-policy ===
            for rp in dev.route_policy:
                ps_name = rp.prefix_set_ref
                definition = f"""if destination in {ps_name} then
  pass
else
  drop
endif"""
                vars_rp = ncs.template.Variables()
                vars_rp.add('DEVICE_NAME', dev_name)
                vars_rp.add('RP_NAME', rp.name)
                vars_rp.add('DEFINITION', definition)
                tmpl.apply('route-policy-template', vars_rp)

class Policy(Application):
    def setup(self):
        self.log.info('Policy package RUNNING')
        self.register_service('policy-servicepoint', ServiceCallbacks)
    def teardown(self):
        self.log.info('Policy package STOPPING')