Skip to content

Commit 8aa32fd

Browse files
authored
Merge pull request #26 from dariomi/master
Release R1.7 with updated dependencies
2 parents 1631592 + a1ef5f6 commit 8aa32fd

File tree

8 files changed

+439
-287
lines changed

8 files changed

+439
-287
lines changed

.github/workflows/python-app.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on: ubuntu-latest
1616
strategy:
1717
matrix:
18-
python-version: ["3.8","3.9", "3.10", "3.11", "3.12"]
18+
python-version: ["3.10", "3.11", "3.12", "3.13"]
1919

2020
steps:
2121
- uses: actions/checkout@v4

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
> * v1.00, 11.2020 -- Adjustments for OpenSource - Andreas Graber
44
> * v1.3, 06.2021 -- Snapshot creation added - Dario Kaelin
55
> * v1.5, 01.2023 -- Dependency Update
6-
> * v1.6, 06.2024 -- Improvement for subscription - Dario Kaelin
6+
> * v1.6, 06.2024 -- Improvement for subscription - Dario Kaelin
7+
> * v1.7, 12.2025 -- Dependency Update - Dario Kaelin

aciClient/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
from aciClient.aci import ACI
22
from aciClient.aciCertClient import ACICert
33

4-
__all__ = [
5-
'ACI',
6-
'ACICert'
7-
]
4+
__all__ = ["ACI", "ACICert"]

aciClient/aci.py

Lines changed: 100 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ class ACI:
3232
# constructor
3333
# ==============================================================================
3434
def __init__(self, apicIp, apicUser, apicPasword, refresh=False, proxies=None):
35-
self.__logger.debug('Constructor called')
35+
self.__logger.debug("Constructor called")
3636
self.apicIp = apicIp
3737
self.apicUser = apicUser
3838
self.apicPassword = apicPasword
3939
self.proxies = proxies
4040

41-
self.baseUrl = 'https://' + self.apicIp + '/api/'
42-
self.__logger.debug(f'BaseUrl set to: {self.baseUrl}')
41+
self.baseUrl = "https://" + self.apicIp + "/api/"
42+
self.__logger.debug(f"BaseUrl set to: {self.baseUrl}")
4343

4444
self.refresh_auto = refresh
4545
self.refresh_next = None
@@ -52,17 +52,27 @@ def __init__(self, apicIp, apicUser, apicPasword, refresh=False, proxies=None):
5252
self.retry_backoff_factor = 10 # in seconds; multiplied by previous attempts.
5353

5454
def __refresh_session_timer(self, response):
55-
self.__logger.debug(f'refreshing the token {self.refresh_offset}s before it expires')
56-
self.refresh_next = int(response.json()['imdata'][0]['aaaLogin']['attributes']['refreshTimeoutSeconds'])
57-
self.refresh_thread = threading.Timer(self.refresh_next - self.refresh_offset, self.renewCookie)
58-
self.__logger.debug(f'starting thread to refresh token in {self.refresh_next - self.refresh_offset}s')
55+
self.__logger.debug(
56+
f"refreshing the token {self.refresh_offset}s before it expires"
57+
)
58+
self.refresh_next = int(
59+
response.json()["imdata"][0]["aaaLogin"]["attributes"][
60+
"refreshTimeoutSeconds"
61+
]
62+
)
63+
self.refresh_thread = threading.Timer(
64+
self.refresh_next - self.refresh_offset, self.renewCookie
65+
)
66+
self.__logger.debug(
67+
f"starting thread to refresh token in {self.refresh_next - self.refresh_offset}s"
68+
)
5969
self.refresh_thread.start()
6070

6171
# ==============================================================================
6272
# login
6373
# ==============================================================================
6474
def login(self) -> bool:
65-
self.__logger.debug('login called')
75+
self.__logger.debug("login called")
6676

6777
retry_strategy = urllib3.Retry(
6878
total=self.total_retry_attempts,
@@ -72,30 +82,38 @@ def login(self) -> bool:
7282
adapter = HTTPAdapter(max_retries=retry_strategy)
7383

7484
self.session = requests.Session()
75-
self.session.mount('http://', adapter)
76-
self.session.mount('https://', adapter)
77-
self.__logger.debug('Session Object Created')
85+
self.session.mount("http://", adapter)
86+
self.session.mount("https://", adapter)
87+
self.__logger.debug("Session Object Created")
7888

7989
if self.proxies is not None:
8090
self.session.proxies = self.proxies
8191

8292
# create credentials structure
83-
userPass = json.dumps({'aaaUser': {'attributes': {'name': self.apicUser, 'pwd': self.apicPassword}}})
93+
userPass = json.dumps(
94+
{
95+
"aaaUser": {
96+
"attributes": {"name": self.apicUser, "pwd": self.apicPassword}
97+
}
98+
}
99+
)
84100

85-
self.__logger.info(f'Login to apic {self.baseUrl}')
86-
response = self.session.post(self.baseUrl + 'aaaLogin.json', data=userPass, verify=False, timeout=5)
101+
self.__logger.info(f"Login to apic {self.baseUrl}")
102+
response = self.session.post(
103+
self.baseUrl + "aaaLogin.json", data=userPass, verify=False, timeout=5
104+
)
87105

88106
# Don't raise an exception for 401
89107
if response.status_code == 401:
90-
self.__logger.error(f'Login not possible due to Error: {response.text}')
108+
self.__logger.error(f"Login not possible due to Error: {response.text}")
91109
self.session = False
92110
return False
93111

94112
# Raise a exception for all other 4xx and 5xx status_codes
95113
response.raise_for_status()
96114

97-
self.token = response.json()['imdata'][0]['aaaLogin']['attributes']['token']
98-
self.__logger.debug('Successful get Token from APIC')
115+
self.token = response.json()["imdata"][0]["aaaLogin"]["attributes"]["token"]
116+
self.__logger.debug("Successful get Token from APIC")
99117

100118
if self.refresh_auto:
101119
self.__refresh_session_timer(response=response)
@@ -105,31 +123,34 @@ def login(self) -> bool:
105123
# logout
106124
# ==============================================================================
107125
def logout(self):
108-
self.__logger.debug('logout called')
126+
self.__logger.debug("logout called")
109127
self.refresh_auto = False
110128
if self.refresh_thread is not None:
111129
if self.refresh_thread.is_alive():
112-
self.__logger.debug('Stoping refresh_auto thread')
130+
self.__logger.debug("Stoping refresh_auto thread")
113131
self.refresh_thread.cancel()
114-
self.postJson(jsonData={'aaaUser': {'attributes': {'name': self.apicUser}}}, url='aaaLogout.json')
115-
self.__logger.debug('Logout from APIC sucessfull')
132+
self.postJson(
133+
jsonData={"aaaUser": {"attributes": {"name": self.apicUser}}},
134+
url="aaaLogout.json",
135+
)
136+
self.__logger.debug("Logout from APIC sucessfull")
116137

117138
# ==============================================================================
118139
# renew cookie (aaaRefresh)
119140
# ==============================================================================
120141
def renewCookie(self) -> bool:
121-
self.__logger.debug('Renew Cookie called')
122-
response = self.session.post(self.baseUrl + 'aaaRefresh.json', verify=False)
142+
self.__logger.debug("Renew Cookie called")
143+
response = self.session.post(self.baseUrl + "aaaRefresh.json", verify=False)
123144

124145
if response.status_code == 200:
125146
if self.refresh_auto:
126147
self.__refresh_session_timer(response=response)
127-
self.token = response.json()['imdata'][0]['aaaLogin']['attributes']['token']
128-
self.__logger.debug('Successfuly renewed the token')
148+
self.token = response.json()["imdata"][0]["aaaLogin"]["attributes"]["token"]
149+
self.__logger.debug("Successfuly renewed the token")
129150
else:
130151
self.token = False
131152
self.refresh_auto = False
132-
self.__logger.error(f'Could not renew token. {response.text}')
153+
self.__logger.error(f"Could not renew token. {response.text}")
133154
response.raise_for_status()
134155
return False
135156
return True
@@ -138,101 +159,118 @@ def renewCookie(self) -> bool:
138159
# getToken
139160
# ==============================================================================
140161
def getToken(self) -> str:
141-
self.__logger.debug('Get Token called')
162+
self.__logger.debug("Get Token called")
142163
return self.token
143164

144165
# ==============================================================================
145166
# getJson
146167
# ==============================================================================
147168
def getJson(self, uri, subscription=False) -> {}:
148169
url = self.baseUrl + uri
149-
self.__logger.debug(f'Get Json called url: {url}')
170+
self.__logger.debug(f"Get Json called url: {url}")
150171

151172
if subscription:
152-
url = '{}?subscription=yes'.format(url)
173+
url = "{}?subscription=yes".format(url)
153174
response = self.session.get(url, verify=False)
154175

155176
if response.ok:
156177
responseJson = response.json()
157-
self.__logger.debug(f'Successful get Data from APIC: {responseJson}')
178+
self.__logger.debug(f"Successful get Data from APIC: {responseJson}")
158179
if subscription:
159-
subscription_id = responseJson['subscriptionId']
160-
self.__logger.debug(f'Returning Subscription Id: {subscription_id}')
180+
subscription_id = responseJson["subscriptionId"]
181+
self.__logger.debug(f"Returning Subscription Id: {subscription_id}")
161182
return subscription_id
162-
return responseJson['imdata']
183+
return responseJson["imdata"]
163184

164185
elif response.status_code == 400:
165-
resp_text = response.json()['imdata'][0]['error']['attributes']['text']
166-
self.__logger.error(f'Error 400 during get occured: {resp_text}')
167-
if resp_text == 'Unable to process the query, result dataset is too big':
186+
resp_text = response.json()["imdata"][0]["error"]["attributes"]["text"]
187+
self.__logger.error(f"Error 400 during get occured: {resp_text}")
188+
if resp_text == "Unable to process the query, result dataset is too big":
168189
# Dataset was too big, we try to grab all the data with pagination
169-
self.__logger.debug(f'Trying with Pagination, uri: {uri}')
190+
self.__logger.debug(f"Trying with Pagination, uri: {uri}")
170191
return self.getJsonPaged(uri)
171192
return resp_text
172193
else:
173-
self.__logger.error(f'Error during get occured: {response.json()}')
194+
self.__logger.error(f"Error during get occured: {response.json()}")
174195
return response.json()
175196

176197
# ==============================================================================
177198
# getJson with Pagination
178199
# ==============================================================================
179200
def getJsonPaged(self, uri) -> {}:
180201
url = self.baseUrl + uri
181-
self.__logger.debug(f'Get Json Pagination called url: {url}')
202+
self.__logger.debug(f"Get Json Pagination called url: {url}")
182203
parsed_url = urlparse(url)
183204
parsed_query = parse_qsl(parsed_url.query)
184205

185206
return_data = []
186207
page = 0
187208

188209
while True:
189-
parsed_query.extend([('page', page), ('page-size', '50000')])
210+
parsed_query.extend([("page", page), ("page-size", "50000")])
190211
page += 1
191-
url_to_call = urlunparse((parsed_url[0], parsed_url[1], parsed_url[2], parsed_url[3],
192-
urlencode(parsed_query), parsed_url[5]))
212+
url_to_call = urlunparse(
213+
(
214+
parsed_url[0],
215+
parsed_url[1],
216+
parsed_url[2],
217+
parsed_url[3],
218+
urlencode(parsed_query),
219+
parsed_url[5],
220+
)
221+
)
193222
response = self.session.get(url_to_call, verify=False)
194223

195224
if response.ok:
196225
responseJson = response.json()
197-
self.__logger.debug(f'Successful get Data from APIC: {responseJson}')
198-
if responseJson['imdata']:
199-
return_data.extend(responseJson['imdata'])
226+
self.__logger.debug(f"Successful get Data from APIC: {responseJson}")
227+
if responseJson["imdata"]:
228+
return_data.extend(responseJson["imdata"])
200229
else:
201230
return return_data
202231

203232
elif response.status_code == 400:
204-
resp_text = '400: ' + response.json()['imdata'][0]['error']['attributes']['text']
205-
self.__logger.error(f'Error 400 during get occured: {resp_text}')
233+
resp_text = (
234+
"400: "
235+
+ response.json()["imdata"][0]["error"]["attributes"]["text"]
236+
)
237+
self.__logger.error(f"Error 400 during get occured: {resp_text}")
206238
return resp_text
207239

208240
else:
209-
self.__logger.error(f'Error during get occured: {response.json()}')
241+
self.__logger.error(f"Error during get occured: {response.json()}")
210242
return False
211243

212244
# ==============================================================================
213245
# postJson
214246
# ==============================================================================
215-
def postJson(self, jsonData, url='mo.json') -> {}:
216-
self.__logger.debug(f'Post Json called data: {jsonData}')
217-
response = self.session.post(self.baseUrl + url, verify=False, data=json.dumps(jsonData, sort_keys=True))
247+
def postJson(self, jsonData, url="mo.json") -> {}:
248+
self.__logger.debug(f"Post Json called data: {jsonData}")
249+
response = self.session.post(
250+
self.baseUrl + url, verify=False, data=json.dumps(jsonData, sort_keys=True)
251+
)
218252
if response.status_code == 200:
219-
self.__logger.debug(f'Successful Posted Data to APIC: {response.json()}')
253+
self.__logger.debug(f"Successful Posted Data to APIC: {response.json()}")
220254
return response.status_code
221255
elif response.status_code == 400:
222-
resp_text = '400: ' + response.json()['imdata'][0]['error']['attributes']['text']
223-
self.__logger.error(f'Error 400 during get occured: {resp_text}')
256+
resp_text = (
257+
"400: " + response.json()["imdata"][0]["error"]["attributes"]["text"]
258+
)
259+
self.__logger.error(f"Error 400 during get occured: {resp_text}")
224260
return resp_text
225261
else:
226-
self.__logger.error(f'Error during get occured: {response.json()}')
262+
self.__logger.error(f"Error during get occured: {response.json()}")
227263
response.raise_for_status()
228264
return response.status_code
229265

230266
# ==============================================================================
231267
# deleteMo
232268
# ==============================================================================
233269
def deleteMo(self, dn) -> int:
234-
self.__logger.debug(f'Delete Mo called DN: {dn}')
235-
response = self.session.delete(self.baseUrl + "mo/" + dn + ".json", verify=False)
270+
self.__logger.debug(f"Delete Mo called DN: {dn}")
271+
response = self.session.delete(
272+
self.baseUrl + "mo/" + dn + ".json", verify=False
273+
)
236274

237275
# Raise Exception if http Error occurred
238276
response.raise_for_status()
@@ -243,7 +281,7 @@ def deleteMo(self, dn) -> int:
243281
# snapshot
244282
# ==============================================================================
245283
def snapshot(self, description="snapshot", target_dn="") -> bool:
246-
self.__logger.debug(f'snapshot called {description}')
284+
self.__logger.debug(f"snapshot called {description}")
247285

248286
json_payload = [
249287
{
@@ -258,21 +296,21 @@ def snapshot(self, description="snapshot", target_dn="") -> bool:
258296
"name": "aciclient",
259297
"nameAlias": "",
260298
"snapshot": "yes",
261-
"targetDn": f"{target_dn}"
299+
"targetDn": f"{target_dn}",
262300
}
263301
}
264302
}
265303
]
266304

267305
response = self.postJson(json_payload)
268306
if response == 200:
269-
self.__logger.debug('snapshot created and triggered')
307+
self.__logger.debug("snapshot created and triggered")
270308
return True
271309
else:
272-
self.__logger.error(f'snapshot creation not succesfull: {response}')
310+
self.__logger.error(f"snapshot creation not succesfull: {response}")
273311
return False
274312

275-
# ==============================================================================
313+
# ==============================================================================
276314
# subscribe
277315
# ==============================================================================
278316
def subscribe(

0 commit comments

Comments
 (0)