I agree that you cannot use must or leafref - what you want is to validate an operation, not the resulting configuration (which is somewhat against YANG principles, by the way). I don’t think you can use standard validation callback either - technically they can be used to compare current and new configurations, but it looks to me that in this case it is not possible or at least it would be difficult.
You may want to have a look at transaction hooks - you can declare a transaction hook on the vrf list and register a remove callback in which you would check if there is a reference to the instance that is being deleted and let the transaction fail if it is. The check might be done using XPath expressions for better performance.
I believe one should wait until the prepare phase and do that type of check from there.
Perhaps using get_modifications() from a prepare phase subscriber.
Validation should be done on the data (not operations) in YANG model checks, e.g. must expressions, and validataion code. Operations, tests etc should be done in the prepare phase, e.g. from a prepare phase subscriber.
The reason for that is that aborting the transaction from a transaction-hook will not enable you to provide the proper error message (the transaction hook is not meant to be used for validation), while an abort in the prepare phase will. The examples.confd/cdb_subscription/twophase example that comes with ConfD provide guidance.
Im still unsure why a prepare phase is preferred than the validation ? (apart from the error message )
If validation phase is still an option,
Does the validation phase have the view of resulting config ?
Can i write a validation hook
If Prepare phase is the way to go ,
Do i get the view of CDB snapshot just at the cdb_diff_iterate stage,
if a transaction already contains both config
no vrf A
no rout-leak blah from vrf A.
Does the CDB already reflect both configs at that point ?
I have to correct myself. The validation phase using a validation point is actually what you want here. One of the reasons for that is if someone decides to just validate without committing the configuration, you then too want to run your validation code.
To read the old configuration use the CDB API. To get the new configuration use MAAPI. Note that maapi_diff_iterate() will not show the old value in the validation phase. The “oldv” passed to the iter() function by maapi_diff_iterate() will always be null. You will have to use the CDB API and cdb_get_*() to get the old value(s).
The error message handling is there for both the validation point and the prepare phase subscriber. I was referring to the transaction-hook and how the transaction-hook is not where you want to validate and abort a transaction.
Just set the developer log level to “trace”, execute a transaction, and check the log for what was done for your confd.conf setup and applications. Example:
entering validate phase for running…
grabbing transaction lock…
grabbing transaction lock ok
creating rollback file…
creating rollback file ok run transforms and transaction hooks... run transforms and transaction hooks done
pre validate done
run validation over the changeset…
run validation over the changeset done
run dependency-triggered validation…
run dependency-triggered validation done
leaving validate phase for running
entering write-start phase for running…
check data kickers…
check data kickers done
leaving write-start phase for running
entering prepare phase for running…
leaving prepare phase for running
entering commit phase for running…
releasing transaction lock
leaving commit phase for running
applying transaction done
Let me summarize some differences between transaction hooks and validation, as applied to this use case:
Validation callbacks in general are the right thing for validation. They are invoked when all changes are complete, pretty much just before they are written to CDB; and as Conny pointed out, they are also invoked when the operator asks for mere validation (e.g. using validate CLI command or test-only NETCONF message). There are some problems with validations in your case though:
you need to declare tailf:validate on the from-vrf node, not on the top vrf node, but with dependencies on it - this might somewhat counterintuitive;
your validation callback will not be simple to write - you need to compare the list of vrf instances in the transaction with the list of vrf instance in CDB to find which ones have been deleted, and then verify that no from-vrf instance refers to one of them.
The second point is related to that validation callbacks are tailored to validate configurations, but you are trying to validate an operation.
Now for transaction hooks: in a remove callback as described in the other post you immediately know that a vrf instance is being deleted - as a result, your code would be much simpler (in particular, no need to open CDB session). But then again, transaction hooks are supposed to serve a bit different role then to validate, so it comes with a price:
as mentioned, they are not invoked for mere validations, and I believe they are not invoked for transactions targeting candidate datastore;
there is no guarantee that when a callback “validates” the configuration (or the operation), another callback in the same transaction does not make the configuration invalid, e.g. by configuring from-vrf instance.
And then there are prepare-phase subscribers, as mentioned by Conny. I would say they stay somewhere between those two - both in resulting code complexity as well as in other drawbacks. Not sure if there is any reason to pick that instead of a validation callback (or a transaction hook).
One more comment: is it possible to rethink the use case? As said, it goes somewhat against YANG and transactionality principles, plus it has some unclear corner cases: for instance, what constitutes invalid “remove” operation - is it valid to remove a vrf instance and in the same transaction add a reference to it? Is it valid if the operations are done in this order, and invalid if done in the opposite order?