Maintenance Events¶
A Maintenance represents a planned, scheduled maintenance window for a provider's service. Maintenance events have a required start and end time, a status that follows a defined lifecycle, and zero or more Impact records linking them to affected NetBox objects.
Model¶
| Field | Type | Notes |
|---|---|---|
name |
CharField (100) | Provider-supplied event ID or ticket. Required. |
summary |
CharField (200) | One-line description. Required. |
provider |
FK -> circuits.Provider |
Required. related_name="maintenance". |
start |
DateTimeField (indexed) | Required. |
end |
DateTimeField (indexed) | Required. Must be after start. |
status |
CharField (choices) | See Status Workflow below. |
original_timezone |
CharField (63) | IANA tz name from the provider's notification, used for "show in original tz" display. |
internal_ticket |
CharField (100) | Your organisation's change reference. |
acknowledged |
BooleanField | Whether the event has been acknowledged by the NOC. |
comments |
TextField | Free-form notes. |
impact |
TextField | Free-form impact description (separate from the structured Impact records). |
replaces |
FK -> Maintenance |
Self-reference for rescheduled events. Set when a new event replaces an old one; the old event's status is auto-set to RE-SCHEDULED via signal. |
tags |
M2M -> NetBox Tag | Standard NetBox tagging. |
The model inherits from BaseEvent (an abstract NetBoxModel) which contributes name, summary, provider, start, original_timezone, internal_ticket, acknowledged, comments, and impact. Maintenance adds end (required), status, and replaces.
clone_fields includes everything except status and replaces, so the Clone action in the UI gives you a fresh editable copy.
Status workflow¶
Status values are defined in notices/choices.py:MaintenanceTypeChoices and follow the BCOP standard.
+-> CANCELLED
|
TENTATIVE -> CONFIRMED -> IN-PROCESS -> COMPLETED
| |
| +-> RE-SCHEDULED (set automatically when a replacement is created)
|
+-> UNKNOWN (used when the provider has not yet specified)
| Status | Colour | When to use |
|---|---|---|
TENTATIVE |
yellow | Initial state when a notification is parsed; provider has proposed a window. |
CONFIRMED |
green | Provider has confirmed the window. Acknowledge once you've reviewed it. |
IN-PROCESS |
orange | Maintenance has started. Set automatically only by the Mark In Progress quick action. |
COMPLETED |
indigo | Maintenance finished successfully. |
CANCELLED |
gray | Provider cancelled before the start time. |
RE-SCHEDULED |
teal | Auto-set on the original event when a new Maintenance is created with replaces=<this event>. |
UNKNOWN |
blue | Used when status cannot be determined. |
Quick actions¶
The Operations dropdown on the Maintenance detail page exposes four POST-only actions and one GET form. All require notices.change_maintenance.
| Action | URL | Effect |
|---|---|---|
| Acknowledge | POST .../maintenance/<id>/acknowledge/ |
Sets acknowledged = True. Idempotent. |
| Mark In Progress | POST .../maintenance/<id>/mark-in-progress/ |
Sets status = IN-PROCESS. Refused if already COMPLETED or CANCELLED. |
| Mark Completed | POST .../maintenance/<id>/mark-completed/ |
Sets status = COMPLETED. Refused if CANCELLED. No-op if already COMPLETED. |
| Cancel | GET/POST .../maintenance/<id>/cancel/ |
Shows confirmation page on GET; sets status = CANCELLED on POST. Refused if already COMPLETED or CANCELLED. |
| Reschedule | GET/POST .../maintenance/<id>/reschedule/ |
Opens an edit form for a new Maintenance pre-populated from the original; setting replaces automatically transitions the original to RE-SCHEDULED via post-save signal. |
Each quick action snapshots the object first so the changelog records the change.
Reschedule flow¶
- User clicks Reschedule on the original Maintenance.
MaintenanceRescheduleViewbuilds a fresh, unsavedMaintenanceinstance, copies every field from the original exceptid/created/last_updated, setsreplacesto the original, resetsstatustoTENTATIVE.- User edits the new start/end and saves.
- The post-save signal observes that the new event has
replacesset and updates the original event's status toRE-SCHEDULED.
Both events remain in the database; you can navigate from the new event to the replaced one via the replaces link, and the list view exposes a has_replaces filter to find rescheduled events.
Timezone handling¶
Provider notifications often quote times in the provider's local timezone. The plugin stores start and end in UTC (Django's standard) and additionally records original_timezone so the UI can display both:
Maintenance.get_start_in_original_tz()returns the start time inoriginal_timezoneif set, otherwise falls back tostartas-is.Maintenance.get_end_in_original_tz()is the same forend.Maintenance.has_timezone_difference()returnsTrueonly whenoriginal_timezoneis set and differs from the NetBox active timezone, so the template only renders the dual display when it would actually be useful.
original_timezone accepts any IANA name; the form exposes the curated list in notices/choices.py:TimeZoneChoices (Africa, America, Asia, Australia, Europe, Pacific groups), but anything zoneinfo.ZoneInfo() accepts will work.
Validation¶
Maintenance.clean() (inherited from BaseEvent) enforces end >= start.
Impact.clean() enforces that you cannot add or modify Impacts on a Maintenance whose status is COMPLETED or CANCELLED. Once a maintenance is finalized, its impact set is frozen for audit purposes.
Detail view¶
The Maintenance detail page (MaintenanceView) renders four panels:
- Operations dropdown (the quick actions above plus standard Edit/Clone/Delete).
- Details card with all model fields, including the original-timezone display when applicable.
- Impacts table with prefetched
sitesandlocationscolumns (resolved via the resolver registry). - Notifications table listing every
EventNotificationlinked to this event. - Timeline showing the most recent 20 changelog entries with status-coloured icons.
REST API¶
The Maintenance endpoints are documented in detail in the REST API reference. Quick reference:
GET /api/plugins/notices/maintenance/
POST /api/plugins/notices/maintenance/
GET /api/plugins/notices/maintenance/{id}/
PATCH /api/plugins/notices/maintenance/{id}/
PUT /api/plugins/notices/maintenance/{id}/
DELETE /api/plugins/notices/maintenance/{id}/
Filterable fields (via MaintenanceFilterSet):
id,name,summary,status,provider_id,start,end,original_timezone,internal_ticket,acknowledged,impact,commentsreplaces_id(find the new event that replaces a given one)has_replaces=true|falsesite_id,region_id,site_group_id,location_id(traverseImpact.sites/Impact.locationscache)
Free-text search via ?q= matches name, summary, internal_ticket, and impact.