Skip to content

Commit 43dea49

Browse files
authored
Merge pull request #144 from caphrim007/feature.add-debug-array-and-curl-collections
Adds debug tracing of curl commands
2 parents 0cfcf5a + f8c6ab5 commit 43dea49

5 files changed

Lines changed: 113 additions & 85 deletions

File tree

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ deploy:
4141
on:
4242
repo: F5Networks/f5-icontrol-rest-python
4343
tags: true
44+
python: 2.7
4445
- provider: pypi
4546
user: $PYPI_USER
4647
password: $PYPI_PASSWORD

icontrol/session.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,17 @@
6666
from icontrol.exceptions import InvalidScheme
6767
from icontrol.exceptions import InvalidSuffixCollection
6868
from icontrol.exceptions import InvalidURIComponentPart
69+
from six import iteritems
6970

7071
import functools
7172
import logging
7273
import requests
7374

75+
try:
76+
import json
77+
except ImportError:
78+
import simplejson as json
79+
7480
try:
7581
# Python 3
7682
from urllib.parse import urlsplit
@@ -369,6 +375,10 @@ def __init__(self, username, password, **kwargs):
369375
On BIG-IQ systems, the value 'local' can be used to refer to
370376
local user authentication.
371377
"""
378+
379+
# Used for holding debug information
380+
self._debug = []
381+
372382
verify = kwargs.pop('verify', False)
373383
timeout = kwargs.pop('timeout', 30)
374384
token_auth = kwargs.pop('token', None)
@@ -437,7 +447,10 @@ def delete(self, uri, **kwargs):
437447
:type partition: str
438448
:param \**kwargs: The :meth:`reqeusts.Session.delete` optional params
439449
"""
440-
return self.session.delete(uri, **kwargs)
450+
req = requests.Request('DELETE', uri, **kwargs)
451+
prepared = self.session.prepare_request(req)
452+
self._debug.append(debug_prepared_request(prepared))
453+
return self.session.send(prepared)
441454

442455
@decorate_HTTP_verb_method
443456
def get(self, uri, **kwargs):
@@ -459,7 +472,10 @@ def get(self, uri, **kwargs):
459472
:type partition: str
460473
:param \**kwargs: The :meth:`reqeusts.Session.get` optional params
461474
"""
462-
return self.session.get(uri, **kwargs)
475+
req = requests.Request('GET', uri, **kwargs)
476+
prepared = self.session.prepare_request(req)
477+
self._debug.append(debug_prepared_request(prepared))
478+
return self.session.send(prepared)
463479

464480
@decorate_HTTP_verb_method
465481
def patch(self, uri, data=None, **kwargs):
@@ -483,7 +499,10 @@ def patch(self, uri, data=None, **kwargs):
483499
:type partition: str
484500
:param \**kwargs: The :meth:`reqeusts.Session.patch` optional params
485501
"""
486-
return self.session.patch(uri, data=data, **kwargs)
502+
req = requests.Request('PATCH', uri, data=data, **kwargs)
503+
prepared = self.session.prepare_request(req)
504+
self._debug.append(debug_prepared_request(prepared))
505+
return self.session.send(prepared)
487506

488507
@decorate_HTTP_verb_method
489508
def post(self, uri, data=None, json=None, **kwargs):
@@ -509,7 +528,10 @@ def post(self, uri, data=None, json=None, **kwargs):
509528
:type partition: str
510529
:param \**kwargs: The :meth:`reqeusts.Session.post` optional params
511530
"""
512-
return self.session.post(uri, data=data, json=json, **kwargs)
531+
req = requests.Request('POST', uri, data=data, json=json, **kwargs)
532+
prepared = self.session.prepare_request(req)
533+
self._debug.append(debug_prepared_request(prepared))
534+
return self.session.send(prepared)
513535

514536
@decorate_HTTP_verb_method
515537
def put(self, uri, data=None, **kwargs):
@@ -535,7 +557,10 @@ def put(self, uri, data=None, **kwargs):
535557
:type partition: str
536558
:param **kwargs: The :meth:`reqeusts.Session.put` optional params
537559
"""
538-
return self.session.put(uri, data=data, **kwargs)
560+
req = requests.Request('PUT', uri, data=data, **kwargs)
561+
prepared = self.session.prepare_request(req)
562+
self._debug.append(debug_prepared_request(prepared))
563+
return self.session.send(prepared)
539564

540565
def append_user_agent(self, user_agent):
541566
"""Append text to the User-Agent header for the request.
@@ -568,3 +593,14 @@ def token(self, value):
568593
been read from a stored value for example.
569594
"""
570595
self.session.auth.token = value
596+
597+
598+
def debug_prepared_request(request):
599+
return
600+
result = "curl -k -X {0} {1}".format(request.method.upper(), request.url)
601+
for k, v in iteritems(request.headers):
602+
result = result + " -H '{0}: {1}'".format(k, v)
603+
if request.body:
604+
kwargs = json.loads(request.body)
605+
result = result + " -d '" + json.dumps(kwargs, sort_keys=True) + "'"
606+
return result

icontrol/test/unit/test_session.py

Lines changed: 66 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import mock
1616
import pytest
17+
import requests
1718

1819
from icontrol import __version__ as VERSION
1920
from icontrol import session
@@ -27,11 +28,7 @@ def iCRS():
2728
fake_iCRS.session = mock.MagicMock()
2829
mock_response = mock.MagicMock()
2930
mock_response.status_code = 200
30-
fake_iCRS.session.delete.return_value = mock_response
31-
fake_iCRS.session.get.return_value = mock_response
32-
fake_iCRS.session.patch.return_value = mock_response
33-
fake_iCRS.session.post.return_value = mock_response
34-
fake_iCRS.session.put.return_value = mock_response
31+
fake_iCRS.session.send.return_value = mock_response
3532
return fake_iCRS
3633

3734

@@ -380,119 +377,109 @@ def test_correct_uri_construction_mgmt_cm(uparts_cm):
380377

381378
# Test exception handling
382379
def test_wrapped_delete_success(iCRS, uparts):
383-
iCRS.delete(uparts['base_uri'], partition='AFN', name='AIN',
384-
uri_as_parts=True)
385-
assert iCRS.session.delete.call_args ==\
386-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN')
380+
iCRS.delete(uparts['base_uri'], partition='AFN', name='AIN', uri_as_parts=True)
381+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
382+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
387383

388384

389385
def test_wrapped_delete_207_fail(iCRS, uparts):
390-
iCRS.session.delete.return_value.status_code = 207
391-
with pytest.raises(session.iControlUnexpectedHTTPError) as CHE:
392-
iCRS.delete(uparts['base_uri'], partition='A_FOLDER_NAME',
393-
name='AN_INSTANCE_NAME')
394-
assert str(CHE.value).startswith('207 Unexpected Error: ')
386+
iCRS.session.send.return_value.status_code = 207
387+
with pytest.raises(session.iControlUnexpectedHTTPError) as ex:
388+
iCRS.delete(uparts['base_uri'], partition='A_FOLDER_NAME', name='AN_INSTANCE_NAME')
389+
assert str(ex.value).startswith('207 Unexpected Error: ')
395390

396391

397392
def test_wrapped_get_success(iCRS, uparts):
398-
iCRS.get(uparts['base_uri'], partition='AFN', name='AIN',
399-
uri_as_parts=True)
400-
assert iCRS.session.get.call_args ==\
401-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN')
393+
iCRS.get(uparts['base_uri'], partition='AFN', name='AIN', uri_as_parts=True)
394+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
395+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
402396

403397

404398
def test_wrapped_get_success_with_suffix(iCRS, uparts):
405-
iCRS.get(uparts['base_uri'], partition='AFN', name='AIN',
406-
suffix=uparts['suffix'],
407-
uri_as_parts=True)
408-
assert iCRS.session.get.call_args ==\
409-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN/members/m1')
399+
iCRS.get(uparts['base_uri'], partition='AFN', name='AIN', suffix=uparts['suffix'], uri_as_parts=True)
400+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
401+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN/members/m1'
410402

411403

412404
def test_wrapped_get_207_fail(iCRS, uparts):
413-
iCRS.session.get.return_value.status_code = 207
414-
with pytest.raises(session.iControlUnexpectedHTTPError) as CHE:
415-
iCRS.get(uparts['base_uri'], partition='A_FOLDER_NAME',
416-
name='AN_INSTANCE_NAME')
417-
assert str(CHE.value).startswith('207 Unexpected Error: ')
405+
iCRS.session.send.return_value.status_code = 207
406+
with pytest.raises(session.iControlUnexpectedHTTPError) as ex:
407+
iCRS.get(uparts['base_uri'], partition='A_FOLDER_NAME', name='AN_INSTANCE_NAME')
408+
assert str(ex.value).startswith('207 Unexpected Error: ')
418409

419410

420411
def test_wrapped_patch_success(iCRS, uparts):
421-
iCRS.patch(uparts['base_uri'], partition='AFN', name='AIN',
422-
uri_as_parts=True)
423-
assert iCRS.session.patch.call_args ==\
424-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN', data=None)
412+
iCRS.patch(uparts['base_uri'], partition='AFN', name='AIN', uri_as_parts=True)
413+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
414+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
415+
assert iCRS.session.prepare_request.call_args[0][0].data == []
425416

426417

427418
def test_wrapped_patch_207_fail(iCRS, uparts):
428-
iCRS.session.patch.return_value.status_code = 207
429-
with pytest.raises(session.iControlUnexpectedHTTPError) as CHE:
430-
iCRS.patch(uparts['base_uri'], partition='A_FOLDER_NAME',
431-
name='AN_INSTANCE_NAME')
432-
assert str(CHE.value).startswith('207 Unexpected Error: ')
419+
iCRS.session.send.return_value.status_code = 207
420+
with pytest.raises(session.iControlUnexpectedHTTPError) as ex:
421+
iCRS.patch(uparts['base_uri'], partition='A_FOLDER_NAME', name='AN_INSTANCE_NAME')
422+
assert str(ex.value).startswith('207 Unexpected Error: ')
433423

434424

435425
def test_wrapped_put_207_fail(iCRS, uparts):
436-
iCRS.session.put.return_value.status_code = 207
437-
with pytest.raises(session.iControlUnexpectedHTTPError) as CHE:
438-
iCRS.put(uparts['base_uri'], partition='A_FOLDER_NAME',
439-
name='AN_INSTANCE_NAME')
440-
assert str(CHE.value).startswith('207 Unexpected Error: ')
426+
iCRS.session.send.return_value.status_code = 207
427+
with pytest.raises(session.iControlUnexpectedHTTPError) as ex:
428+
iCRS.put(uparts['base_uri'], partition='A_FOLDER_NAME', name='AN_INSTANCE_NAME')
429+
assert str(ex.value).startswith('207 Unexpected Error: ')
441430

442431

443432
def test_wrapped_post_207_fail(iCRS, uparts):
444-
iCRS.session.post.return_value.status_code = 207
445-
with pytest.raises(session.iControlUnexpectedHTTPError) as CHE:
446-
iCRS.post(uparts['base_uri'], partition='A_FOLDER_NAME',
447-
name='AN_INSTANCE_NAME')
448-
assert str(CHE.value).startswith('207 Unexpected Error: ')
433+
iCRS.session.send.return_value.status_code = 207
434+
with pytest.raises(session.iControlUnexpectedHTTPError) as ex:
435+
iCRS.post(uparts['base_uri'], partition='A_FOLDER_NAME', name='AN_INSTANCE_NAME')
436+
assert str(ex.value).startswith('207 Unexpected Error: ')
449437

450438

451439
def test_wrapped_post_success(iCRS, uparts):
452-
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN',
453-
uri_as_parts=True)
454-
assert iCRS.session.post.call_args ==\
455-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN', data=None,
456-
json=None)
440+
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', uri_as_parts=True)
441+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
442+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
443+
assert iCRS.session.prepare_request.call_args[0][0].data == []
444+
assert iCRS.session.prepare_request.call_args[0][0].json is None
457445

458446

459447
def test_wrapped_post_success_with_data(iCRS, uparts):
460-
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', data={'a': 1},
461-
uri_as_parts=True)
462-
assert iCRS.session.post.call_args ==\
463-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN',
464-
data={'a': 1}, json=None)
448+
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', data={'a': 1}, uri_as_parts=True)
449+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
450+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
451+
assert iCRS.session.prepare_request.call_args[0][0].data == {'a': 1}
452+
assert iCRS.session.prepare_request.call_args[0][0].json is None
465453

466454

467455
def test_wrapped_post_success_with_json(iCRS, uparts):
468-
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', json='{"a": 1}',
469-
uri_as_parts=True)
470-
assert iCRS.session.post.call_args ==\
471-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN', data=None,
472-
json='{"a": 1}')
456+
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', json='{"a": 1}', uri_as_parts=True)
457+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
458+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
459+
assert iCRS.session.prepare_request.call_args[0][0].data == []
460+
assert iCRS.session.prepare_request.call_args[0][0].json == '{"a": 1}'
473461

474462

475463
def test_wrapped_post_success_with_json_and_data(iCRS, uparts):
476-
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', data={'a': 1},
477-
json='{"a": 1}', uri_as_parts=True)
478-
assert iCRS.session.post.call_args ==\
479-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN',
480-
data={'a': 1}, json='{"a": 1}')
464+
iCRS.post(uparts['base_uri'], partition='AFN', name='AIN', data={'a': 1}, json='{"a": 1}', uri_as_parts=True)
465+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
466+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
467+
assert iCRS.session.prepare_request.call_args[0][0].data == {'a': 1}
468+
assert iCRS.session.prepare_request.call_args[0][0].json == '{"a": 1}'
481469

482470

483471
def test_wrapped_put_success(iCRS, uparts):
484-
iCRS.put(uparts['base_uri'], partition='AFN', name='AIN',
485-
uri_as_parts=True)
486-
assert iCRS.session.put.call_args ==\
487-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN', data=None)
472+
iCRS.put(uparts['base_uri'], partition='AFN', name='AIN', uri_as_parts=True)
473+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
474+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
475+
assert iCRS.session.prepare_request.call_args[0][0].data == []
488476

489477

490478
def test_wrapped_put_success_with_data(iCRS, uparts):
491-
iCRS.put(uparts['base_uri'], partition='AFN', name='AIN', data={'b': 2},
492-
uri_as_parts=True)
493-
assert iCRS.session.put.call_args ==\
494-
mock.call('https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN',
495-
data={'b': 2})
479+
iCRS.put(uparts['base_uri'], partition='AFN', name='AIN', data={'b': 2}, uri_as_parts=True)
480+
assert isinstance(iCRS.session.prepare_request.call_args[0][0], requests.Request)
481+
assert iCRS.session.prepare_request.call_args[0][0].url == 'https://0.0.0.0/mgmt/tm/root/RESTiface/~AFN~AIN'
482+
assert iCRS.session.prepare_request.call_args[0][0].data == {'b': 2}
496483

497484

498485
def test___init__user_agent():
@@ -531,7 +518,8 @@ def test__init__without_verify():
531518

532519

533520
def test__init__with_verify():
534-
icrs = session.iControlRESTSession('test_name', 'test_pw',
535-
token=True, verify='/path/to/cert')
521+
icrs = session.iControlRESTSession(
522+
'test_name', 'test_pw', token=True, verify='/path/to/cert'
523+
)
536524
assert icrs.session.verify is '/path/to/cert'
537525
assert icrs.session.auth.verify is '/path/to/cert'

requirements.test.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ git+https://github.com/F5Networks/pytest-symbols.git
1313
python-coveralls==2.7.0
1414
pyOpenSSL==16.2.0
1515
requests-mock==1.1.0
16-
tox
16+
tox
17+
six

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
[bdist_rpm]
2-
requires = python-requests >= 2.5.0
2+
requires = six>=1.9.0
3+
six<2.0.0
4+
python-requests >= 2.5.0

0 commit comments

Comments
 (0)