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/<service>/*.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/<service>/index.js] client -->|axios, X-Auth-Token, microversion| http[(HTTP, same origin:<br/>/api/openstack/skyline/… or<br/>/api/openstack/<region>/<service>/…)]
| Layer | What it is | Where |
|---|---|---|
| Route | React Router entry: path → component, inside a layout (BaseLayout, AuthLayout…) | e.g. src/pages/compute/routes/index.js:40 (PATH = '/compute') |
| Page | a 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 missing | src/containers/List/index.jsx:664 (getDataWithPolicy), src/containers/List/index.jsx:254 (endpointError) |
| Action | a class extending ConfirmAction, ModalAction, FormAction or StepAction, with id, title, policy, allowedCheckFunc (state check on the item) and onSubmit | e.g. src/pages/compute/containers/Instance/actions/Stop.jsx:21 |
| Store | MobX store extending stores/base; binds one client resource (get client()) and implements list paging, project-name enrichment and CRUD | src/stores/base.js:34, src/stores/base.js:360 (fetchList) |
| Client | declarative table of resources; src/client/client/base.js turns each entry into list, listDetail, show, create, update, patch, delete, head and extra operations | src/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) | Routes | Pages | Actions | Main services | Routes file |
|---|---|---|---|---|---|
auth (/auth) — login, TOTP, change password, SSO callback | 3 | 0 | 0 | skyline (login, sso, config), keystone (password change) | src/pages/auth/routes/index.js:19 |
base (/base) — overview, 404 | 2 | 0 | 1 | nova, cinder, neutron quotas; skyline | src/pages/base/routes/index.js:20 |
basic (/) — top-level router and redirects | 16 | 0 | 0 | — | src/pages/basic/routes/index.js:68 |
compute (/compute) — instances, flavors, images, keypairs, server groups, hypervisors, host aggregates, bare metal nodes | 34 | 47 | 80 | nova, glance, neutron, cinder, ironic, placement | src/pages/compute/routes/index.js:40 |
storage (/storage) — volumes, snapshots, backups, volume types, QoS, storage backends, Swift containers | 20 | 20 | 55 | cinder, swift | src/pages/storage/routes/index.js:31 |
network (/network) — networks, subnets, ports, routers, floating IPs, security groups, QoS, VPN, firewalls, load balancers, DNS, topology, agents | 74 | 81 | 111 | neutron, octavia, designate | src/pages/network/routes/index.js:61 |
share (/share) — shares, share types, networks, groups, servers, access rules | 22 | 24 | 35 | manila | src/pages/share/routes/index.js:34 |
identity (/identity) — domains, projects, users, groups, roles, quotas | 21 | 11 | 35 | keystone, nova/cinder/neutron/manila quotas | src/pages/identity/routes/index.js:34 |
configuration (/configuration-admin) — system info (services, agents), metadata definitions, Skyline settings | 5 | 14 | 17 | nova, neutron, cinder, heat, glance, skyline settings | src/pages/configuration/routes/index.js:23 |
heat (/heat) — stacks | 4 | 7 | 3 | heat | src/pages/heat/routes/index.js:21 |
database (/database) — Trove instances, backups, configurations | 9 | 20 | 16 | trove | src/pages/database/routes/index.jsx |
container-infra (/container-infra) — Magnum clusters and templates | 11 | 17 | 6 | magnum | src/pages/container-infra/routes/index.js:25 |
container-service (/container-service) — Zun containers and capsules | 10 | 16 | 16 | zun | src/pages/container-service/routes/index.js:26 |
ha (/ha) — Masakari segments, hosts, notifications | 7 | 12 | 6 | masakari | src/pages/ha/routes/index.js:23 |
| barbican — secrets, containers | 2 | 3 | 2 | barbican | src/pages/barbican/routes/index.jsx |
monitor (/monitor-center) — Prometheus dashboards | 5 | 0 | 0 | skyline query / query_range | src/pages/monitor/routes/index.js:23 |
management (/management) — recycle bin | 0 | 1 | 2 | skyline extension/recycle_servers, nova | src/pages/management/routes/index.js:20 |
| user-center — credentials (application credentials), profile | 3 | 1 | 2 | keystone | src/pages/user-center/routes/index.js |
| shared components (global header: switch project, region, token) | — | — | 5 | skyline | src/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 callsGET /profileandGET /policiesin 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}plusregionwhen several regions exist. A 401 whosedetailis an object withtotp_requiredswitches the form to a TOTP step and keepsdetail.receipt(src/pages/auth/containers/Login/index.jsx:296). Adetailstring containingThe password is expired and needs to be changed for useropens the change-password page (src/pages/auth/containers/Login/index.jsx:306). - The TOTP step posts
{domain, username, passcode, receipt}tologin/totp;detailequal toreceipt_expiredorinvalid_totpselects the message (src/pages/auth/containers/Login/index.jsx:346). - The profile's
keystone_tokenis stored inlocalStoragewith a 10-day local expiry (src/stores/root.js:117,src/utils/local-storage.js:31). Every later request carries it asX-Auth-Token, but **only while thetime_expiredcookie is in the future** (src/client/client/request.js:56). - Switching project or region posts to
switch_project/{id}orswitch_region/{region}. The console then clears every store andlocalStorageexcept the language, and reloads profile and policies (src/stores/root.js:271,src/stores/root.js:227,src/stores/root.js:317).
- On page load outside
- Flow:
POST /api/openstack/skyline/api/v1/login→ profile +sessionandtime_expiredcookies.GET profile+GET policies(withX-Auth-Token).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.GET {endpoints.neutron}/v2.0/extensions: enables extension-dependent screens; a failure leaves the list empty (src/stores/root.js:217).- 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 whentime_expiredpasses: 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 thesessioncookie is notHttpOnly(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/instanceoverrideslistFetchByClientto call skyline-apiserver'sGET extension/servers, which aggregates Nova, Glance and Neutron (src/stores/nova/instance.js:78). The parameters seen on the wire arelimit=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}withOpenstack-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.
- The list does not call Nova directly:
- 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:createandos_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_groupsitems are sent as{"name": <security group id>}(src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:717): Nova must accept an ID inname.- When Cinder is present, the root disk and data disks become
block_device_mapping_v2entries (source_typeimage,volume,blank;destination_typevolume), withdelete_on_terminationfrom the form (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:728). An ISO image boots as acdrombehind a blank data disk. - A fixed IP is refused when
count > 1. Several instances are requested withmin_count = max_count = countandreturn_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 becomesOS-SCH-HNT:scheduler_hints.group(src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:760).
- Flow: 1. (synchronous)
POST {endpoints.nova}/v2.1/serverswithOpenstack-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 isBUILD. - Errors: a 403 whose body has
forbidden.messagecontainingQuota exceededis 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}/actionwith a one-key body.stores/nova/instance.operationbuilds{<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;confirmResizeandrevertResizeare built in their action classes (src/pages/compute/containers/Instance/actions/ConfirmResize.jsx:54). Each action'sallowedCheckFuncchecks 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-migrateLive2.30,lock2.42,rebuildandrescue2.93, plusremote_console2.53 forremote-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-consoleswith{"remote_console": {"protocol": "vnc", "type": "novnc"}}(serial/serialfor Ironic instances), microversion 2.53 (src/stores/nova/instance.js:260). Thenwindow.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}/actionwithos-extend,os-retype,os-set_bootable,os-reset_status,os-volume_upload_image,revert(src/stores/cinder/volume.js:137), attach/detach through Novaservers/{id}/os-volume_attachments(src/client/nova/index.js:35). Cinder calls carryOpenstack-Api-Version: volume 3.59;os-extendaddsvolume 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, thenPOST subnetswhen 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 byproject_id, withlimit,sort_keyandsort_dir(recorded:networks?project_id=p1&limit=10&sort_key=status&sort_dir=desc). - Floating IPs: allocate (
POST floatingips), associate (PUT floatingips/{id}withport_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 uploadPUT images/{id}/file(application/octet-stream, the file streamed from the browser) orPOST images/{id}/import(src/stores/glance/image.js:92,src/stores/glance/image.js:104). The upload goes through the nginx proxy, which hasclient_max_body_size 0andproxy_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/queryandquery_rangewith 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 callsupdate(null, body), so no key in the path,src/stores/skyline/setting.js:29),DELETE setting/{key}(reset). The catalogue shows the genericsetting/{id}template forPUT; the real path issetting.
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.