一 用户命令到的HTTP请求
一般的 Openstack 用户和管理员能够通过执行简易的 Openstack Commands 来管理和使用 Openstack 。
但需要注意的是,Openstack Services 的 API 并不会识别这类指令,所以在 API 的外层还需要一重转化机制 —— novaclient。
如果是使用 Devstack 进行部署的话,novaclient 会被默认安装在本地,安装目录为 /usr/local/lib/python2.7/dist-packages/novaclient。
我们可以通过设置该目录下的 shell.py 文件中的DEFAULT_OS_COMPUTE_API_VERSION选项来修改 novaclient 的版本。
目前novaclient默认使用的是v2 API,如果希望使用V3 API,可以修改配置。
DEFAULT_OS_COMPUTE_API_VERSION = '3'
执行 Openstack Commands 都发生了什么?
以 nova list 为例进行说明
1. 执行 nova list指令
2. 该指令被 shell.py 捕获
3. shell.py 将所需要的 Header/Context 等信息和 nova list 的操作请求内容进行组合封装,形成一个标准的 HTTP 请求
4. HTTP 请求被 nova-api 监听捕获
5. nova-api 按照 WSGI 规范的流程来完成HTTP请求的分发、路由、执行、响应的操作。
现在以子命令"list"为例,执行"nova list"获取当前所有活跃状态的虚拟机,附加"--debug"选项,可以打印一些中间信息。
[root@localhost ~(keystone_demo)]# nova --debug list
DEBUG (extension:180) found extension EntryPoint.parse('v2token = keystoneauth1.loading._plugins.identity.v2:Token')
DEBUG (extension:180) found extension EntryPoint.parse('v3oauth1 = keystoneauth1.extras.oauth1._loading:V3OAuth1')
DEBUG (extension:180) found extension EntryPoint.parse('admin_token = keystoneauth1.loading._plugins.admin_token:AdminToken')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcauthcode = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectAuthorizationCode')
DEBUG (extension:180) found extension EntryPoint.parse('v2password = keystoneauth1.loading._plugins.identity.v2:Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3samlpassword = keystoneauth1.extras._saml2._loading:Saml2Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3password = keystoneauth1.loading._plugins.identity.v3:Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcaccesstoken = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectAccessToken')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcpassword = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectPassword')
DEBUG (extension:180) found extension EntryPoint.parse('v3kerberos = keystoneauth1.extras.kerberos._loading:Kerberos')
DEBUG (extension:180) found extension EntryPoint.parse('token = keystoneauth1.loading._plugins.identity.generic:Token')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcclientcredentials = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectClientCredentials')
DEBUG (extension:180) found extension EntryPoint.parse('v3tokenlessauth = keystoneauth1.loading._plugins.identity.v3:TokenlessAuth')
DEBUG (extension:180) found extension EntryPoint.parse('v3token = keystoneauth1.loading._plugins.identity.v3:Token')
DEBUG (extension:180) found extension EntryPoint.parse('v3totp = keystoneauth1.loading._plugins.identity.v3:TOTP')
DEBUG (extension:180) found extension EntryPoint.parse('password = keystoneauth1.loading._plugins.identity.generic:Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3fedkerb = keystoneauth1.extras.kerberos._loading:MappedKerberos')
DEBUG (extension:180) found extension EntryPoint.parse('v1password = swiftclient.authv1:PasswordLoader')
DEBUG (extension:180) found extension EntryPoint.parse('token_endpoint = openstackclient.api.auth_plugin:TokenEndpoint')
DEBUG (extension:180) found extension EntryPoint.parse('gnocchi-basic = gnocchiclient.auth:GnocchiBasicLoader')
DEBUG (extension:180) found extension EntryPoint.parse('gnocchi-noauth = gnocchiclient.auth:GnocchiNoAuthLoader')
DEBUG (extension:180) found extension EntryPoint.parse('aodh-noauth = aodhclient.noauth:AodhNoAuthLoader')
DEBUG (session:347) REQ: curl -g -i -X GET http://192.168.0.120:5000/v3 -H "Accept: application/json" -H "User-Agent: nova keystoneauth1/2.18.0 python-requests/2.11.1 CPython/2.7.5"
INFO (connectionpool:214) Starting new HTTP connection (1): 192.168.0.120
DEBUG (connectionpool:401) "GET /v3 HTTP/1.1" 200 196
DEBUG (session:395) RESP: [200] Date: Sat, 24 Mar 2018 10:19:04 GMT Server: Apache/2.4.6 (CentOS) Vary: X-Auth-Token,Accept-Encoding x-openstack-request-id: req-8f183e1c-9817-49e4-b541-e1e0fdb0cb5d Content-Encoding: gzip Content-Length: 196 Connection: close Content-Type: application/json
RESP BODY: {"version": {"status": "stable", "updated": "2017-02-22T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v3+json"}], "id": "v3.8", "links": [{"href": "http://192.168.0.120:5000/v3/";, "rel": "self"}]}}
DEBUG (session:640) GET call to None for http://192.168.0.120:5000/v3 used request id req-8f183e1c-9817-49e4-b541-e1e0fdb0cb5d
DEBUG (base:165) Making authentication request to http://192.168.0.120:5000/v3/auth/tokens
INFO (connectionpool:249) Resetting dropped connection: 192.168.0.120
DEBUG (connectionpool:401) "POST /v3/auth/tokens HTTP/1.1" 201 7793
DEBUG (base:170) {"token": {"is_domain": false, "methods": ["password"], "roles": [{"id": "9fe2ff9ee4384b1894a90878d3e92bab", "name": "_member_"}], "expires_at": "2018-03-24T11:19:04.000000Z", "project": {"domain": {"id": "default", "name": "Default"}, "id": "b38c09cb0b5a4d46820c66052bb0ee94", "name": "demo"}, "catalog": [{"endpoints": [{"url": "http://192.168.0.120:8778/placement";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "17c4fd23e6994597b6f2905c71957b51"}, {"url": "http://192.168.0.120:8778/placement";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "f2041ca3559d41c58598155d777af732"}, {"url": "http://192.168.0.120:8778/placement";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "fbf96657d51941a5b1627b14b5069d11"}], "type": "placement", "id": "126f23393d2c4db98f19ff93b8444d35", "name": "placement"}, {"endpoints": [{"url": "http://192.168.0.120:8777";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "1c2fe371656a49afa2d05b693620dddc"}, {"url": "http://192.168.0.120:8777";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "66c1ac9c0305402ca06dc97379a8f5b0"}, {"url": "http://192.168.0.120:8777";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "fd6418c3d0314af4b93788d7f2469842"}], "type": "metering", "id": "134bde6449bd43239bea70ca9a179b2a", "name": "ceilometer"}, {"endpoints": [{"url": "http://192.168.0.120:8776/v2/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "194f3c15afd4415093c7124bc9a264f9"}, {"url": "http://192.168.0.120:8776/v2/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "40d754a2e89a4baaba65db46a32ed646"}, {"url": "http://192.168.0.120:8776/v2/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "bcf8c85d409e4a119753d6c7530c4365"}], "type": "volumev2", "id": "3e22b4e4252b43639b41da61aef0ad03", "name": "cinderv2"}, {"endpoints": [{"url": "http://192.168.0.120:8042";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "2a68ff1e21d24f1281cdbc2425c767ee"}, {"url": "http://192.168.0.120:8042";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "c7fb7d968dbf4683b4a4a464d2492ed6"}, {"url": "http://192.168.0.120:8042";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "d5e2af9cf3194c67879a2428259c62f6"}], "type": "alarming", "id": "4aa8df57593d495092630f2e85ce6174", "name": "aodh"}, {"endpoints": [{"url": "http://192.168.0.120:8776/v1/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "5c62e261854d40e5b90de3f7a948960a"}, {"url": "http://192.168.0.120:8776/v1/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "92111e55ef4941b4a485ea61af7d2c6c"}, {"url": "http://192.168.0.120:8776/v1/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "d30bc30757f849c79b68e16cd2b9f31f"}], "type": "volume", "id": "5d3d752e84ef4c989fcc917de9042324", "name": "cinder"}, {"endpoints": [{"url": "http://192.168.0.120:5000/v3";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "63ab2cf4942a45b7933eb8b7cde297dc"}, {"url": "http://192.168.0.120:35357/v3";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "b4738b39a020433d9203662dfee96d30"}, {"url": "http://192.168.0.120:5000/v3";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "bef9f544713a47939b0e68848e627956"}], "type": "identity", "id": "5f0e253c80854443be5cab7ed3a77cae", "name": "keystone"}, {"endpoints": [{"url": "http://192.168.0.120:9292";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "5ac11bad61f84a3ebae97c421a067c80"}, {"url": "http://192.168.0.120:9292";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "c59d9756e8b044e289bb4aca6665b16f"}, {"url": "http://192.168.0.120:9292";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "ff19aeecce074395a6e3f053d53129cd"}], "type": "image", "id": "605c3d6c06424606854166792671bdb4", "name": "glance"}, {"endpoints": [{"url": "http://192.168.0.120:8776/v3/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "1abdbe731ad641b2b20d505e6180687f"}, {"url": "http://192.168.0.120:8776/v3/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "4711c98645a44653809ee1edd6ad7e6f"}, {"url": "http://192.168.0.120:8776/v3/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "d48a986e4c17401c97bbbad431e29e30"}], "type": "volumev3", "id": "73cd56899898444d9909f692221f9f18", "name": "cinderv3"}, {"endpoints": [{"url": "http://192.168.0.120:8041";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "2a654c0ff8944a9683ebc498007eeb2e"}, {"url": "http://192.168.0.120:8041";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "65e4c4754ac04447934f5010f29c6aef"}, {"url": "http://192.168.0.120:8041";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "7bc10dd694594a679a8ab75556ab46f2"}], "type": "metric", "id": "7605431b7a2b4ed296d6279d4e3b736d", "name": "gnocchi"}, {"endpoints": [{"url": "http://192.168.0.120:8080/v1/AUTH_b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "90bbfbb898c54fecb1ecb542b7252087"}, {"url": "http://192.168.0.120:8080/v1/AUTH_b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "c93c87dfe7d7497a85e240000429f3dc"}, {"url": "http://192.168.0.120:8080/v1/AUTH_b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "f92aae7ceb9642a98cbb63a2931e16a4"}], "type": "object-store", "id": "8c718458160a404eb3ddbf1f302fbc1b", "name": "swift"}, {"endpoints": [{"url": "http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "3a44a54d5f4e4c249863e1d87cb4def3"}, {"url": "http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "535998df2dac489f88ed535fd28ff93e"}, {"url": "http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "69565e2a72a542e3b291fa8ea1a8d89c"}], "type": "compute", "id": "e8d57e5cec094ab9ada6df9f53b15cf2", "name": "nova"}, {"endpoints": [{"url": "http://192.168.0.120:9696";, "interface": "public", "region": "RegionOne", "region_id": "RegionOne", "id": "1a12521b0c3b429485e401eb56e204c9"}, {"url": "http://192.168.0.120:9696";, "interface": "admin", "region": "RegionOne", "region_id": "RegionOne", "id": "2820d2534c2f44fcab59ecc20165a2f0"}, {"url": "http://192.168.0.120:9696";, "interface": "internal", "region": "RegionOne", "region_id": "RegionOne", "id": "a2a0b2633eec45d3b3d3863118b8da14"}], "type": "network", "id": "eed91bf85e1246cdbed5f10ed36b4bd0", "name": "neutron"}], "user": {"domain": {"id": "default", "name": "Default"}, "password_expires_at": null, "name": "demo", "id": "7ed4ce59626941478efb08c37cad526e"}, "audit_ids": ["7GlloCWJTRKmZhEXtrhTMA"], "issued_at": "2018-03-24T10:19:04.000000Z"}}
REQ: curl -g -i -X GET http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94 -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}a061ff6ccfe550621f348287929f8c6e64541eb8"
DEBUG (session:347) REQ: curl -g -i -X GET http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94 -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}a061ff6ccfe550621f348287929f8c6e64541eb8"
INFO (connectionpool:214) Starting new HTTP connection (1): 192.168.0.120
DEBUG (connectionpool:401) "GET /v2.1/b38c09cb0b5a4d46820c66052bb0ee94 HTTP/1.1" 404 112
RESP: [404] Content-Length: 112 Content-Type: application/json; charset=UTF-8 X-Compute-Request-Id: req-f071ad6e-a5a0-4453-9b99-529d9ebf9733 Date: Sat, 24 Mar 2018 10:19:05 GMT Connection: keep-alive
RESP BODY: {"message": "The resource could not be found.<br /><br />\n\n\n", "code": "404 Not Found", "title": "Not Found"}
DEBUG (session:395) RESP: [404] Content-Length: 112 Content-Type: application/json; charset=UTF-8 X-Compute-Request-Id: req-f071ad6e-a5a0-4453-9b99-529d9ebf9733 Date: Sat, 24 Mar 2018 10:19:05 GMT Connection: keep-alive
RESP BODY: {"message": "The resource could not be found.<br /><br />\n\n\n", "code": "404 Not Found", "title": "Not Found"}
GET call to compute for http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94 used request id req-f071ad6e-a5a0-4453-9b99-529d9ebf9733
DEBUG (session:640) GET call to compute for http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94 used request id req-f071ad6e-a5a0-4453-9b99-529d9ebf9733
REQ: curl -g -i -X GET http://192.168.0.120:8774/v2.1/ -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}a061ff6ccfe550621f348287929f8c6e64541eb8"
DEBUG (session:347) REQ: curl -g -i -X GET http://192.168.0.120:8774/v2.1/ -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}a061ff6ccfe550621f348287929f8c6e64541eb8"
DEBUG (connectionpool:401) "GET /v2.1/ HTTP/1.1" 200 387
RESP: [200] Content-Length: 387 Content-Type: application/json Openstack-Api-Version: compute 2.1 X-Openstack-Nova-Api-Version: 2.1 Vary: OpenStack-API-Version, X-OpenStack-Nova-API-Version X-Compute-Request-Id: req-e2d4e47d-f24f-4013-a06a-52a35c243b5c Date: Sat, 24 Mar 2018 10:19:05 GMT Connection: keep-alive
RESP BODY: {"version": {"status": "CURRENT", "updated": "2013-07-23T11:33:21Z", "links": [{"href": "http://192.168.0.120:8774/v2.1/";, "rel": "self"}, {"href": "http://docs.openstack.org/";, "type": "text/html", "rel": "describedby"}], "min_version": "2.1", "version": "2.42", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.compute+json;version=2.1"}], "id": "v2.1"}}
DEBUG (session:395) RESP: [200] Content-Length: 387 Content-Type: application/json Openstack-Api-Version: compute 2.1 X-Openstack-Nova-Api-Version: 2.1 Vary: OpenStack-API-Version, X-OpenStack-Nova-API-Version X-Compute-Request-Id: req-e2d4e47d-f24f-4013-a06a-52a35c243b5c Date: Sat, 24 Mar 2018 10:19:05 GMT Connection: keep-alive
RESP BODY: {"version": {"status": "CURRENT", "updated": "2013-07-23T11:33:21Z", "links": [{"href": "http://192.168.0.120:8774/v2.1/";, "rel": "self"}, {"href": "http://docs.openstack.org/";, "type": "text/html", "rel": "describedby"}], "min_version": "2.1", "version": "2.42", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.compute+json;version=2.1"}], "id": "v2.1"}}
GET call to compute for http://192.168.0.120:8774/v2.1/ used request id req-e2d4e47d-f24f-4013-a06a-52a35c243b5c
DEBUG (session:640) GET call to compute for http://192.168.0.120:8774/v2.1/ used request id req-e2d4e47d-f24f-4013-a06a-52a35c243b5c
DEBUG (extension:180) found extension EntryPoint.parse('v2token = keystoneauth1.loading._plugins.identity.v2:Token')
DEBUG (extension:180) found extension EntryPoint.parse('v3oauth1 = keystoneauth1.extras.oauth1._loading:V3OAuth1')
DEBUG (extension:180) found extension EntryPoint.parse('admin_token = keystoneauth1.loading._plugins.admin_token:AdminToken')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcauthcode = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectAuthorizationCode')
DEBUG (extension:180) found extension EntryPoint.parse('v2password = keystoneauth1.loading._plugins.identity.v2:Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3samlpassword = keystoneauth1.extras._saml2._loading:Saml2Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3password = keystoneauth1.loading._plugins.identity.v3:Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcaccesstoken = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectAccessToken')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcpassword = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectPassword')
DEBUG (extension:180) found extension EntryPoint.parse('v3kerberos = keystoneauth1.extras.kerberos._loading:Kerberos')
DEBUG (extension:180) found extension EntryPoint.parse('token = keystoneauth1.loading._plugins.identity.generic:Token')
DEBUG (extension:180) found extension EntryPoint.parse('v3oidcclientcredentials = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectClientCredentials')
DEBUG (extension:180) found extension EntryPoint.parse('v3tokenlessauth = keystoneauth1.loading._plugins.identity.v3:TokenlessAuth')
DEBUG (extension:180) found extension EntryPoint.parse('v3token = keystoneauth1.loading._plugins.identity.v3:Token')
DEBUG (extension:180) found extension EntryPoint.parse('v3totp = keystoneauth1.loading._plugins.identity.v3:TOTP')
DEBUG (extension:180) found extension EntryPoint.parse('password = keystoneauth1.loading._plugins.identity.generic:Password')
DEBUG (extension:180) found extension EntryPoint.parse('v3fedkerb = keystoneauth1.extras.kerberos._loading:MappedKerberos')
DEBUG (extension:180) found extension EntryPoint.parse('v1password = swiftclient.authv1:PasswordLoader')
DEBUG (extension:180) found extension EntryPoint.parse('token_endpoint = openstackclient.api.auth_plugin:TokenEndpoint')
DEBUG (extension:180) found extension EntryPoint.parse('gnocchi-basic = gnocchiclient.auth:GnocchiBasicLoader')
DEBUG (extension:180) found extension EntryPoint.parse('gnocchi-noauth = gnocchiclient.auth:GnocchiNoAuthLoader')
DEBUG (extension:180) found extension EntryPoint.parse('aodh-noauth = aodhclient.noauth:AodhNoAuthLoader')
DEBUG (session:347) REQ: curl -g -i -X GET http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94/servers/detail -H "OpenStack-API-Version: compute 2.41" -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-OpenStack-Nova-API-Version: 2.41" -H "X-Auth-Token: {SHA1}a061ff6ccfe550621f348287929f8c6e64541eb8"
DEBUG (connectionpool:401) "GET /v2.1/b38c09cb0b5a4d46820c66052bb0ee94/servers/detail HTTP/1.1" 200 1544
DEBUG (session:395) RESP: [200] Content-Length: 1544 Content-Type: application/json Openstack-Api-Version: compute 2.41 X-Openstack-Nova-Api-Version: 2.41 Vary: OpenStack-API-Version, X-OpenStack-Nova-API-Version X-Compute-Request-Id: req-219e6e99-6065-4ac0-a4bb-a8181059af2b Date: Sat, 24 Mar 2018 10:19:05 GMT Connection: keep-alive
RESP BODY: {"servers": [{"OS-EXT-STS:task_state": null, "addresses": {"private": [{"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:5f:26:45", "version": 4, "addr": "10.0.0.3", "OS-EXT-IPS:type": "fixed"}]}, "links": [{"href": "http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94/servers/57835459-1d60-40ae-b0c7-381bcbaba738";, "rel": "self"}, {"href": "http://192.168.0.120:8774/b38c09cb0b5a4d46820c66052bb0ee94/servers/57835459-1d60-40ae-b0c7-381bcbaba738";, "rel": "bookmark"}], "image": "", "OS-EXT-STS:vm_state": "stopped", "OS-SRV-USG:launched_at": "2018-03-18T08:48:43.000000", "flavor": {"id": "1", "links": [{"href": "http://192.168.0.120:8774/b38c09cb0b5a4d46820c66052bb0ee94/flavors/1";, "rel": "bookmark"}]}, "id": "57835459-1d60-40ae-b0c7-381bcbaba738", "security_groups": [{"name": "default"}], "OS-SRV-USG:terminated_at": null, "user_id": "7ed4ce59626941478efb08c37cad526e", "OS-DCF:diskConfig": "AUTO", "accessIPv4": "", "accessIPv6": "", "OS-EXT-STS:power_state": 4, "OS-EXT-AZ:availability_zone": "nova", "metadata": {}, "status": "SHUTOFF", "updated": "2018-03-24T10:07:23Z", "hostId": "7d96721294a7a29a9c48879e04f370eb2e7dd70a36aa0d96ca225f30", "description": "test", "tags": [], "key_name": null, "locked": false, "name": "test", "created": "2018-03-18T08:48:06Z", "tenant_id": "b38c09cb0b5a4d46820c66052bb0ee94", "os-extended-volumes:volumes_attached": [{"id": "b64fedef-d528-435f-9256-414b44aa9bc5", "delete_on_termination": false}, {"id": "7236ed4b-bf0a-423f-bc7a-bb213c0e9853", "delete_on_termination": false}], "config_drive": ""}]}
DEBUG (session:640) GET call to compute for http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94/servers/detail used request id req-219e6e99-6065-4ac0-a4bb-a8181059af2b
+--------------------------------------+------+---------+------------+-------------+------------------+
| ID                                   | Name | Status  | Task State | Power State | Networks         |
+--------------------------------------+------+---------+------------+-------------+------------------+
| 57835459-1d60-40ae-b0c7-381bcbaba738 | test | SHUTOFF | -          | Shutdown    | private=10.0.0.3 |
+--------------------------------------+------+---------+------------+-------------+------------------+
"nova list"命令发送有两个重要的HTTP请求,第一个请求发给keystone获取授权。Openstack中的各种服务都需要经过授权才能够使用,因此对Nova API的调用都要首先从keystone拿到一个授权token,然后将其填入随后API请求中"X-Auth-Token"字段。
对于上面的例子,获取token,然后基于这个授权发送第二个请求给Nova获取虚拟机列表。
向Nova API服务发送请求有多种方式,使用novaclient提供的命令只是其中的一种,我们还可以使用Dashboard提供的图形界面,比如Horizon,它本质上也基于novaclient对API的封装。此外还可以使用curl命令直接发送HTTP请求。
二 PasteDeploy 将 HTTP 请求路由到具体的 WSGI Application
首先,Openstack 在启动 nova-api service 时,会根据 Nova 配置文件 nova.conf 中的配置项 enabled_apis = ec2,osapi_compute,metadata 来创建一个或多个 WSGI Server 。
每一个 WSGI Server 负责处理一种类型的 Nova API 请求。
上述的选项值表示了三种类型的 Nova API 。
在创建 WSGI Server 的同时会根据 Paste 的配置文件 nova/etc/nova/api-paste.ini 加载 WSGI Application 。
在加载 WSGI Application 之后,WSGI Application 就处在等待和监听请求的状态。
加载的动作由nova/nova/service.py实现。
# nova/nova/service.py
class WSGIService(service.Service):"""Provides ability to launch API from a 'paste' configuration."""def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):self.name = nameself.manager = self._get_manager()self.loader = loader or wsgi.Loader()#从api-paste.ini配置文件加载 Nova API 对应的 WSGI Application,由 HTTP 请求的 URL 来决定self.app = self.loader.load_app(name)# inherit all compute_api worker counts from osapi_computeif name.startswith('openstack_compute_api'):wname = 'osapi_compute'else:wname = nameself.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")self.port = getattr(CONF, '%s_listen_port' % name, 0)self.workers = (getattr(CONF, '%s_workers' % wname, None) orprocessutils.get_worker_count())if self.workers and self.workers < 1:worker_name = '%s_workers' % namemsg = (_("%(worker_name)s value of %(workers)s is invalid, ""must be greater than 0") %{'worker_name': worker_name,'workers': str(self.workers)})raise exception.InvalidInput(msg)self.use_ssl = use_ssl#使用指定的 HostIP 和 Port 创建用于监听HTTP请求的 Socket,这个Socket只会捕获与 host 和 port 对应的 HTTP 请求。self.server = wsgi.Server(name,self.app,host=self.host,port=self.port,use_ssl=self.use_ssl,max_url_len=max_url_len)
#nova/etc/nova/api-paste.ini
############
# Metadata #
############
[composite:metadata]
use = egg:Paste#urlmap
/: meta
[pipeline:meta]
pipeline = ec2faultwrap logrequest metaapp
[app:metaapp]
paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory
#######
# EC2 #
#######
[composite:ec2]
use = egg:Paste#urlmap
/services/Cloud: ec2cloud
[composite:ec2cloud]
use = call:nova.api.auth:pipeline_factory
noauth = ec2faultwrap logrequest ec2noauth cloudrequest validator ec2executor
keystone = ec2faultwrap logrequest ec2keystoneauth cloudrequest validator ec2executor
[filter:ec2faultwrap]
paste.filter_factory = nova.api.ec2:FaultWrapper.factory
[filter:logrequest]
paste.filter_factory = nova.api.ec2:RequestLogging.factory
[filter:ec2lockout]
paste.filter_factory = nova.api.ec2:Lockout.factory
[filter:ec2keystoneauth]
paste.filter_factory = nova.api.ec2:EC2KeystoneAuth.factory
[filter:ec2noauth]
paste.filter_factory = nova.api.ec2:NoAuth.factory
[filter:cloudrequest]
controller = nova.api.ec2.cloud.CloudController
paste.filter_factory = nova.api.ec2:Requestify.factory
[filter:authorizer]
paste.filter_factory = nova.api.ec2:Authorizer.factory
[filter:validator]
paste.filter_factory = nova.api.ec2:Validator.factory
[app:ec2executor]
paste.app_factory = nova.api.ec2:Executor.factory
#############
# OpenStack #
#############
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v2
/v2: openstack_compute_api_v2
/v2.1: openstack_compute_api_v21
/v3: openstack_compute_api_v3
[composite:openstack_compute_api_v2]
use = call:nova.api.auth:pipeline_factory
noauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2
keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
[composite:openstack_compute_api_v21]
use = call:nova.api.auth:pipeline_factory_v21
noauth = request_id faultwrap sizelimit noauth osapi_compute_app_v21
keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21
[composite:openstack_compute_api_v3]
use = call:nova.api.auth:pipeline_factory_v21
noauth = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3
keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v3
[filter:request_id]
paste.filter_factory = nova.openstack.common.middleware.request_id:RequestIdMiddleware.factory
[filter:compute_req_id]
paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory
[filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
[filter:noauth]
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory
[filter:noauth_v3]
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddlewareV3.factory
[filter:ratelimit]
paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory
[filter:sizelimit]
paste.filter_factory = nova.api.sizelimit:RequestBodySizeLimiter.factory
[app:osapi_compute_app_v2]
paste.app_factory = nova.api.openstack.compute:APIRouter.factory
[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory
[app:osapi_compute_app_v3]
paste.app_factory = nova.api.openstack.compute:APIRouterV3.factory
[pipeline:oscomputeversions]
pipeline = faultwrap oscomputeversionapp
[app:oscomputeversionapp]
paste.app_factory = nova.api.openstack.compute.versions:Versions.factory
##########
# Shared #
##########
[filter:keystonecontext]
paste.filter_factory = nova.api.auth:NovaKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
api-paste.ini 配置文件定义了不同类型的 composite section。
[composite:metadata] / [composite:ec2] / [composite:osapi_compute] / [composite:openstack_compute_api_v2] /[composite:openstack_compute_api_v21]、[composite:openstack_compute_api_v3] 等。
当 Socket 监听到 HTTP 请求: http://192.168.0.120:8774/v2.1/b38c09cb0b5a4d46820c66052bb0ee94/servers/detail时,composite section 是该请求进入到 nova-api 后第一个通过的 Section。
下面是该请求通过 api-paste.ini 配置映射到一个 WSGI Application 的步骤:
第一步:因为这个请求是 OpenstackAPI 类型,所以会被 [composite:osapi_compute] 监听到,并使用 nova.api.openstack.urlmap 模块下的 urlmap_factory 函数将请求分发到 [composite:openstack_compute_api_v21] 。
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v21_legacy_v2_compatible
/v2: openstack_compute_api_v21_legacy_v2_compatible
/v2.1: openstack_compute_api_v21
第二步:[composite:openstack_compute_api_v21] 再使用 nova.api.auth模块下的 pipeline_factory_v21 函数对请求进一步分发。
而且这里需要根据 nova.conf 配置文件中的认证策略选项 auth_strategy 来确定使用那一种认证方式 (keystone/noauth),在 nova/api/auth.py 文件定义了默认为认证方式为 keystone 。
[composite:openstack_compute_api_v21]
use = call:nova.api.auth:pipeline_factory_v21
noauth = request_id faultwrap sizelimit noauth osapi_compute_app_v21
keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21
第三步:keystone/noauth 这两个 Pipeline 的参数列表中除了最后一个参数之外的都是 filter section,最后一个参数对应了一个 app section( Application) 。
等这个 pipeline 里所有的 filter 都执行完了之后,最终再调用 application osapi_compute_app_v21 。
[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory
所以说,将 HTTP 请求路由到哪一个 WSGI Application 是由配置文件 api-paste.ini 来决定的。
而且需要注意的是:Openstack Project 拥有着各自不同的 Paste 配置文件,即拥有各自不同的 WSGI Serever 和 WSGI Application 。
三 WSGI Application到具体的执行函数
在 Openstack 中,每个资源都被封装成为一个 nova.api.openstack.wsgi.Resource 对象,且对应着一个 WSGI Application ,这样就保证了资源的独立性。
上面,我们已经确定了处理 HTTP 请求的 Application ,
除此之外,我们还需要继续的去确定这个 HTTP 请求希望执行 Application 中具体的哪一个操作函数。
这需要 Routes Module 的 Mapper 对象来支撑。
Routes Module:主要用于把接收到的 HTTP URL 请求的后缀 Path(/servers/detail)映射到具体的操作函数。
其实现原理是创建一个 mapper 对象,然后调用该对象的 connect() 方法把后缀 Path 和 HTTP 内建方法映射到一个 Controller 的某个 Action 上。
Controller 是一个自定义的类实例对象,每一个Openstack 资源都会对应一个 Controller,同时 Controller 也是资源操作函数的集合。
Action 表示 Controller 对象提供的操作函数(index/show/delect/update/create)。
一般调用这些 Action 之前会把这个 Action 映射到 HTTP 的内置方法。GET/POST/DELETE/PUT 。
HTTP 请求的 URL Path 和 HTTP 内置方法能够确定对 Openstack 中唯一的一个资源执行何种操作。
class Router(object):"""WSGI middleware that maps incoming requests to WSGI apps."""def __init__(self, mapper):"""Create a router for the given routes.Mapper.Each route in `mapper` must specify a 'controller', which is aWSGI app to call. You'll probably want to specify an 'action' aswell and have your controller be an object that can routethe request to the action-specific method.Examples:mapper = routes.Mapper()sc = ServerController()# Explicit mapping of one route to a controller+actionmapper.connect(None, '/svrlist', controller=sc, action='list')# Actions are all implicitly definedmapper.resource('server', 'servers', controller=sc)# Pointing to an arbitrary WSGI app. You can specify the# {path_info:.*} parameter so the target app can be handed just that# section of the URL.mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp())"""self.map = mapper# 使用 routers 模块将 mapper 和 _dispatch 关联起来# routes.middleware.RoutesMiddleware 会调用mapper.routematch()函数#来获取url的controller等参数,保存在match中,并设置environ变量供_dispatch()使用#environ['wsgiorg.routing_args']=((url),match)#environ['routes.url']=urlself._router = routes.middleware.RoutesMiddleware(self._dispatch,self.map)@webob.dec.wsgify(RequestClass=Request)def __call__(self, req):"""Route the incoming request to a controller based on self.map.If no match, return a 404."""# 根据 mapper 将HTTP请求路由到适当的 WSGI Application,即资源上。再根据 HTTP 请求的 URL 将其路由到对应的 Controller 中的 Action。return self._router@staticmethod@webob.dec.wsgify(RequestClass=Request)def _dispatch(req):"""Dispatch the request to the appropriate controller.Called by self._router after matching the incoming request to a routeand putting the information into req.environ. Either returns 404or the routed WSGI app's response."""# 根据 HTTP 请求的 environ 信息并根据上述设置的 environ 来找到 URL 对应的 Controller 。match = req.environ['wsgiorg.routing_args'][1]if not match:return webob.exc.HTTPNotFound()app = match['controller']return app
通过 Route 模块,一个 HTTP 请求 的 URL 能够映射到对应的资源的 Action(对资源操作的方法) 。

四 参考
http://blog.csdn.net/shenshanmiaoke/article/details/52809133
https://www.2cto.com/net/201608/534264.html

Nova API的执行过程相关推荐

  1. openstack nova 源码解析 — Nova API 执行过程从(novaclient到Action)

    目录 目录 Nova API Nova API 的执行过程 novaclient 将 Commands 转换为标准的HTTP请求 PasteDeploy 将 HTTP 请求路由到具体的 WSGI Ap ...

  2. ASP.NET Web API 过滤器创建、执行过程(二)

    ASP.NET Web API 过滤器创建.执行过程(二) 前言 前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器 ...

  3. Web APi之过滤器执行过程原理解析【二】(十一)

    前言 上一节我们详细讲解了过滤器的创建过程以及粗略的介绍了五种过滤器,用此五种过滤器对实现对执行Action方法各个时期的拦截非常重要.这一节我们简单将讲述在Action方法上.控制器上.全局上以及授 ...

  4. [nova]nova api执行过程分析

    新手,水平较低,很多地方还不是很理解,错漏在所难免,后续会慢慢完善的. 我总结了一下,想要通过读懂源代码来学习一个新技术,大概可以分为三个步骤走: 1.理解相关的类库的功能及使用. 2.理清代码中对象 ...

  5. 操作系统原理,系统调用,系统调用与库函数API等函数之间的调用关系,功能与机制设计,系统调用的执行过程与Linux系统调用执行示例,不同操作系统下的PCB

    操作系统原理,系统调用,功能与机制设计,系统调用的执行过程与Linux系统调用执行示例,不同操作系统下的PCB 一.系统调用:操作系统功能调用,用户在编程时可以调用的操作系统功能. 1.系统调用是操作 ...

  6. nova api 分析

    0. 总览 nova的服务在功能上主要分为应用层和逻辑层两类,nova-api就是应用层的服务,而nova-scheduler.nova-conductor等则是逻辑层服务.本文主要通过注释的方式讲述 ...

  7. Nova API简单介绍

    Nova API是访问并使用Nova所提供的各种服务的唯一途径,作为客户端和Nova之间的中间层,Nova API扮演了一个桥梁,或者说中间人的角色,Nova API把客户端的请求传达给Nova,待N ...

  8. saiku执行过程代码跟踪

    使用了很久的saiku,决定跟踪一下代码,看看它的执行核心过程: 一.入口controller代码 1.1.页面打开之后,会发送一个ajax请求 Request URL: http://l-tdata ...

  9. mysql查询解析过程_MySQL查询执行过程详解

    查询是用户通过设置某些查询条件,从表或其他查询中选取全部或者部分数据,以表的形式显示数据供用户浏览.查询是一个独立的.功能强大的.具有计算功能和条件检索功能的数据库对象.MySQL数据库中,MySQL ...

最新文章

  1. SQL查询四舍五入 解决方法
  2. 配置nginx下别名alias支持PHP fastcgi解析
  3. UVA10020(最小区间覆盖)
  4. Linq之隐式类型、自动属性、初始化器、匿名类
  5. 字符集和编码规范:ASCII,Unicode和UTF-8, latin1,BIG5,GBK
  6. css资源网站收集推荐
  7. python 常量 模块_Python字符串模块的有用常量
  8. 前端:CSS/14/综合案例:传智首页
  9. oracle数据库无法写入文件,如何在ORACLE的PL/SQL中将数据写入文件
  10. JVM第一讲:为什么需要 JVM?它处在什么位置?
  11. Maven Install报错:Perhaps you are running on a JRE rather than a JDK
  12. 排列组合 “n个球放入m个盒子“里,再来一遍
  13. (十二)JAVA springboot微服务b2b2c电子商务系统:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪...
  14. Fl Studio20切换中文教程汉化补丁包
  15. winform之控件在Panel中居中
  16. python的sysfont_pygame.font.SysFont游戏文字交互
  17. Allegro 小知识总结
  18. 视频教程-Unity网络游戏架构设计-Unity3D
  19. linux 防火墙的配置
  20. linux课程思政方案,课程建设

热门文章

  1. 红包还能这么玩?60行代码教你模拟群发手气红包
  2. react 修改循环数组对象的数据
  3. 基于 ModelBox 实现 AI 应用快速开发:AI 养猪,实时看护猪的健康
  4. mysql dml回滚_数据库回滚与DDL、DML
  5. pdf拆分怎么做?这几款软件帮你一键拆分
  6. 使用Python批量修改文件名后缀
  7. 实验五:Romberg算法
  8. 对话微众银行马智涛:数据可携带权对金融意味着什么
  9. 5G来了,但4G手机和iPad却无法享受5G,怎么办?
  10. mysql给用户赋予权限