OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

skyline-console — Ui modules

UI modules, pages and actions — skyline-console — branch stable/2026.2

Branch stable/2026.2 @ 806d200392ee (9.0.0-1-g806d2003) — repository https://opendev.org/openstack/skyline-console — analysed on 2026-10-29

skyline-console has no REST API of its own. For a client, this file plays the role of the CLI commands: it lists what a user can see (routes and pages) and do (actions), and the HTTP operations that each one triggers. The exhaustive, generated tables are in [_rewrite/ui-catalogue_stable-2026.2.md](_rewrite/ui-catalogue_stable-2026.2.md) (251 routes, 274 list/detail pages, 392 actions, each with file:line, policy and the HTTP calls reached). This file explains the structure and describes the flows that matter most.

1. How a page or an action reaches the network

Every screen follows the same four-layer chain. It is the key to reading the catalogue.

flowchart LR
  route[Route<br/>src/pages/*/routes] --> page[Page: List / Detail container<br/>src/containers/List]
  page -->|fetchList / fetchDetail| store[Store<br/>src/stores/&lt;service&gt;/*.js]
  action[Action: Confirm / Modal / Form / Step<br/>src/pages/*/containers/*/actions] -->|onSubmit → store.method| store
  store -->|this.client.op| client[Client resource<br/>src/client/&lt;service&gt;/index.js]
  client -->|axios, X-Auth-Token, microversion| http[(HTTP, same origin:<br/>/api/openstack/skyline/… or<br/>/api/openstack/&lt;region&gt;/&lt;service&gt;/…)]
LayerWhat it isWhere
RouteReact Router entry: path → component, inside a layout (BaseLayout, AuthLayout…)e.g. src/pages/compute/routes/index.js:40 (PATH = '/compute')
Pagea class extending containers/List (lists) or containers/TabDetail (detail pages). Before fetching, it checks the page's policy against the policy list returned by skyline-apiserver, and hides itself when a required endpoint is missingsrc/containers/List/index.jsx:664 (getDataWithPolicy), src/containers/List/index.jsx:254 (endpointError)
Actiona class extending ConfirmAction, ModalAction, FormAction or StepAction, with id, title, policy, allowedCheckFunc (state check on the item) and onSubmite.g. src/pages/compute/containers/Instance/actions/Stop.jsx:21
StoreMobX store extending stores/base; binds one client resource (get client()) and implements list paging, project-name enrichment and CRUDsrc/stores/base.js:34, src/stores/base.js:360 (fetchList)
Clientdeclarative table of resources; src/client/client/base.js turns each entry into list, listDetail, show, create, update, patch, delete, head and extra operationssrc/client/client/base.js:174 (generateResource), src/client/client/base.js:369 (generateAll)

Policy checks are UI-only. An action is shown when checkItemPolicy says so (src/resources/skyline/policy.js:168). The rule is first prefixed with its service (os_compute_api:servers:stop → nova:os_compute_api:servers:stop, src/resources/skyline/policy.js:108), then looked up in GET /policies. A rule missing from that list counts as allowed (src/resources/skyline/policy.js:141); an empty list denies everything (src/resources/skyline/policy.js:176). The OpenStack service still enforces its own policy when the call arrives.

Menus depend on the profile's endpoints. A menu entry with endpoints: '<key>' is hidden when profile.endpoints[<key>] is absent (src/layouts/Base/index.jsx:147, src/stores/root.js:199). Keys used: cinder, swift, neutron_qos, octavia, barbican, neutron_vpn, neutron_firewall, designate, manilav2, heat, trove, zun, magnum (src/layouts/menu.jsx:169 to src/layouts/menu.jsx:835), and ironic in the administrator menu (src/layouts/admin-menu.jsx:163). The keys are the values of skyline-apiserver's service_mapping and extension_mapping options (skyline-apiserver:skyline_apiserver/config/openstack.py:121, skyline-apiserver:skyline_apiserver/config/openstack.py:149).

Administrator pages (…-admin routes) are reachable only when the user has a system-scoped admin or reader role, which the console checks itself with GET /v3/system/users/{id}/roles (src/stores/root.js:155).

2. Modules

Counts come from the generated catalogue. "Main services" lists the OpenStack APIs the module's actions and pages reach.

Module (URL prefix)RoutesPagesActionsMain servicesRoutes file
auth (/auth) — login, TOTP, change password, SSO callback300skyline (login, sso, config), keystone (password change)src/pages/auth/routes/index.js:19
base (/base) — overview, 404201nova, cinder, neutron quotas; skylinesrc/pages/base/routes/index.js:20
basic (/) — top-level router and redirects1600—src/pages/basic/routes/index.js:68
compute (/compute) — instances, flavors, images, keypairs, server groups, hypervisors, host aggregates, bare metal nodes344780nova, glance, neutron, cinder, ironic, placementsrc/pages/compute/routes/index.js:40
storage (/storage) — volumes, snapshots, backups, volume types, QoS, storage backends, Swift containers202055cinder, swiftsrc/pages/storage/routes/index.js:31
network (/network) — networks, subnets, ports, routers, floating IPs, security groups, QoS, VPN, firewalls, load balancers, DNS, topology, agents7481111neutron, octavia, designatesrc/pages/network/routes/index.js:61
share (/share) — shares, share types, networks, groups, servers, access rules222435manilasrc/pages/share/routes/index.js:34
identity (/identity) — domains, projects, users, groups, roles, quotas211135keystone, nova/cinder/neutron/manila quotassrc/pages/identity/routes/index.js:34
configuration (/configuration-admin) — system info (services, agents), metadata definitions, Skyline settings51417nova, neutron, cinder, heat, glance, skyline settingssrc/pages/configuration/routes/index.js:23
heat (/heat) — stacks473heatsrc/pages/heat/routes/index.js:21
database (/database) — Trove instances, backups, configurations92016trovesrc/pages/database/routes/index.jsx
container-infra (/container-infra) — Magnum clusters and templates11176magnumsrc/pages/container-infra/routes/index.js:25
container-service (/container-service) — Zun containers and capsules101616zunsrc/pages/container-service/routes/index.js:26
ha (/ha) — Masakari segments, hosts, notifications7126masakarisrc/pages/ha/routes/index.js:23
barbican — secrets, containers232barbicansrc/pages/barbican/routes/index.jsx
monitor (/monitor-center) — Prometheus dashboards500skyline query / query_rangesrc/pages/monitor/routes/index.js:23
management (/management) — recycle bin012skyline extension/recycle_servers, novasrc/pages/management/routes/index.js:20
user-center — credentials (application credentials), profile312keystonesrc/pages/user-center/routes/index.js
shared components (global header: switch project, region, token)——5skylinesrc/components/Layout/GlobalHeader/ProjectTable.jsx:21

Total: 251 routes, 274 pages, 392 actions. 374 actions (95 %) resolve statically to at least one HTTP call. The 18 others are UI-only (copy a URL, show the token, open the console in a new window) or build their call dynamically. They are listed with "no store call resolved" in the catalogue.

3. Sheets for the main flows

3.1 Sign in, restore the session, switch project or region

  • Purpose: open an authenticated session and load what the UI needs to decide what to show.
  • Actor: any user with a Keystone account; the login page itself is public.
  • Preconditions: skyline-apiserver reachable under the same origin at /api/openstack/skyline/; the browser accepts cookies.
  • Business rules:
    • On page load outside /login, the console calls GET /profile and GET /policies in parallel. Any failure sends the user to /auth/login?referer=<path> (src/core/index.jsx:65, src/stores/root.js:207, src/stores/root.js:254).
    • The login body is {domain, username, password} plus region when several regions exist. A 401 whose detail is an object with totp_required switches the form to a TOTP step and keeps detail.receipt (src/pages/auth/containers/Login/index.jsx:296). A detail string containing The password is expired and needs to be changed for user opens the change-password page (src/pages/auth/containers/Login/index.jsx:306).
    • The TOTP step posts {domain, username, passcode, receipt} to login/totp; detail equal to receipt_expired or invalid_totp selects the message (src/pages/auth/containers/Login/index.jsx:346).
    • The profile's keystone_token is stored in localStorage with a 10-day local expiry (src/stores/root.js:117, src/utils/local-storage.js:31). Every later request carries it as X-Auth-Token, but **only while the time_expired cookie is in the future** (src/client/client/request.js:56).
    • Switching project or region posts to switch_project/{id} or switch_region/{region}. The console then clears every store and localStorage except the language, and reloads profile and policies (src/stores/root.js:271, src/stores/root.js:227, src/stores/root.js:317).
  • Flow:
    1. POST /api/openstack/skyline/api/v1/login → profile + session and time_expired cookies.
    2. GET profile + GET policies (with X-Auth-Token).
    3. GET {endpoints.keystone}/v3/system/users/{user_id}/roles: decides access to administrator pages (src/stores/root.js:159). A failure is swallowed: no admin pages.
    4. GET {endpoints.neutron}/v2.0/extensions: enables extension-dependent screens; a failure leaves the list empty (src/stores/root.js:217).
    5. Redirect to the referer, or to the overview.
  • Outcome: user, project, region, roles, endpoints and policies are in the root store. The session ends when the user logs out (POST logout, src/stores/root.js:236), on any 401 (§ 2 of errors), or when time_expired passes: the token is then no longer sent and the next call returns 401.
  • Effects visible elsewhere: none in OpenStack beyond token issue and validation by Keystone (done by skyline-apiserver).
  • Limitations: the raw Keystone token is readable by any script of the page (localStorage), and the session cookie is not HttpOnly (skyline-apiserver:skyline_apiserver/api/v1/login.py:172). Verified on the wire with a stand-in server (§ 7 of the rewrite pack).

3.2 Instance list (project and administrator views)

  • Purpose: show the project's servers (or every project's, on /compute/instance-admin), with image, flavor, IP addresses and floating IPs already joined.
  • Actor: project member or reader; system admin or reader for the admin view.
  • Business rules:
    • The list does not call Nova directly: stores/nova/instance overrides listFetchByClient to call skyline-apiserver's GET extension/servers, which aggregates Nova, Glance and Neutron (src/stores/nova/instance.js:78). The parameters seen on the wire are limit=10&sort_keys=created_at&sort_dirs=desc (recorded, _rewrite/smoke-exchanges_stable-2026.2.json).
    • The detail page uses GET {endpoints.nova}/v2.1/servers/{id} with Openstack-Api-Version: compute 2.79 (src/stores/nova/instance.js:97).
    • Auto-refresh every 30 s, or every 10 s while an instance is in a transition status; see 05.
  • Limitations: on the admin view, projects whose name is unknown are resolved one by one with GET /v3/projects/{id} (src/stores/base.js:235), so N extra calls per refresh in the worst case.

3.3 Create an instance

  • Purpose: boot one or several servers from an image, a volume or an instance snapshot.
  • Actor: project member; policies os_compute_api:servers:create and os_compute_api:os-availability-zone:list (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:34).
  • Preconditions: flavors, images, networks, security groups and keypairs readable (the wizard lists them first: Nova flavors and availability zones, Glance images, Neutron networks, ports and security groups, Cinder volume types and quotas).
  • Business rules (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:689):
    • security_groups items are sent as {"name": <security group id>} (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:717): Nova must accept an ID in name.
    • When Cinder is present, the root disk and data disks become block_device_mapping_v2 entries (source_type image, volume, blank; destination_type volume), with delete_on_termination from the form (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:728). An ISO image boots as a cdrom behind a blank data disk.
    • A fixed IP is refused when count > 1. Several instances are requested with min_count = max_count = count and return_reservation_id = true (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:739).
    • Choosing a host sets hypervisor_hostname (needs microversion ≥ 2.74; the console sends 2.79) (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:744).
    • The password is turned into cloud-init user_data (base64) for Linux images; a server group becomes OS-SCH-HNT:scheduler_hints.group (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:760).
  • Flow: 1. (synchronous) POST {endpoints.nova}/v2.1/servers with Openstack-Api-Version: compute 2.79 (src/stores/nova/instance.js:255). 2. Redirect to the list. 3. (asynchronous) the list refreshes every 10 s while the server is BUILD.
  • Errors: a 403 whose body has forbidden.message containing Quota exceeded is shown as "Quota exceeded"; anything else shows the raw body (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:794). This is a hard expectation on Nova's error body (see 07 § 4).

3.4 Instance state actions (start, stop, lock, reboot, migrate, rebuild…)

  • Purpose: change the state of one server or of a selection.
  • Business rules: every state action is a POST {endpoints.nova}/v2.1/servers/{id}/action with a one-key body. stores/nova/instance.operation builds {<key>: null} or the given body (src/stores/nova/instance.js:291): os-start, os-stop, lock, unlock, pause, unpause, suspend, resume, shelve, unshelve, migrate, os-migrateLive, reboot, rebuild, rescue, unrescue, resize, createImage, os-resetState, forceDelete, restore, os-getConsoleOutput, changePassword, removeFloatingIp; confirmResize and revertResize are built in their action classes (src/pages/compute/containers/Instance/actions/ConfirmResize.jsx:54). Each action's allowedCheckFunc checks the status and the lock before enabling the button (e.g. src/pages/compute/containers/Instance/actions/Stop.jsx:52).
  • Microversions: four action keys raise the version: os-migrateLive 2.30, lock 2.42, rebuild and rescue 2.93, plus remote_console 2.53 for remote-consoles (src/client/client/constants.js:117). See 03 § 2 for how the two headers are merged.
  • Batch: a batch runs one request per item in parallel (Promise.allSettled), and each failure is shown separately (src/components/Tables/Base/ActionButton/index.jsx:355).
  • Outcome: Nova answers 202; the console shows success and refreshes the list after 500 ms, then follows the transition with 10 s refreshes (src/components/Tables/Base/ActionButton/index.jsx:224).

3.5 Open the instance console

  • Purpose: open the noVNC (or serial, for bare metal) console in a new browser window.
  • Flow: POST {endpoints.nova}/v2.1/servers/{id}/remote-consoles with {"remote_console": {"protocol": "vnc", "type": "novnc"}} (serial/serial for Ironic instances), microversion 2.53 (src/stores/nova/instance.js:260). Then window.open(<url>&language=<locale>) (src/pages/compute/containers/Instance/actions/Console.jsx:69).
  • Limitations: the URL returned by Nova is opened directly by the browser, outside the Skyline origin and proxy. The noVNC proxy (novncproxy_base_url) must be reachable from users' browsers, and its URL must already contain a query string, since the console appends &language=. The Zun container console works the same way with a WebSocket opened straight to the URL returned by Zun (src/pages/container-service/containers/Containers/Detail/Console.jsx:37).

3.6 Volumes

  • List: through skyline-apiserver GET extension/volumes (src/stores/cinder/volume.js:58), with the same paging parameters as servers (recorded).
  • Actions: POST {endpoints.cinder}/v3/{project_id}/volumes (create), volumes/{id}/action with os-extend, os-retype, os-set_bootable, os-reset_status, os-volume_upload_image, revert (src/stores/cinder/volume.js:137), attach/detach through Nova servers/{id}/os-volume_attachments (src/client/nova/index.js:35). Cinder calls carry Openstack-Api-Version: volume 3.59; os-extend adds volume 3.60 (src/client/client/constants.js:125).
  • Limitation: the Cinder project ID is part of the path (projectInUrl, src/client/cinder/index.js:27).

3.7 Networks, routers and floating IPs

  • Create network: POST {endpoints.neutron}/v2.0/networks, then POST subnets when a subnet is defined; if the subnet fails, the error body of each call is kept as JSON (src/stores/neutron/network.js:280, src/stores/neutron/network.js:296). Lists are Neutron collections filtered by project_id, with limit, sort_key and sort_dir (recorded: networks?project_id=p1&limit=10&sort_key=status&sort_dir=desc).
  • Floating IPs: allocate (POST floatingips), associate (PUT floatingips/{id} with port_id), router gateway (PUT routers/{id}).
  • Repeated parameters: arrays in query strings are serialised as repeated keys (id=a&id=b, src/client/client/request.js:213).

3.8 Images

  • Create: POST {endpoints.glance}/v2/images, then either a direct upload PUT images/{id}/file (application/octet-stream, the file streamed from the browser) or POST images/{id}/import (src/stores/glance/image.js:92, src/stores/glance/image.js:104). The upload goes through the nginx proxy, which has client_max_body_size 0 and proxy_request_buffering off (skyline-apiserver:skyline_apiserver/templates/nginx.conf.j2:18): any ingress in front must allow large bodies too.

3.9 Monitoring

  • Purpose: dashboards from Prometheus.
  • Flow: GET /api/openstack/skyline/api/v1/query and query_range with PromQL in the query string (src/stores/prometheus/monitor-base.js:98); skyline-apiserver forwards to Prometheus.
  • Limitation: startAutoRefresh (5 s) exists but nothing calls it on this branch (src/resources/prometheus/monitoring.js:312); charts refresh when the range or interval changes.

3.10 Skyline settings (administrator)

  • GET settings, PUT setting (body {key, value}; the store calls update(null, body), so no key in the path, src/stores/skyline/setting.js:29), DELETE setting/{key} (reset). The catalogue shows the generic setting/{id} template for PUT; the real path is setting.

4. Status-driven behaviour (instance example)

The console does not own any lifecycle. It observes the service's statuses and adapts its refresh and its buttons:

stateDiagram-v2
  [*] --> Listed: page mounted (fetchList)
  Listed --> FastPolling: an item is in a transition status
  FastPolling --> Listed: refresh shows no transition (every 10 s)
  Listed --> Listed: auto refresh every 30 s (at most 20 times, i.e. 10 min)
  Listed --> Idle: 20 silent refreshes without user action
  Idle --> Listed: action, selection, paging or manual refresh
  Listed --> Stopped: a fetch fails (auto refresh switched off)
  Stopped --> Listed: manual refresh

Details and sources in 05.

Source: reverse-spec-skyline-console/01-ui-modules_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.