@@ -20,10 +20,16 @@ class ManagerNet(object):
20
20
namespace = 'apic-management'
21
21
username = ''
22
22
password = ''
23
+ cm_username = ''
24
+ cm_password = ''
23
25
hostname = ''
24
- client_id = "caa87d9a-8cd7-4686-8b6e-ee2cdc5ee267"
25
- client_secret = "3ecff363-7eb3-44be-9e07-6d4386c48b0b"
26
+ default_client_id = "caa87d9a-8cd7-4686-8b6e-ee2cdc5ee267"
27
+ default_client_secret = "3ecff363-7eb3-44be-9e07-6d4386c48b0b"
28
+ client_id = default_client_id
29
+ client_secret = default_client_secret
26
30
grant_type = 'password'
31
+ cm_token = None # Cloud manager access token
32
+ cm_token_expires = 0 # Cloud manager access token expiration
27
33
token = None
28
34
token_expires = 0
29
35
max_frequency = 600
@@ -43,11 +49,13 @@ def __init__(self, config, trawler):
43
49
self .namespace = config .get ('namespace' , 'default' )
44
50
# Maximum frequency to pull data from APIC
45
51
self .max_frequency = int (config .get ('frequency' , 600 ))
46
- self .grant_type = config .get ('grant_type' , 'password' )
52
+ # self.grant_type = config.get('grant_type', 'password')
47
53
self .org_metrics = (config .get ('process_org_metrics' , 'true' ) == 'true' )
48
54
self .version = Gauge ('apiconnect_build_info' ,
49
55
"A metric with a constant '1' value labeled with API Connect version details" ,
50
56
["version" , "juhu_release" ])
57
+
58
+ # Provider creds
51
59
if 'secret' in config :
52
60
# If config points to a secret, then load from that
53
61
# either in this namespace, or the specified one
@@ -63,30 +71,52 @@ def __init__(self, config, trawler):
63
71
else :
64
72
# Load password from secret `cloudmanager_password`
65
73
self .password = trawler .read_secret ('cloudmanager_password' )
74
+
75
+ # Cloud manager creds
76
+ if 'cloud_manager_secret' in config :
77
+ # If config points to a secret, then load from that
78
+ # either in this namespace, or the specified one
79
+ self .load_credentials_from_secret (
80
+ config .get ('cloud_manager_secret' ),
81
+ config .get ('cloud_manager_secret_namespace' , self .namespace ),
82
+ cloud_manager = True )
83
+
84
+
66
85
if self .password is None :
67
86
# Use out of box default password
68
87
self .password = 'admin'
88
+
69
89
self .hostname = self .find_hostname ()
70
90
logger .debug ("Hostname found is {}" .format (self .hostname ))
71
91
self .trawler = trawler
72
92
73
- def load_credentials_from_secret (self , secret_name , namespace ):
93
+ def load_credentials_from_secret (self , secret_name , namespace , cloud_manager = False ):
74
94
try :
75
95
if self .use_kubeconfig :
76
96
config .load_kube_config ()
77
97
else :
78
98
config .load_incluster_config ()
99
+
79
100
v1 = client .CoreV1Api ()
80
101
logger .info ("Loading cloud manager credentials from secret {} in namespace {}" .format (secret_name , namespace ))
81
102
# Get credentials secret
82
103
secrets_response = v1 .read_namespaced_secret (name = secret_name , namespace = namespace )
83
- if 'password' in secrets_response .data :
84
- self .password = base64 .b64decode (secrets_response .data ['password' ]).decode ('utf-8' )
85
- self .username = base64 .b64decode (secrets_response .data ['username' ]).decode ('utf-8' )
86
- logger .info ("Username to use is {}, password length is {}" .format (self .username , len (self .password )))
104
+ if cloud_manager :
105
+ if 'password' in secrets_response .data :
106
+ self .cm_password = base64 .b64decode (secrets_response .data ['password' ]).decode ('utf-8' )
107
+ self .cm_username = base64 .b64decode (secrets_response .data ['username' ]).decode ('utf-8' )
108
+ logger .info ("Username to use is {}, password length is {}" .format (self .cm_username , len (self .cm_password )))
109
+ else :
110
+ if 'password' in secrets_response .data :
111
+ self .password = base64 .b64decode (secrets_response .data ['password' ]).decode ('utf-8' )
112
+ self .username = base64 .b64decode (secrets_response .data ['username' ]).decode ('utf-8' )
113
+ logger .info ("Username to use is {}, password length is {}" .format (self .username , len (self .password )))
114
+
115
+ # Client secret is not applicable to cloud manager so no test needed
87
116
if 'client_secret' in secrets_response .data :
88
117
self .client_secret = base64 .b64decode (secrets_response .data ['client_secret' ]).decode ('utf-8' )
89
118
self .client_id = base64 .b64decode (secrets_response .data ['client_id' ]).decode ('utf-8' )
119
+ self .grant_type = 'client_credentials'
90
120
logger .info ("Client ID to use is {}, Client Secret length is {}" .format (self .client_id , len (self .client_secret )))
91
121
92
122
except client .rest .ApiException as e :
@@ -128,15 +158,20 @@ def find_hostname(self):
128
158
logger .exception (e )
129
159
130
160
def get_webhook_status (self ):
131
- logger .info ("Getting webhook data from API Manager" )
161
+ """Get the webhook data from the API Manager
162
+ This requires cloud manager access
163
+ """
164
+ if not self .cm_token :
165
+ logger .debug ("No cloud manager token available. Not getting webhook data" )
166
+ logger .info ("Getting webhook data from Cloud Manager" )
132
167
try :
133
168
url = "https://{}/api/cloud/webhooks" .format (self .hostname )
134
169
response = requests .get (
135
170
url = url ,
136
171
headers = {
137
172
"Accept" : "application/json" ,
138
173
"Content-Type" : "application/json" ,
139
- "Authorization" : "Bearer {}" .format (self .token ),
174
+ "Authorization" : "Bearer {}" .format (self .cm_token ),
140
175
},
141
176
verify = False
142
177
)
@@ -200,6 +235,10 @@ def fish(self):
200
235
logger .debug ("Disabled because a fatal error already occurred" )
201
236
return
202
237
238
+ # Allow 10 seconds to run
239
+ if self .cm_token_expires - 10 < time .time ():
240
+ self .get_token (self .hostname , cloud_manager = True )
241
+
203
242
# Allow 10 seconds to run
204
243
if self .token_expires - 10 < time .time ():
205
244
self .get_token (self .hostname )
@@ -214,6 +253,7 @@ def fish(self):
214
253
logger .debug (self .data )
215
254
else :
216
255
logger .warning ("No token" )
256
+
217
257
if 'counts' in self .data :
218
258
for object_type in self .data ['counts' ]:
219
259
logger .debug ("Type: {}, Value: {}" .format (object_type , self .data ['counts' ][object_type ]))
@@ -223,7 +263,9 @@ def fish(self):
223
263
if org ['org_type' ] != 'admin' :
224
264
for catalog in org ['catalogs' ]['results' ]:
225
265
self .process_org_metrics (org ['name' ], catalog ['name' ])
226
- self .get_webhook_status ()
266
+
267
+ if self .cm_token :
268
+ self .get_webhook_status ()
227
269
228
270
@alog .timed_function (logger .trace )
229
271
def process_org_metrics (self , org_name , catalog_name ):
@@ -273,7 +315,13 @@ def process_org_metrics(self, org_name, catalog_name):
273
315
274
316
# Get the authorization bearer token
275
317
# See https://chrisphillips-cminion.github.io/apiconnect/2019/09/18/GettingoAuthTokenFromAPIC.html
276
- def get_token (self , host ):
318
+ def get_token (self , host , cloud_manager = False ):
319
+ """Get the auth token from API Manager or Cloud Manager
320
+ API Manager requires client credentials, client_id and client_secret
321
+ Cloud Manager requires username and password, along with the known client_id and client_secret
322
+ The secret in the config could be either grant_type client_credentials or password,
323
+ but cloud manager access is always requried to be grant_type password.
324
+ """
277
325
logger .debug ("Getting bearer token" )
278
326
279
327
headers = {'Content-Type' : 'application/json' , 'Accept' : 'application/json' }
@@ -284,6 +332,12 @@ def get_token(self, host):
284
332
data ['username' ] = self .username
285
333
data ['password' ] = self .password
286
334
data ['realm' ] = 'admin/default-idp-1'
335
+ if cloud_manager :
336
+ data ['client_id' ] = self .default_client_id
337
+ data ['client_secret' ] = self .default_client_secret
338
+ data ['username' ] = self .cm_username
339
+ data ['password' ] = self .cm_password
340
+ data ['realm' ] = 'admin/default-idp-1'
287
341
288
342
url = "https://{}/api/token" .format (host )
289
343
response = requests .post (
@@ -293,10 +347,19 @@ def get_token(self, host):
293
347
verify = False )
294
348
295
349
if response .status_code == 200 :
350
+ token_expires = 0
351
+ token_type = "Token"
296
352
json_data = response .json ()
297
- self .token = json_data ['access_token' ]
298
- self .token_expires = json_data ['expires_in' ] + time .time ()
299
- logger .info ("Token expires at {} UTC" .format (datetime .datetime .utcfromtimestamp (int (self .token_expires ))))
353
+ if cloud_manager :
354
+ self .cm_token = json_data ['access_token' ]
355
+ self .cm_token_expires = json_data ['expires_in' ] + time .time ()
356
+ token_expires = self .cm_token_expires
357
+ token_type = "Cloud Manager Token"
358
+ else :
359
+ self .token = json_data ['access_token' ]
360
+ self .token_expires = json_data ['expires_in' ] + time .time ()
361
+ token_expires = self .token_expires
362
+ logger .info ("{} expires at {} UTC" .format (token_type , datetime .datetime .utcfromtimestamp (int (token_expires ))))
300
363
else :
301
364
logger .error ("Disabled manager net as failed to get bearer token: {}" .format (response .status_code ))
302
365
self .errored = True
0 commit comments