Skip to content

Writing your own validation

Validation is the service owner's first job: every consumer request arrives as a PENDING change instance, and your automation decides whether it may proceed. The collection gives you the two ends of that job - fetch the queue, write the decision - and leaves the middle to you. This page is that middle on its own; the load balancer scenario shows the same pattern wired into a real BIG-IP deployment.

What "custom validation" is - and isn't

NetOrca validates every declaration against the service's JSON Schema at submission time. A change only becomes PENDING once it has passed, so by the time your playbook sees it the structure, types, enums and required fields are already guaranteed. Re-checking them in Ansible just duplicates the platform.

Your custom validation is for the things a schema can't express:

  • conflicts with the existing estate (is this VIP / name / subnet already taken?),
  • external state - IPAM allocation, CMDB ownership, capacity or quota,
  • business rules that depend on who is asking or what else is deployed.

If you have none of these, there is nothing to write: you simply approve every PENDING change.

The shape

Poll PENDING, then approve. That is the whole play when there is nothing extra to check:

- name: Validate pending changes
  hosts: localhost
  gather_facts: false
  vars:
    # safe by default; -e apply_decisions=true arms the writes
    apply_decisions: false
  tasks:
    - name: Poll the validation queue
      netautomate.netorca.netorca_change_instance_info:
        state: [PENDING]
        exclude_referenced: true
      register: pending

    # HERE IS YOUR CUSTOM VALIDATION
    # Nothing schema-level here - reject the offenders (see below), then:
    - name: Approve each pending change
      netautomate.netorca.netorca_change_instance:
        id: "{{ item.id }}"
        state: APPROVED
      check_mode: "{{ not (apply_decisions | bool) }}"
      loop: "{{ pending.change_instances }}"
      loop_control:
        label: "#{{ item.id }}"

Rejecting some

When you do have a business check, reject the offenders first, then approve the rest - guard the two tasks with your condition and its negation. reason in the log is shown straight back to the consumer:

    - name: Reject changes that collide with the estate
      netautomate.netorca.netorca_change_instance:
        id: "{{ item.id }}"
        state: REJECTED
        log: "rejected: VIP {{ item.new_declaration.declaration.virtual_server.ip }} already in use"
      check_mode: "{{ not (apply_decisions | bool) }}"
      loop: "{{ pending.change_instances }}"
      when: my_conflict_check      # <- your test against item

    - name: Approve the rest
      netautomate.netorca.netorca_change_instance:
        id: "{{ item.id }}"
        state: APPROVED
      check_mode: "{{ not (apply_decisions | bool) }}"
      loop: "{{ pending.change_instances }}"
      when: not my_conflict_check

Each change carries everything your check needs: item.new_declaration.declaration (the desired state), item.change_type (CREATE / MODIFY / DELETE), item.service_item, item.commit_id. Conflict checks are easiest with declaration search - look the value up across the estate and reject if it is already claimed.

Both tasks are idempotent and support real check mode, so keep them behind apply_decisions while you develop: the play reports what it would decide without writing anything.

Approvals are always yours over the API

The service's allow_manual_approval / allow_manual_completion flags govern only GUI (human) approvals. API transitions like these are always available to the owning team's key; a 403 means the wrong key or context, not a missing flag. The full lifecycle is in Check mode and idempotency.

Completing the change

The deployment half of the workflow has the same shape and the same marker: poll APPROVED work, run your infrastructure step - the task marked HERE IS YOUR CUSTOM DEPLOYMENT - then mark the change COMPLETED with a deployed_item. If the deploy fails, don't touch the change: let the run fail and leave it APPROVED, and the next scheduled run retries it (the queue is the state). NetOrca has no "mark this failed" step in the fulfilment loop - never report a failure as COMPLETED, and never transition a change to ERROR. The deploy examples show both an imperative and a declarative variant.

Prefer to have the AI review it?

NetOrca Pack can auto-review incoming PENDING changes with a change_instance_validator processor instead of hand-written checks - see Pack and AI. The two compose: let the AI clear the obvious cases and keep an Ansible pass for the policies you must guarantee.

Try it