Reverse-specification of ec2-api — branch 17.0.0
Branch
17.0.0@32740781f605— repository https://opendev.org/openstack/ec2-api (retired; local branchpre-retirement) — analysed on 2026-10-29
- Repository: https://opendev.org/openstack/ec2-api (local clone
OpenKeel/ec2-api) — specified branch: tag17.0.0= local branchpre-retirement@32740781f6050cbb08847066fc2fef996d93f2a1(2023-06-23, last release before the project was retired;2023.2-eoladds only two CI commits) — analysed on: 2026-10-29. - Scope: the whole service:
ec2-api(EC2 Query API),ec2-api-metadata(instance metadata),ec2-api-s3(S3 object store),ec2-api-manage(DB commands); the rewrite pack (step 8) foropenkeel-ec2-api. The change history (step 7) covers the last two cycles,15.0.0.0rc1..17.0.0(12 commits). - Clients whose expectations are in the pack: AWS CLI / boto3 (botocore 1.29.118 EC2 model, checked mechanically), Terraform's AWS provider (aws-sdk-go, same model; provider behaviour [inferred]), euca2ools / boto2 (SigV2), instance metadata consumers (cloud-init).
- Other repositories cited:
keystone:openstack/keystonestable/2026.2@e6dc5ae07,keystoneclient:python-keystoneclientstable/2026.2@b76de6a3(signature verification),novaclient:/neutronclient:local clones (exception attributes only).
| File | Content |
|---|---|
| 01-query-api_17.0.0.md | the EC2 Query wire protocol (pipeline, SigV2/SigV4 through Keystone, parameter decoding, dispatcher, XML), the 97 actions by family with their translation to OpenStack, the metadata API and the S3 API |
| 02-errors_17.0.0.md | error paths and bodies, validation errors, catalogue of 87 exception classes with raise sites, defects, errors outside the API |
| 03-interactions_17.0.0.md | database (items/tags, item model per kind, reconstructed SQL), Keystone ec2tokens frames, Nova/Neutron/Cinder/Glance calls, S3, metadata, cache, openssl |
| 04-scenarios_17.0.0.md | sequence diagrams: authenticated request, RunInstances in a VPC, default VPC, elastic IP, CreateImage, metadata, termination clean-up |
| 05-async-processing_17.0.0.md | no bus or periodic task; green threads, Describe reconciliation, default VPC, metadata cache |
| 06-change-history_17.0.0.md | 15.0.0.0rc1..17.0.0 (12 commits) as change specifications: own key-pair generation (+ DB autocommit), KernelId/RamdiskId rejected, install-script fixes |
| 07-rewrite-pack_17.0.0.md | rewrite pack: EC2 ID mapping, decoding, BDM merge, states, route tables in Neutron, S3 import, describe, metadata, Keystone contract; client expectations; DB strategy; conformance |
| _rewrite/ | machine-readable action catalogue and probes, recorded exchanges, botocore compatibility, schemas, config, tools |
| _verification_17.0.0.md | every file:line reference mapped to the function it points into |
Overview
ec2-api is a translation layer: it serves the AWS EC2 Query API (Action=RunInstances&Version=…, XML responses, AWS error codes) and turns each action into calls to Nova, Neutron, Cinder and Glance made with the caller's own Keystone token. Requests are signed with AWS signatures (v2 or v4) using EC2 credentials stored in Keystone; ec2-api does not verify them itself but forwards them to Keystone's POST /v3/ec2tokens, which returns a token for the credential's user and project.
Its only state is a two-table database: items, which maps every EC2 identifier (i-, ami-, vol-, snap-, vpc-, subnet-, eni-, sg-, eipalloc-, rtb-, igw-, dopt-, vgw-, cgw-, vpn- followed by 8 hex digits) to an OpenStack UUID and keeps the attributes OpenStack has no place for, and tags. Several EC2 resources exist only there (route tables, internet and VPN gateways, DHCP options, customer gateways, VPN connections) and are realised in Neutron as router gateways, subnet host routes, port DHCP options and VPNaaS objects. A VPC is a Neutron router, an EC2 subnet a Neutron network with one subnet, an ENI a port, an elastic IP a floating IP.
Two side services complete it: ec2-api-metadata builds the EC2 instance-metadata tree from a DescribeInstances behind Neutron's metadata proxy, and ec2-api-s3 is a minimal, unauthenticated S3 server kept as a source of AMI bundles for RegisterImage.
Architecture
flowchart LR cli[AWS CLI / boto3 / Terraform / euca2ools] -->|HTTPS POST / Action=…<br/>AWS SigV4 or SigV2| api[ec2-api :8788<br/>paste: faultwrap, log, EC2KeystoneAuth, Requestify, Executor] api -->|POST /v3/ec2tokens<br/>signature check| ks[Keystone] api -->|SQL items, tags| db[(MariaDB ec2api)] api -->|REST, user token| nova[Nova] api -->|REST, admin session<br/>all_tenants listings| nova api -->|REST, user token| neu[Neutron incl. VPNaaS] api -->|REST, user token| cin[Cinder] api -->|REST, user token| gl[Glance] api -->|S3 GetObject, user's EC2 credential| s3[S3 endpoint<br/>ec2-api-s3 or Swift s3api] vm[Instance] -->|169.254.169.254| nmp[Neutron metadata proxy] nmp -->|X-Instance-ID, signed| md[ec2-api-metadata :8789] md -->|admin session| nova md -->|admin session| neu md -->|SQL| db md -->|oslo.cache| cache[(memcached)] md -->|/openstack/* proxied| nmeta[Nova metadata :8775] s3srv[ec2-api-s3 :3334] -->|files| fs[(buckets_path)]
Processes
| Process | Entry point | Role | Listens to / consumes |
|---|---|---|---|
| ec2-api | ec2api/cmd/api.py:30 → paste ec2api (etc/ec2api/api-paste.ini:5) | EC2 Query API | HTTP ec2api_listen:ec2api_listen_port (0.0.0.0:8788), ec2api_workers processes (ec2api/service.py:30) |
| ec2-api-metadata | ec2api/cmd/api_metadata.py:30 → paste metadata (etc/ec2api/api-paste.ini:31) | EC2 instance metadata | HTTP 0.0.0.0:8789 (ec2api/service.py:42) |
| ec2-api-s3 | ec2api/cmd/api_s3.py:29 → ec2api/s3/s3server.py:68 | S3 store for image bundles | HTTP 0.0.0.0:3334 (ec2api/s3/s3server.py:55) |
| ec2-api-manage | ec2api/cmd/manage.py:61 | db_sync [version], db_version | — |
No process consumes a message bus or runs periodic tasks (05).
External dependencies
| System | Protocol | Library | Configuration | Usage |
|---|---|---|---|---|
| Keystone | HTTPS JSON | requests, keystoneauth1, keystoneclient | keystone_ec2_tokens_url, [keystone_authtoken] | signature verification per request; catalogue; service session for admin reads; user EC2 credentials for S3 |
| Nova (≤ 2.32) | REST | novaclient | nova_service_type | instances, flavors, key pairs, AZs, quotas, volume and interface attach, createImage |
| Neutron (+ VPNaaS) | REST | neutronclient | — | VPCs, subnets, ports, routers, floating IPs, security groups, VPN |
| Cinder | REST | cinderclient | cinder_service_type | volumes, snapshots |
| Glance v2 | REST | glanceclient | — | images, web-download import |
| MariaDB/MySQL | SQL | oslo.db (legacy facade), sqlalchemy-migrate | [database] | ID mapping, DB-only resources, tags |
| S3 | REST SigV4 | botocore | s3_url, s3_region | AMI bundle import |
| Nova metadata | HTTP | httplib2 | [metadata] | /openstack/ paths |
| oslo.cache back end | memcached etc. | oslo.cache | [cache] | metadata cache |
openssl | process | oslo.concurrency | x509_root_private_key, image_decryption_dir | bundle decryption |
Key figures
- 97 EC2 actions (48 in
CloudController, 49 VPC actions inVpcCloudController), all present in the AWS SDK model; theapi-ref"supported features" list matches the code exactly (api-ref/source/supported_features.inc:7). 21 describers with filter maps. - 3 HTTP services; 0 RPC calls, 0 bus consumers, 0 periodic tasks; 2 green-thread background jobs (CreateImage, S3 import).
- 2 tables (
items,tags), 1 migration; 17 item kinds plus 5 derived ID forms (rtbassoc-,eni-attach-,eipassoc-, defaultsg-, reservationsr-). - 87 exception classes, 78 of them EC2 client errors (HTTP 400) with 78 distinct EC2 codes.
- 15 543 lines of Python outside tests; 34 unit-test modules, 443 tests (all pass on Python 3.10 except
test_hacking, not run). - Conformance data: 1009 + 33 + 12 + 7 recorded exchanges, 955 with the OpenStack/DB calls of the action; 726 responses parsed by botocore.
Points of attention
- **Any parameter the code does not declare causes
500 TypeError** —DryRun,TagSpecification(Terraform tags on create), the AWS CLI's--protocol/--port/--cidrform of security group rules, newer RunInstances options, and the SigV4 query-string parameters (presigned requests) (ec2api/api/cloud.py:76,ec2api/api/__init__.py:252;_rewrite/botocore-compat_17.0.0.md). - Every parameter value is type-coerced (
ec2api/api/ec2utils.py:52): a numeric tag value (Value=123) gives500 TypeError, a descriptionTrue500 UnboundLocalError,0755becomes 493 (probes in_rewrite/ec2-actions_17.0.0.json). - OpenStack exceptions leak with their status and Python class name as EC2 code (
NotFound,Forbidden…); programming errors leak asTypeError,KeyError(ec2api/api/__init__.py:304). - Describe operations write: they create EC2 IDs for OpenStack objects and delete items whose object vanished (
ec2api/api/common.py:389); terminated instances disappear from the DB only on a later describe. - ID mapping is global:
os_idis unique across kinds and projects, foreign objects get project-less ID rows, and public images are found with aLIKE '%"is_public": True%'on JSON that only works with case-insensitive matching and Python's JSON separators (ec2api/db/sqlalchemy/api.py:228). - **One Keystone
ec2tokenscall per request**, without timeout or cache (ec2api/api/__init__.py:211); Keystone must enable theec2credentialmethod. - SigV4 only works for POST form requests (ec2-api sends empty
paramsto Keystone for SigV4,ec2api/api/__init__.py:182). - Errors before the Executor are not EC2 XML (missing
Action/Version, timestamps: webob plain text, 403 for timestamps) (ec2api/api/__init__.py:262). - No transactions, no locks except a per-host file lock for default-VPC creation (
ec2api/api/vpc.py:175); partial failures are compensated by in-memory clean-ups that can themselves fail and orphan Neutron/Nova objects (ec2api/api/common.py:63). - The S3 server has no authentication and a prefix-based path check (
ec2api/s3/s3server.py:256); the VPN pre-shared keys are stored in clear (ec2api/api/vpn_connection.py:93); the AES key of S3 bundles is passed onopenssl's command line (ec2api/api/image.py:1037). DhcpOptionsassociation updates the ports of every VPC of the project (ec2api/api/dhcp_options.py:147);CreateSecurityGroupalways applies the VPC character set (ec2api/api/cloud.py:69); StartInstances/StopInstances returntrueinstead of state changes.- EC2-Classic floating IPs use Nova
addFloatingIp, removed in Nova microversion 2.44 (ec2api/api/address.py:321); ec2-api pins 2.32 (ec2api/clients.py:59).
Blind spots / [inferred]
- No black-box run: the upstream functional suite (
ec2api-tempest-plugin) and a devstack were not available; behaviour against real OpenStack services is read from the code and the unit tests (whose OpenStack and DB layers are mocks). - Terraform AWS provider requirements (§ 4.1 of 07) are inferred from the SDK model and the provider's known behaviour, not from a run.
- The MySQL branch of the migration was not executed (SQLite only).
- Keystone and keystoneclient are cited at
stable/2026.2(the OpenKeel target), not at the 2023 versions ec2-api 17.0.0 was released with; signature rules may have changed since (theec2credentialmethod requirement is recent). ec2api/wsgi.py(eventlet server, SSL) andec2api/hackingwere not specified: framework code.
Source: reverse-spec-ec2-api/00-summary_17.0.0.md. The _rewrite/ and _inventory/ files are not published on the site.