Skip to content

Commit f28e64b

Browse files
author
Thomas
committed
Improved handling exceptions.
1 parent e939aff commit f28e64b

File tree

2 files changed

+73
-72
lines changed

2 files changed

+73
-72
lines changed

netbox/connection.py

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import requests
2-
import socket
32
from netbox import exceptions
3+
import json
44

55

66
class NetboxConnection(object):
@@ -29,14 +29,14 @@ def __init__(self, ssl_verify=False, use_ssl=True, host=None, auth_token=None, a
2929
self.session.headers.update({'Content-Type': 'application/json'})
3030

3131
if auth and auth_token:
32-
raise ValueError('Only one authentication method is possible. Please use auth or auth_token')
32+
raise exceptions.AuthException('Only one authentication method is possible. Please use auth or auth_token')
3333

3434
if extra_headers:
3535
self.session.headers.update(extra_headers)
3636

3737
def __request(self, method, params=None, key=None, body=None, url=None):
3838

39-
if method is not 'GET':
39+
if method != 'GET':
4040
if not self.auth_token:
4141
raise exceptions.AuthException('Authentication credentials were not provided')
4242

@@ -54,25 +54,25 @@ def __request(self, method, params=None, key=None, body=None, url=None):
5454

5555
try:
5656
response = self.session.send(prepared_request)
57-
except socket.gaierror:
58-
err_msg = 'Unable to find address: {}'.format(self.host)
59-
raise socket.gaierror(err_msg)
6057
except requests.exceptions.ConnectionError:
6158
err_msg = 'Unable to connect to Netbox host: {}'.format(self.host)
62-
raise ConnectionError(err_msg)
59+
raise ConnectionError(err_msg) from None
6360
except requests.exceptions.Timeout:
64-
raise TimeoutError('Connection to Netbox host timed out')
61+
raise TimeoutError('Connection to Netbox host timed out') from None
6562
except Exception as e:
6663
raise Exception(e)
6764
finally:
6865
self.close()
6966

67+
if not 200 <= response.status_code < 300:
68+
self.__raise_error(response.status_code, response.content)
69+
7070
try:
7171
response_data = response.json()
72-
except:
73-
response_data = response.content
72+
except json.JSONDecodeError:
73+
raise exceptions.ServerException(response.content) from None
7474

75-
return response.ok, response.status_code, response_data
75+
return response_data
7676

7777
def get(self, param, key=None, limit=0, **kwargs):
7878

@@ -87,15 +87,9 @@ def get(self, param, key=None, limit=0, **kwargs):
8787
else:
8888
url = '{}{}?limit={}'.format(self.base_url, param, limit)
8989

90-
resp_ok, resp_status, resp_data = self.__request('GET', params=param, key=key, url=url)
90+
resp_data = self.__request('GET', params=param, key=key, url=url)
9191

92-
if resp_ok and resp_status == 200:
93-
if 'results' in resp_data:
94-
return resp_data['results']
95-
else:
96-
return resp_data
97-
else:
98-
return []
92+
return resp_data['results']
9993

10094
def put(self, params):
10195

@@ -104,15 +98,9 @@ def put(self, params):
10498
def patch(self, params, key, **kwargs):
10599

106100
body_data = {key: value for (key, value) in kwargs.items()}
107-
resp_ok, resp_status, resp_data = self.__request('PATCH', params=params, key=key, body=body_data)
108-
109-
if resp_ok and resp_status == 200:
110-
return resp_data
111-
112-
if resp_status == 404:
113-
raise exceptions.NotFoundException("object not found with id {}".format(key), from_con=True)
101+
resp_data = self.__request('PATCH', params=params, key=key, body=body_data)
114102

115-
raise exceptions.UpdateException(resp_data)
103+
return resp_data
116104

117105
def post(self, params, required_fields, **kwargs):
118106

@@ -121,24 +109,33 @@ def post(self, params, required_fields, **kwargs):
121109
if kwargs:
122110
body_data.update({key: value for (key, value) in kwargs.items()})
123111

124-
resp_ok, resp_status, resp_data = self.__request('POST', params=params, body=body_data)
125-
if resp_ok and resp_status == 201:
126-
return resp_data
127-
else:
128-
raise exceptions.CreateException(resp_data)
112+
resp_data = self.__request('POST', params=params, body=body_data)
113+
114+
return resp_data
129115

130116
def delete(self, params, del_id):
131117

132118
del_str = '{}{}'.format(params, del_id)
133-
resp_ok, resp_status, resp_data = self.__request('DELETE', del_str)
134-
if resp_ok and resp_status == 204:
135-
return True
136-
137-
if resp_status == 404:
138-
raise exceptions.NotFoundException("Unable to found object with id {}".format(del_id), from_con=True)
119+
self.__request('DELETE', del_str)
139120

140-
raise exceptions.DeleteException(resp_data)
121+
return True
141122

142123
def close(self):
143124

144125
self.session.close()
126+
127+
def __raise_error(self, http_status_code, http_response):
128+
"""Raise error with detailed information from http request."""
129+
try:
130+
error_msg = json.loads(http_response)
131+
except json.JSONDecodeError:
132+
error_msg = http_response
133+
134+
if http_status_code == 404:
135+
raise exceptions.NotFoundException(error_msg)
136+
elif http_status_code == 403:
137+
raise exceptions.AuthorizationException(error_msg)
138+
elif http_status_code == 400:
139+
raise exceptions.ClientException(error_msg)
140+
elif http_status_code == 503:
141+
raise exceptions.ServerException(error_msg)

netbox/exceptions.py

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,62 @@
1-
class BaseExceptions(Exception):
2-
pass
3-
4-
5-
class DeleteException(BaseException):
6-
"""Raised when delete failed"""
1+
class GeneralException(BaseException):
72
def __init__(self, resp_data):
8-
93
if isinstance(resp_data, dict):
10-
self.err = ''.join('Failed with reason {}'.format(val) for key, val in resp_data.items())
4+
if 'detail' in resp_data:
5+
self.err = resp_data['detail']
6+
else:
7+
self.err = ''.join('{} '.format(val[0]) for key, val in resp_data.items())
118
else:
12-
self.err = 'Delete failed with an unknown reason'
9+
self.err = 'Unknown Error, please check the Netbox logs'
1310

1411
def __str__(self):
1512
return '{}'.format(self.err)
1613

1714

18-
class NotFoundException(BaseException):
15+
class DeleteException(GeneralException):
16+
"""Raised when delete failed"""
17+
def __init__(self, resp_data):
18+
super().__init__(resp_data)
19+
20+
21+
class NotFoundException(GeneralException):
1922
"""Raised when item is not found"""
20-
def __init__(self, msg, from_con=False):
21-
self.msg = msg
22-
self.from_con = from_con
23+
pass
2324

24-
def __str__(self):
25-
if self.from_con:
26-
return self.msg
2725

28-
return 'Unable to found {}'.format(self.msg)
26+
class GetException(GeneralException):
27+
pass
2928

3029

31-
class CreateException(BaseException):
30+
class CreateException(GeneralException):
3231
"""Raised when creation failed"""
3332
def __init__(self, resp_data):
34-
35-
if isinstance(resp_data, dict):
36-
self.err = ''.join('{} '.format(val[0]) for key, val in resp_data.items())
37-
else:
38-
self.err = 'Creation failed with unknown reason'
39-
40-
def __str__(self):
41-
return '{}'.format(self.err)
33+
super().__init__(resp_data)
4234

4335

4436
class UpdateException(BaseException):
4537
"""Raised when an object update fails"""
4638
def __init__(self, resp_data):
47-
if isinstance(resp_data, dict):
48-
self.err = ''.join('{} '.format(val[0]) for key, val in resp_data.items())
49-
else:
50-
self.err = 'Update failed with unknown reason'
51-
52-
def __str__(self):
53-
return '{}'.format(self.err)
39+
super().__init__(resp_data)
5440

5541

5642
class AuthException(BaseException):
5743
"""Raised when an API call method is not allowed"""
5844
pass
45+
46+
47+
class AuthorizationException(GeneralException):
48+
"""HTTP 403 status code"""
49+
def __init__(self, resp_data):
50+
super().__init__(resp_data)
51+
52+
53+
class ClientException(GeneralException):
54+
"""HTTP 400 status code"""
55+
def __init__(self, resp_data):
56+
super().__init__(resp_data)
57+
58+
59+
class ServerException(GeneralException):
60+
"""HTTP 5xx status code"""
61+
def __init__(self, resp_data):
62+
super().__init__(resp_data)

0 commit comments

Comments
 (0)