Merge "Add Betamax for testing"

This commit is contained in:
Jenkins 2017-03-24 15:10:13 +00:00 committed by Gerrit Code Review
commit 37cc3e95af
15 changed files with 8678 additions and 6 deletions

View File

@ -270,13 +270,23 @@ _5xx_classes = [
_5xx_codes = {cls.status_code: cls for cls in _5xx_classes}
def error_from(response):
"""Find an error code that matches a response status_code."""
if 400 <= response.status_code < 500:
cls = _4xx_codes.get(response.status_code, HTTPClientError)
elif 500 <= response.status_code < 600:
cls = _5xx_codes.get(response.status_code, HTTPServerError)
def _error_class_from(status_code):
if 400 <= status_code < 500:
cls = _4xx_codes.get(status_code, HTTPClientError)
elif 500 <= status_code < 600:
cls = _5xx_codes.get(status_code, HTTPServerError)
else:
cls = HTTPError
return cls
def error_from(response):
"""Find an error code that matches a response status_code."""
cls = _error_class_from(response.status_code)
return cls(response=response)
def raise_from(exception):
"""Raise an exception from the keystoneauth1 exception."""
cls = _error_class_from(exception.http_status)
return cls(response=exception.response, exception=exception)

View File

@ -15,6 +15,7 @@
from itertools import chain
import logging
from keystoneauth1 import exceptions as ksa_exc
from keystoneauth1 import session as ksa_session
from requests import exceptions as requests_exc
@ -228,6 +229,8 @@ class Session(object):
raise exc.Timeout(exception=err)
except requests_exc.ConnectionError as err:
raise exc.ConnectionFailed(exception=err)
except ksa_exc.HttpError as err:
raise exc.raise_from(err)
if response.status_code >= 400:
raise exc.error_from(response)

View File

@ -0,0 +1,19 @@
================================
cratonclient Betamax Cassettes
================================
This directory contains the cassettes that were recorded by Betamax_ for
integration level tests of the python-cratonclient library.
For more information about these cassettes, please refer to the Betamax
documentation_. For specific information about what information is stored in a
cassette and its structure, please read `"What is a cassette?"`_
.. links
.. _Betamax:
https://pypi.org/project/betamax
.. _documentation:
https://betamax.readthedocs.io/en/latest/
.. _"What is a cassette?":
https://betamax.readthedocs.io/en/latest/cassettes.html

View File

@ -0,0 +1,201 @@
http_interactions:
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"name\": \"cloud-TestHosts-test_create\"\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '39'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/clouds
response:
body:
encoding: null
string: "{\n \"variables\": {},\n \"id\": 6,\n \"name\": \"cloud-TestHosts-test_create\"\
,\n \"note\": null,\n \"created_at\": \"2017-03-20T23:40:58.854217\",\n\
\ \"updated_at\": null,\n \"project_id\": \"<craton-demo-project>\"\n}"
headers:
Content-Length: '214'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:58 GMT
Location: <craton-url>/clouds/6
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-1993267c-c04e-482d-a2f7-5a3199f45fe3
status:
code: 201
message: CREATED
url: <craton-url>/clouds
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"name\": \"region-TestHosts-test_create\",\n \"cloud_id\": 6\n\
}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '55'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/regions
response:
body:
encoding: null
string: "{\n \"variables\": {},\n \"id\": 2,\n \"name\": \"region-TestHosts-test_create\"\
,\n \"note\": null,\n \"created_at\": \"2017-03-20T23:40:58.893218\",\n\
\ \"updated_at\": null,\n \"cloud_id\": 6,\n \"project_id\": \"<craton-demo-project>\"\
\n}"
headers:
Content-Length: '232'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:58 GMT
Location: <craton-url>/regions/2
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-aa293966-07d6-4832-b294-68d277e7266c
status:
code: 201
message: CREATED
url: <craton-url>/regions
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"region_id\": 2,\n \"device_type\": \"server\",\n \"name\"\
: \"host-0\",\n \"cloud_id\": 6,\n \"ip_address\": \"127.0.1.0\"\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '101'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/hosts
response:
body:
encoding: null
string: "{\n \"id\": 2,\n \"ip_address\": \"127.0.1.0\",\n \"region_id\"\
: 2,\n \"active\": true,\n \"cloud_id\": 6,\n \"project_id\": \"<craton-demo-project>\"\
,\n \"variables\": {},\n \"parent_id\": null,\n \"updated_at\": null,\n\
\ \"name\": \"host-0\",\n \"cell_id\": null,\n \"device_type\": \"server\"\
,\n \"note\": null,\n \"links\": [\n {\n \"href\": \"<craton-url>/regions/2\"\
,\n \"rel\": \"up\"\n }\n ],\n \"created_at\": \"2017-03-20T23:40:58.952662\"\
\n}"
headers:
Content-Length: '442'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:58 GMT
Location: <craton-url>/hosts/2
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-98391187-c6e9-4002-936c-ba3b93ee4081
status:
code: 201
message: CREATED
url: <craton-url>/hosts
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/hosts/2
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-949fad55-ce5e-47fb-8277-13d6f88286a2
status:
code: 204
message: NO CONTENT
url: <craton-url>/hosts/2
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/regions/2
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-dbed3d00-6002-493f-84d0-40b572f500f1
status:
code: 204
message: NO CONTENT
url: <craton-url>/regions/2
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/clouds/6
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-f70acbe0-739f-4bba-9f56-707032a3211d
status:
code: 204
message: NO CONTENT
url: <craton-url>/clouds/6
recorded_with: betamax/0.8.0

View File

@ -0,0 +1,231 @@
http_interactions:
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"name\": \"cloud-TestHosts-test_delete\"\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '39'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/clouds
response:
body:
encoding: null
string: "{\n \"variables\": {},\n \"id\": 10,\n \"name\": \"cloud-TestHosts-test_delete\"\
,\n \"note\": null,\n \"created_at\": \"2017-03-20T23:40:59.563744\",\n\
\ \"updated_at\": null,\n \"project_id\": \"<craton-demo-project>\"\n}"
headers:
Content-Length: '215'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Location: <craton-url>/clouds/10
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-8c61f8a2-83bb-466e-8cdc-b24b38e7de74
status:
code: 201
message: CREATED
url: <craton-url>/clouds
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"name\": \"region-TestHosts-test_delete\",\n \"cloud_id\": 10\n\
}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '56'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/regions
response:
body:
encoding: null
string: "{\n \"variables\": {},\n \"id\": 6,\n \"name\": \"region-TestHosts-test_delete\"\
,\n \"note\": null,\n \"created_at\": \"2017-03-20T23:40:59.702986\",\n\
\ \"updated_at\": null,\n \"cloud_id\": 10,\n \"project_id\": \"<craton-demo-project>\"\
\n}"
headers:
Content-Length: '233'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Location: <craton-url>/regions/6
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-6f6bf38e-9cb4-48ac-b915-e0fec77c5d98
status:
code: 201
message: CREATED
url: <craton-url>/regions
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: "{\n \"region_id\": 6,\n \"device_type\": \"server\",\n \"name\"\
: \"host-to-delete\",\n \"cloud_id\": 10,\n \"ip_address\": \"127.0.1.0\"\
\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '110'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/hosts
response:
body:
encoding: null
string: "{\n \"id\": 7,\n \"ip_address\": \"127.0.1.0\",\n \"region_id\"\
: 6,\n \"active\": true,\n \"cloud_id\": 10,\n \"project_id\": \"<craton-demo-project>\"\
,\n \"variables\": {},\n \"parent_id\": null,\n \"updated_at\": null,\n\
\ \"name\": \"host-to-delete\",\n \"cell_id\": null,\n \"device_type\"\
: \"server\",\n \"note\": null,\n \"links\": [\n {\n \"href\": \"\
<craton-url>/regions/6\",\n \"rel\": \"up\"\n }\n ],\n \"created_at\"\
: \"2017-03-20T23:40:59.817351\"\n}"
headers:
Content-Length: '451'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Location: <craton-url>/hosts/7
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-0a63c2bf-492e-4bc0-a49a-438bc81b7656
status:
code: 201
message: CREATED
url: <craton-url>/hosts
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/hosts/7
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-269e2521-d1d5-4312-a415-b2e8b7a91e6e
status:
code: 204
message: NO CONTENT
url: <craton-url>/hosts/7
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: GET
uri: <craton-url>/hosts/7
response:
body:
encoding: utf-8
string: '{"status": 404, "message": "Not Found"}'
headers:
Content-Length: '46'
Content-Type: text/html; charset=utf-8
Date: Mon, 20 Mar 2017 23:41:00 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-3ae97e93-0cca-4e97-afad-3ac4312d19b1
status:
code: 404
message: NOT FOUND
url: <craton-url>/hosts/7
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/regions/6
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:41:00 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-dc103d8f-ddc7-45a9-a675-2a20518b2a68
status:
code: 204
message: NO CONTENT
url: <craton-url>/regions/6
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/clouds/10
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:41:00 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-332fa9fc-e12f-46e9-922b-6ec1ee9bc2b3
status:
code: 204
message: NO CONTENT
url: <craton-url>/clouds/10
recorded_with: betamax/0.8.0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,239 @@
http_interactions:
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"name\": \"cloud-TestHosts-test_update\"\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '39'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/clouds
response:
body:
encoding: null
string: "{\n \"variables\": {},\n \"id\": 9,\n \"name\": \"cloud-TestHosts-test_update\"\
,\n \"note\": null,\n \"created_at\": \"2017-03-20T23:40:59.516122\",\n\
\ \"updated_at\": null,\n \"project_id\": \"<craton-demo-project>\"\n}"
headers:
Content-Length: '214'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Location: <craton-url>/clouds/9
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-a1f5018e-9b49-4346-8f74-efee8339b882
status:
code: 201
message: CREATED
url: <craton-url>/clouds
- recorded_at: '2017-03-21T14:50:37'
request:
body:
encoding: utf-8
string: "{\n \"name\": \"region-TestHosts-test_update\",\n \"cloud_id\": 9\n\
}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '55'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/regions
response:
body:
encoding: null
string: "{\n \"variables\": {},\n \"id\": 5,\n \"name\": \"region-TestHosts-test_update\"\
,\n \"note\": null,\n \"created_at\": \"2017-03-20T23:40:59.653669\",\n\
\ \"updated_at\": null,\n \"cloud_id\": 9,\n \"project_id\": \"<craton-demo-project>\"\
\n}"
headers:
Content-Length: '232'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Location: <craton-url>/regions/5
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-c89e94bc-aa5a-4622-9d53-b39dfd35af5b
status:
code: 201
message: CREATED
url: <craton-url>/regions
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: "{\n \"region_id\": 5,\n \"device_type\": \"server\",\n \"name\"\
: \"host-0\",\n \"cloud_id\": 9,\n \"ip_address\": \"127.0.1.0\"\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '101'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: POST
uri: <craton-url>/hosts
response:
body:
encoding: null
string: "{\n \"id\": 5,\n \"ip_address\": \"127.0.1.0\",\n \"region_id\"\
: 5,\n \"active\": true,\n \"cloud_id\": 9,\n \"project_id\": \"<craton-demo-project>\"\
,\n \"variables\": {},\n \"parent_id\": null,\n \"updated_at\": null,\n\
\ \"name\": \"host-0\",\n \"cell_id\": null,\n \"device_type\": \"server\"\
,\n \"note\": null,\n \"links\": [\n {\n \"href\": \"<craton-url>/regions/5\"\
,\n \"rel\": \"up\"\n }\n ],\n \"created_at\": \"2017-03-20T23:40:59.766218\"\
\n}"
headers:
Content-Length: '442'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Location: <craton-url>/hosts/5
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-2f98aecc-8f92-4ff4-9837-1b455cd36f86
status:
code: 201
message: CREATED
url: <craton-url>/hosts
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: "{\n \"note\": \"This is an updated note\",\n \"ip_address\": \"127.0.1.1\"\
\n}"
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '62'
Content-Type: application/json
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: PUT
uri: <craton-url>/hosts/5
response:
body:
encoding: null
string: "{\n \"id\": 5,\n \"ip_address\": \"127.0.1.1\",\n \"region_id\"\
: 5,\n \"active\": true,\n \"cloud_id\": 9,\n \"project_id\": \"<craton-demo-project>\"\
,\n \"parent_id\": null,\n \"updated_at\": \"2017-03-20T23:40:59.877079\"\
,\n \"name\": \"host-0\",\n \"cell_id\": null,\n \"device_type\": \"server\"\
,\n \"note\": \"This is an updated note\",\n \"links\": [\n {\n \
\ \"href\": \"<craton-url>/regions/5\",\n \"rel\": \"up\"\n }\n ],\n\
\ \"created_at\": \"2017-03-20T23:40:59.000000\"\n}"
headers:
Content-Length: '468'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-38ce18bc-7de7-436f-a6a4-944b906effe3
status:
code: 200
message: OK
url: <craton-url>/hosts/5
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/hosts/5
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:40:59 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-e2c4967f-aca1-4065-8e66-42efb79e6754
status:
code: 204
message: NO CONTENT
url: <craton-url>/hosts/5
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/regions/5
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:41:00 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-6bca4f72-9c55-4bf8-affd-25492391be45
status:
code: 204
message: NO CONTENT
url: <craton-url>/regions/5
- recorded_at: '2017-03-21T14:50:38'
request:
body:
encoding: utf-8
string: ''
headers:
Accept: '*/*'
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: '0'
User-Agent: python-cratonclient/0.0.1
X-Auth-Project: <craton-demo-project>
X-Auth-Token: <craton-demo-token>
X-Auth-User: <craton-demo-username>
method: DELETE
uri: <craton-url>/clouds/9
response:
body:
encoding: null
string: ''
headers:
Content-Length: '0'
Content-Type: application/json
Date: Mon, 20 Mar 2017 23:41:00 GMT
Server: WSGIServer/0.2 CPython/3.5.2
x-openstack-request-id: req-de348783-f7ce-488b-a1b1-28ee8b7f3f37
status:
code: 204
message: NO CONTENT
url: <craton-url>/clouds/9
recorded_with: betamax/0.8.0

View File

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Module containing the base logic for cratonclient integration tests."""
import os
import betamax
from betamax_matchers import json_body
from keystoneauth1.fixture import keystoneauth_betamax as ksabetamax
from cratonclient import auth
from cratonclient.tests import base
from cratonclient.v1 import client
# NOTE(sigmavirus24): This allows us to use ``'json-body'`` as a matcher below
betamax.Betamax.register_request_matcher(json_body.JSONBodyMatcher)
envget = os.environ.get
CRATON_DEMO_USERNAME = envget('CRATON_DEMO_USERNAME', 'demo')
CRATON_DEMO_TOKEN = envget('CRATON_DEMO_TOKEN', 'demo')
CRATON_DEMO_PROJECT = envget('CRATON_DEMO_PROJECT',
'b9f10eca66ac4c279c139d01e65f96b5')
CRATON_ROOT_USERNAME = envget('CRATON_ROOT_USERNAME', 'root')
CRATON_ROOT_TOKEN = envget('CRATON_ROOT_TOKEN', 'root')
CRATON_ROOT_PROJECT = envget('CRATON_ROOT_PROJECT',
'b9f10eca66ac4c279c139d01e65f96b5')
CRATON_URL = envget('CRATON_URL', 'http://127.0.0.1:8080/v1')
class BetamaxTestCase(base.TestCase):
"""This sets up Betamax with Keystoneauth1 fixture for integration tests.
This relies on existing keystoneauth1 integration with the Betamax library
to make recording integration tests easier.
"""
CASSETTE_LIBRARY_DIR = 'cratonclient/tests/cassettes/'
def generate_cassette_name(self):
"""Generate a cassette name for the current test."""
full_test_name = self.id()
module, test_class, test_method = full_test_name.rsplit('.', 2)
return test_class + '-' + test_method
def setUp(self):
"""Set up betamax fixture for cratonclient."""
super(BetamaxTestCase, self).setUp()
self.cassette_name = self.generate_cassette_name()
self.record_mode = envget('BETAMAX_RECORD_MODE', 'once')
self.url = CRATON_URL
self.betamax_fixture = self.useFixture(ksabetamax.BetamaxFixture(
cassette_name=self.cassette_name,
cassette_library_dir=self.CASSETTE_LIBRARY_DIR,
record=self.record_mode,
))
self.demo_credentials = {
'username': CRATON_DEMO_USERNAME,
'token': CRATON_DEMO_TOKEN,
'project': CRATON_DEMO_PROJECT,
}
self.root_credentials = {
'username': CRATON_ROOT_USERNAME,
'token': CRATON_ROOT_TOKEN,
'project': CRATON_ROOT_PROJECT,
}
def create_client(self, username, token, project):
"""Create a Craton client using Craton Auth."""
self.session = auth.craton_auth(
username=username,
token=token,
project_id=project,
)
self.client = client.Client(self.session, self.url)
def create_demo_client(self):
"""Set up cratonclient with the demo user."""
self.create_client(**self.demo_credentials)
with betamax.Betamax.configure() as config:
config.define_cassette_placeholder(
'<craton-demo-username>', CRATON_DEMO_USERNAME,
)
config.define_cassette_placeholder(
'<craton-demo-token>', CRATON_DEMO_TOKEN,
)
config.define_cassette_placeholder(
'<craton-demo-project>', CRATON_DEMO_PROJECT,
)
config.define_cassette_placeholder(
'<craton-root-username>', CRATON_ROOT_USERNAME,
)
config.define_cassette_placeholder(
'<craton-root-token>', CRATON_ROOT_TOKEN,
)
config.define_cassette_placeholder(
'<craton-root-project>', CRATON_ROOT_PROJECT,
)
config.define_cassette_placeholder(
'<craton-url>', CRATON_URL,
)

View File

@ -0,0 +1 @@
"""Integration tests for Python API client for v1 API."""

View File

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""The integration tests for the cratonclient.v1.devices."""
from cratonclient.tests.integration import base
class TestDevices(base.BetamaxTestCase):
"""DevicesManager integration tests."""
def cleanupCloud(self, cloud):
"""Add a cleanup task for this cloud."""
self.addCleanup(self.client.clouds.delete, cloud.id)
return cloud
def cleanupRegion(self, region):
"""Add a cleanup task for this region."""
self.addCleanup(self.client.regions.delete, region.id)
return region
def cleanupCell(self, cell):
"""Add a cleanup task for this cell."""
self.addCleanup(self.client.cells.delete, cell.id)
return cell
def cleanupHost(self, host):
"""Add a cleanup task for this host."""
self.addCleanup(self.client.hosts.delete, host.id)
return host
def setUp(self):
"""Set up our demo user client."""
super(TestDevices, self).setUp()
self.create_demo_client()
test_name = self.cassette_name.split('-', 1)[-1]
self.cloud = self.cleanupCloud(self.client.clouds.create(
name='cloud_{}'.format(test_name),
))
self.region = self.cleanupRegion(self.client.regions.create(
name='region_{}'.format(test_name),
cloud_id=self.cloud.id,
))
self.cells = [
self.cleanupCell(self.client.cells.create(
name='cell_{}_{}'.format(test_name, i),
region_id=self.region.id,
cloud_id=self.cloud.id,
))
for i in range(4)
]
self.hosts = [
self.cleanupHost(self.client.hosts.create(
name='host_{}_{}'.format(test_name, i),
cell_id=self.cells[i % 4].id,
region_id=self.region.id,
cloud_id=self.cloud.id,
device_type='server',
ip_address='127.0.1.{}'.format(i),
))
for i in range(35)
]
# NOTE(sigmavirus24): The API does not presently support
# /v1/network-devices
# self.network_devices = [
# self.cleanupNetworkDevice(self.client.network_devices.create(
# ))
# for i in range(35)
# ]

View File

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Module containing the cratonclient.v1.hosts integration tests."""
from cratonclient import exceptions
from cratonclient.tests.integration import base
class TestHosts(base.BetamaxTestCase):
"""HostsManager integration tests."""
def setUp(self):
"""Prepare our hosts test case."""
super(TestHosts, self).setUp()
self.create_demo_client()
self.cloud = self.client.clouds.create(
name='cloud-{}'.format(self.cassette_name),
)
self.addCleanup(self.client.clouds.delete, self.cloud.id)
self.region = self.client.regions.create(
name='region-{}'.format(self.cassette_name),
cloud_id=self.cloud.id,
)
self.addCleanup(self.client.regions.delete, self.region.id)
def cleanupHost(self, host):
"""Add a cleanup task for this host."""
self.addCleanup(self.client.hosts.delete, host.id)
return host
def test_create(self):
"""Test creation of hosts via the Python API."""
host = self.cleanupHost(self.client.hosts.create(
name='host-0',
ip_address='127.0.1.0',
device_type='server',
region_id=self.region.id,
cloud_id=self.cloud.id,
))
self.assertEqual('host-0', host.name)
def test_delete(self):
"""Test deletion of a host via the Python API."""
host = self.client.hosts.create(
name='host-to-delete',
ip_address='127.0.1.0',
device_type='server',
region_id=self.region.id,
cloud_id=self.cloud.id,
)
self.assertTrue(self.client.hosts.delete(host.id))
self.assertRaises(exceptions.NotFound, self.client.hosts.get,
host.id)
def test_list_autopaginates(self):
"""Verify listing of hosts via the Python API."""
for i in range(0, 62):
self.cleanupHost(self.client.hosts.create(
name='host-{}'.format(i),
ip_address='127.0.1.{}'.format(i),
device_type='server',
region_id=self.region.id,
cloud_id=self.cloud.id,
))
hosts = list(self.client.hosts.list())
self.assertEqual(62, len(hosts))
def test_list_only_retrieves_first_page(self):
"""Verify the behaviour of not auto-paginating listing."""
for i in range(0, 32):
self.cleanupHost(self.client.hosts.create(
name='host-{}'.format(i),
ip_address='127.0.1.{}'.format(i),
device_type='server',
region_id=self.region.id,
cloud_id=self.cloud.id,
))
hosts = list(self.client.hosts.list(autopaginate=False))
self.assertEqual(30, len(hosts))
def test_update(self):
"""Verify the ability to update a host."""
host = self.cleanupHost(self.client.hosts.create(
name='host-0',
ip_address='127.0.1.0',
device_type='server',
region_id=self.region.id,
cloud_id=self.cloud.id,
))
self.assertTrue(host.active)
updated_host = self.client.hosts.update(
item_id=host.id,
note='This is an updated note',
ip_address='127.0.1.1',
)
self.assertEqual('host-0', updated_host.name)
self.assertEqual(host.id, updated_host.id)
self.assertEqual('This is an updated note', updated_host.note)

View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Unit tests for cratonclient.exceptions."""
from cratonclient import exceptions as exc
from cratonclient.tests import base
import mock
class TestExceptions(base.TestCase):
"""Tests for our exception handling convenience functions."""
client_error_statuses = [
400, 401, 403, 404, 405, 406, 407, 409, 410, 411, 412, 413, 414, 415,
416, 422,
]
server_error_statuses = [
500,
]
def mock_keystoneauth_exception_from(self, status_code):
"""Create a fake keystoneauth1 exception with a response attribute."""
exception = mock.Mock()
exception.response = self.mock_response_from(status_code)
exception.http_status = status_code
return exception
def mock_response_from(self, status_code):
"""Create a mock requests.Response object."""
response = mock.Mock()
response.status_code = status_code
return response
def test_error_from_4xx(self):
"""Verify error_from's behvaiour for 4xx status codes."""
for status in self.client_error_statuses:
response = self.mock_response_from(status)
self.assertIsInstance(exc.error_from(response),
exc.HTTPClientError)
def test_error_from_5xx(self):
"""Verify error_from's behvaiour for 5xx status codes."""
for status in self.server_error_statuses:
response = self.mock_response_from(status)
self.assertIsInstance(exc.error_from(response),
exc.HTTPServerError)
def test_raise_from(self):
"""Verify raise_from handles keystoneauth1 exceptions."""
for status in (self.client_error_statuses +
self.server_error_statuses):
ksaexception = self.mock_keystoneauth_exception_from(status)
exception = exc.raise_from(ksaexception)
self.assertIs(ksaexception, exception.original_exception)

View File

@ -2,6 +2,8 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
betamax>=0.7.0 # Apache-2.0
betamax-matchers>=0.4.0 # Apache-2.0
doc8 # Apache-2.0
hacking<0.12,>=0.10.0
flake8-docstrings==0.2.1.post1 # MIT

View File

@ -10,6 +10,7 @@ setenv =
VIRTUAL_ENV={envdir}
BRANCH_NAME=master
CLIENT_NAME=python-cratonclient
passenv = BETAMAX_* CRATON_*
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt