Skip to content

feat(openstack-sync): add NeutronSegmentRange CRD and sync plugin - #2352

Open
geetikabatra wants to merge 3 commits into
mainfrom
add-neutron-segment-range-crd-new-framework
Open

geetikabatra wants to merge 3 commits into
mainfrom
add-neutron-segment-range-crd-new-framework

Conversation

@geetikabatra

Copy link
Copy Markdown

Add a NeutronSegmentRange CRD to the openstack-sync-operator and a segment_ranges hook plus plugin package that reconciles Neutron network segment ranges from custom resources, built on the new SyncPlugin framework.

  • CRD: neutron.understack.rackspace.net/v1alpha1 NeutronSegmentRange
  • plugin: reconcile/create/adopt by owner-prefixed name, prune on removal
  • immutable network_type/physical_network mismatches fail loudly
  • wired into operator values.yaml (plugins.neutronSegmentRanges, disabled)
  • prune driven by framework CleanupPolicy (no manual config.prune gate)

What does this change do?

Upgrade impact

  • This change requires operator action to upgrade. If checked, add the
    upgrade-impact label and a release note: run scriv create from the
    repository root and describe the required action in the generated
    changelog.d/ file. See RELEASING.md.

Operator action means anything a deployment has to do beyond a normal resync:
deploy repo or values changes, new or removed secrets, enabling or disabling a
component, or a manual one-time step.

Add a NeutronSegmentRange CRD to the openstack-sync-operator and a
segment_ranges hook plus plugin package that reconciles Neutron network
segment ranges from custom resources, built on the new SyncPlugin framework.

- CRD: neutron.understack.rackspace.net/v1alpha1 NeutronSegmentRange
- plugin: reconcile/create/adopt by owner-prefixed name, prune on removal
- immutable network_type/physical_network mismatches fail loudly
- wired into operator values.yaml (plugins.neutronSegmentRanges, disabled)
- prune driven by framework CleanupPolicy (no manual config.prune gate)
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

Python test results

1 tests   - 62   1 ✅  - 62   0s ⏱️ -7s
1 suites ± 0   0 💤 ± 0 
1 files   ± 0   0 ❌ ± 0 

Results for commit b1b883f. ± Comparison against base commit 9f78877.

This pull request removes 63 and adds 1 tests. Note that renamed tests count towards both.
tests.test_argo_client.TestArgoClient ‑ test_generate_workflow_name
tests.test_argo_client.TestArgoClient ‑ test_init_with_ssl_verify
tests.test_argo_client.TestArgoClient ‑ test_init_with_token
tests.test_argo_client.TestArgoClient ‑ test_init_without_token
tests.test_argo_client.TestArgoClient ‑ test_kubernetes_token_property
tests.test_argo_client.TestArgoClient ‑ test_run_playbook_creation_failure
tests.test_argo_client.TestArgoClient ‑ test_run_playbook_success
tests.test_argo_client.TestArgoClient ‑ test_run_playbook_with_empty_extra_vars
tests.test_argo_client.TestArgoClient ‑ test_url_stripping
tests.test_argo_client.TestArgoClient ‑ test_wait_for_completion_api_error
…
tests.test_noop ‑ test_noop

♻️ This comment has been updated with latest results.

it must be unique per cloud.
type: string
minLength: 1
maxLength: 255

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are prepending NAME_PREFIX = "understack-sr:" which is 14 chars,
so the CRD’s safe spec.name max should be 241.

this at reconcile time.
type: string
minLength: 1
maxLength: 255

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

physical_network should be capped at 64

physical_network = sa.Column(sa.String(64), nullable=False,
                                 server_default='') 

)
want = {
"network_type": spec["network_type"],
"physical_network": spec.get("physical_network"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes tunnel segment ranges non-idempotent.
For vxlan/gre/geneve, the spec can omit physical_network, but Neutron normalizes non-VLAN segment ranges to physical_network="".

is_vlan = self.network_type == constants.TYPE_VLAN
self.physical_network = kwargs['physical_network'] if is_vlan else ''

- cap spec.name at 241 chars: the 14-char understack-sr: ownership prefix
  has to fit inside Neutron's 255-char name column
- cap physical_network at 64 chars to match Neutron's String(64) column
- normalize physical_network to "" when checking immutable drift. Neutron
  stores non-VLAN ranges with physical_network='' while a tunnelled spec
  omits the field, so the raw comparison reported drift on every reconcile
  of a vxlan/gre/geneve range against its own spec
@cardoe

cardoe commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

CRD schema and the name-prefix ownership marker both look right. Two blockers though.

Needs a rebase first — main moved under it. 9c8535a2 landed after this branch's last commit and converted SyncPlugin.reconcile to return a ReconcileResult, migrating the other three plugins with it. SegmentRangePlugin.reconcile() still returns the bare list[str] from sync_segment_range. runner.py reads result.notes and result.extra_status outside the try/except around the reconcile call, so a plain list raises an uncaught AttributeError on the first CR that syncs cleanly and takes the rest of the credential group down with it. Return ReconcileResult(notes=...), or wrap it in the hook like router_flavors.py does.

CI is green because test_segment_ranges_reconcile.py only calls _validate_spec and _immutable_drift directly. The siblings each ship a test_*_hook.py that drives the plugin through run_sync — that's what would have caught this, and it's worth adding here.

Prune treats an in-use range as a successful delete. _delete_range catches ConflictException and logs "skipping delete", but prune_removed_ranges never records it and never raises. Same thing I flagged on #2337: we can't release the finalizer just because prune() returned normally. Under FINALIZED_PRUNE the CR disappears, the range stays in Neutron, and nothing is left to retry the cleanup. subnet_pools/prune.py has the identical shape but appends to incomplete and raises at the end — this looks like that code minus the last three lines. Test precedent in test_prune.py::test_prune_fails_when_removed_flavor_delete_conflicts.

Smaller:

  • _mutable_updates sends shared and project_id, but both are allow_put: False in neutron_lib/api/definitions/network_segment_range.py — only name, minimum, maximum accept a PUT. A CR that flips shared gets a raw 400 instead of the ConfigError we raise for the other immutable fields. Move them into _immutable_drift and stop advertising them as mutable in the docstring.

  • The CRD description and markers.py both say we adopt existing ranges, but load_managed_ranges only caches names already carrying the understack-sr: prefix. Re-adopting our own range works; one created out-of-band doesn't — we take the create branch and Neutron's unique constraint on (default, network_type, physical_network, minimum, maximum) rejects it. Narrow the wording or implement the lookup.

  • new_cache()'s comment claims the listing is reused by prune. Neither SyncPlugin.prune nor PruneRequest carries the cache, and prune_removed_ranges re-lists from scratch.

values.yaml, the Dockerfile COPY, the 241 cap, and the tunnel-idempotency fix in b1b883f9 all look fine.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants