1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | 'use strict';
|
18 |
|
19 | const q = require('q');
|
20 | const util = require('./util');
|
21 | const Logger = require('./logger');
|
22 |
|
23 | const LICENSE_PATH = '/cm/device/licensing/pool/regkey/licenses/';
|
24 | const LICENSE_TIMEOUT = { maxRetries: 40, retryIntervalMs: 5000 };
|
25 |
|
26 | let logger;
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | function BigIq52LicenseProvider(bigIp, options) {
|
43 | const version = options ? options.version : '5.2.0';
|
44 | const injectedLogger = options ? options.logger : undefined;
|
45 | let loggerOptions = options ? options.loggerOptions : undefined;
|
46 |
|
47 | if (injectedLogger) {
|
48 | this.logger = injectedLogger;
|
49 | util.setLogger(injectedLogger);
|
50 | } else {
|
51 | loggerOptions = loggerOptions || { logLevel: 'none' };
|
52 | loggerOptions.module = module;
|
53 | this.logger = Logger.getLogger(loggerOptions);
|
54 | util.setLoggerOptions(loggerOptions);
|
55 | }
|
56 |
|
57 | logger = this.logger;
|
58 | this.bigIp = bigIp;
|
59 | this.version = version || '5.2.0';
|
60 | }
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | BigIq52LicenseProvider.prototype.getUnmanagedDeviceLicense = function getUnmanagedDeviceLicense(
|
74 | bigIqControl,
|
75 | poolName,
|
76 | bigIpMgmtAddress,
|
77 | bigIpMgmtPort
|
78 | ) {
|
79 | this.logger.debug('Getting BIG-IP license pool UUID.');
|
80 |
|
81 | return getPoolUuid(bigIqControl, poolName)
|
82 | .then((poolUuid) => {
|
83 | this.logger.debug('Got pool UUID:', poolUuid);
|
84 | return util.tryUntil(
|
85 | this,
|
86 | util.MEDIUM_RETRY,
|
87 | licenseFromPool,
|
88 | [
|
89 | bigIqControl,
|
90 | bigIpMgmtAddress,
|
91 | bigIpMgmtPort,
|
92 | poolUuid
|
93 | ]
|
94 | );
|
95 | })
|
96 | .catch((err) => {
|
97 | this.logger.warn(err);
|
98 | return q.reject(err);
|
99 | });
|
100 | };
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | BigIq52LicenseProvider.prototype.revoke = function revoke(bigIqControl, poolName, instance) {
|
113 | let poolUuid;
|
114 |
|
115 | return getPoolUuid(bigIqControl, poolName)
|
116 | .then((uuid) => {
|
117 | poolUuid = uuid;
|
118 | return getLicensesInPool(bigIqControl, poolUuid);
|
119 | })
|
120 | .then((licensesInPool) => {
|
121 | const deferred = q.defer();
|
122 | let licenses;
|
123 |
|
124 | if (!licensesInPool) {
|
125 | licenses = [];
|
126 | } else if (!Array.isArray(licensesInPool)) {
|
127 | licenses = [licensesInPool];
|
128 | } else {
|
129 | licenses = licensesInPool.slice();
|
130 | }
|
131 |
|
132 | const findRegKeyForHostname = function (index) {
|
133 | let currentIndex = index;
|
134 | let license;
|
135 |
|
136 | if (currentIndex > licenses.length - 1) {
|
137 | logger.info('License for host not found.');
|
138 | deferred.reject(new Error('License for host not found.'));
|
139 | } else {
|
140 | license = licenses[currentIndex];
|
141 | if (license.licenseState) {
|
142 | getMembersForKey(bigIqControl, poolUuid, license.licenseState.registrationKey)
|
143 | .then((membersForKey) => {
|
144 | let found = false;
|
145 | let members;
|
146 |
|
147 | if (!membersForKey) {
|
148 | members = [];
|
149 | } else if (!Array.isArray(membersForKey)) {
|
150 | members = [membersForKey];
|
151 | } else {
|
152 | members = membersForKey.slice();
|
153 | }
|
154 |
|
155 | logger.silly(
|
156 | 'reg key members',
|
157 | license.licenseState.registrationKey,
|
158 | 'members',
|
159 | members
|
160 | );
|
161 |
|
162 | for (let i = 0; i < members.length; i++) {
|
163 | if (members[i].deviceName === instance.hostname) {
|
164 | found = true;
|
165 | deferred.resolve(
|
166 | {
|
167 | regKey: license.licenseState.registrationKey,
|
168 | member: members[i]
|
169 | }
|
170 | );
|
171 | }
|
172 | }
|
173 |
|
174 | if (!found) {
|
175 | currentIndex += 1;
|
176 | findRegKeyForHostname(currentIndex);
|
177 | }
|
178 | })
|
179 | .catch((err) => {
|
180 | logger.debug('error while iterating licenses', err);
|
181 | currentIndex += 1;
|
182 | findRegKeyForHostname(currentIndex);
|
183 | });
|
184 | }
|
185 | }
|
186 | };
|
187 |
|
188 | findRegKeyForHostname(0);
|
189 |
|
190 | return deferred.promise;
|
191 | })
|
192 | .then((regKeyMember) => {
|
193 | if (regKeyMember) {
|
194 |
|
195 |
|
196 | const body = {
|
197 | username: this.bigIp.user || 'dummyUser',
|
198 | password: this.bigIp.password || 'dummyPassword',
|
199 | id: regKeyMember.member.id
|
200 | };
|
201 |
|
202 | return bigIqControl.delete(
|
203 |
|
204 | `${LICENSE_PATH}${poolUuid}/offerings/${regKeyMember.regKey}/members/${regKeyMember.member.id}`,
|
205 | body
|
206 | );
|
207 | }
|
208 | return q();
|
209 | });
|
210 | };
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | BigIq52LicenseProvider.prototype.getLicenseTimeout = function getLicenseTimeout() {
|
220 | return LICENSE_TIMEOUT;
|
221 | };
|
222 |
|
223 | function licenseFromPool(bigIqControl, bigIpMgmtAddress, bigIpMgmtPort, poolUuid) {
|
224 | const deferred = q.defer();
|
225 |
|
226 | getValidRegKey.call(this, bigIqControl, poolUuid)
|
227 | .then((regKey) => {
|
228 | if (regKey) {
|
229 | return tryRegKey.call(this, bigIqControl, bigIpMgmtAddress, bigIpMgmtPort, poolUuid, regKey);
|
230 | }
|
231 | deferred.reject(new Error('No valid reg keys found.'));
|
232 | })
|
233 | .then(() => {
|
234 | deferred.resolve();
|
235 | })
|
236 | .catch((err) => {
|
237 | this.logger.info(err);
|
238 | deferred.reject(err);
|
239 | });
|
240 |
|
241 | return deferred.promise;
|
242 | }
|
243 |
|
244 | function getPoolUuid(bigIqControl, poolName) {
|
245 | let poolUuid;
|
246 |
|
247 | return bigIqControl.list(`${LICENSE_PATH}?$select=id,name`)
|
248 | .then((poolResponse) => {
|
249 | let pools;
|
250 |
|
251 | if (!poolResponse) {
|
252 | pools = [];
|
253 | } else if (!Array.isArray(poolResponse)) {
|
254 | pools = [poolResponse];
|
255 | } else {
|
256 | pools = poolResponse.slice();
|
257 | }
|
258 |
|
259 | for (let i = 0; i < pools.length; i++) {
|
260 | if (pools[i].name === poolName) {
|
261 | poolUuid = pools[i].id;
|
262 | break;
|
263 | }
|
264 | }
|
265 |
|
266 | if (poolUuid) {
|
267 | return poolUuid;
|
268 | }
|
269 | return q.reject(new Error(`No license pool found with name: ${poolName}`));
|
270 | });
|
271 | }
|
272 |
|
273 | function getValidRegKey(bigIqControl, poolUuid) {
|
274 | this.logger.debug('Getting reg keys in pool');
|
275 | return getLicensesInPool(bigIqControl, poolUuid)
|
276 | .then((licensesResponse) => {
|
277 | const now = new Date();
|
278 | const deferred = q.defer();
|
279 |
|
280 | let licenses;
|
281 |
|
282 | if (!licensesResponse) {
|
283 | licenses = [];
|
284 | } else if (!Array.isArray(licensesResponse)) {
|
285 | licenses = [licensesResponse];
|
286 | } else {
|
287 | licenses = licensesResponse.slice();
|
288 | }
|
289 |
|
290 | const findValidLicense = function (index) {
|
291 | let currentIndex = index;
|
292 |
|
293 | let license;
|
294 |
|
295 | if (index > licenses.length - 1) {
|
296 | logger.info('No valid licenses available.');
|
297 | deferred.resolve();
|
298 | } else {
|
299 | license = licenses[currentIndex];
|
300 | if (
|
301 | license.licenseState &&
|
302 | license.licenseState.licenseStartDateTime &&
|
303 | license.licenseState.licenseEndDateTime &&
|
304 | new Date(license.licenseState.licenseStartDateTime) < now &&
|
305 | now < new Date(license.licenseState.licenseEndDateTime)
|
306 | ) {
|
307 | logger.silly(license.licenseState.registrationKey, 'is active');
|
308 | getMembersForKey(bigIqControl, poolUuid, license.licenseState.registrationKey)
|
309 | .then((response) => {
|
310 | logger.silly(
|
311 | 'reg key',
|
312 | license.licenseState.registrationKey,
|
313 | 'members',
|
314 | response
|
315 | );
|
316 |
|
317 | if (Array.isArray(response) && response.length === 0) {
|
318 | logger.silly(license.licenseState.registrationKey, 'is available');
|
319 | deferred.resolve(license.licenseState.registrationKey);
|
320 | } else {
|
321 | currentIndex += 1;
|
322 | findValidLicense(currentIndex);
|
323 | }
|
324 | })
|
325 | .catch((err) => {
|
326 | logger.debug('error while iterating licenses', err);
|
327 | currentIndex += 1;
|
328 | findValidLicense(currentIndex);
|
329 | });
|
330 | } else {
|
331 | logger.debug(license.licenseState.registrationKey, 'is not active');
|
332 | currentIndex += 1;
|
333 | findValidLicense(currentIndex);
|
334 | }
|
335 | }
|
336 | };
|
337 |
|
338 | findValidLicense(0);
|
339 |
|
340 | return deferred.promise;
|
341 | });
|
342 | }
|
343 |
|
344 | function getLicensesInPool(bigIqControl, poolUuid) {
|
345 | return bigIqControl.list(`${LICENSE_PATH}${poolUuid}/offerings?$select=licenseState`);
|
346 | }
|
347 |
|
348 | function getMembersForKey(bigIqControl, poolUuid, regKey) {
|
349 | return bigIqControl.list(`${LICENSE_PATH}${poolUuid}/offerings/${regKey}/members`);
|
350 | }
|
351 |
|
352 | function tryRegKey(bigIqControl, bigIpMgmtAddress, bigIpMgmtPort, poolUuid, regKey) {
|
353 | this.logger.info('Requesting license using', regKey);
|
354 |
|
355 |
|
356 |
|
357 |
|
358 | let deviceAddress;
|
359 | let port;
|
360 |
|
361 | if (util.versionCompare(this.version, '5.2.0') > 0) {
|
362 | deviceAddress = bigIpMgmtAddress;
|
363 | port = bigIpMgmtPort;
|
364 | } else {
|
365 | deviceAddress = `${bigIpMgmtAddress}:${bigIpMgmtPort}`;
|
366 | }
|
367 |
|
368 | const body = {
|
369 | deviceAddress,
|
370 | username: this.bigIp.user,
|
371 | password: this.bigIp.password
|
372 | };
|
373 |
|
374 | if (port) {
|
375 | body.httpsPort = port;
|
376 | }
|
377 |
|
378 | return bigIqControl.create(
|
379 | `${LICENSE_PATH}${poolUuid}/offerings/${regKey}/members`,
|
380 | body
|
381 | )
|
382 | .then((response) => {
|
383 | this.logger.debug(response);
|
384 |
|
385 | let status;
|
386 | let memberId;
|
387 |
|
388 | const isLicensed = function () {
|
389 | return bigIqControl.list(`${LICENSE_PATH}${poolUuid}/offerings/${regKey}/members/${memberId}`)
|
390 | .then((membersResponse) => {
|
391 | status = membersResponse.status;
|
392 | logger.verbose('Current licensing status:', status);
|
393 | if (status === 'LICENSED') {
|
394 | return q();
|
395 | }
|
396 |
|
397 | return q.reject();
|
398 | });
|
399 | };
|
400 |
|
401 | if (response) {
|
402 | status = response.status;
|
403 | memberId = response.id;
|
404 | this.logger.debug('Current licensing state:', status);
|
405 | this.logger.silly('Member UUID:', memberId);
|
406 |
|
407 | if (status === 'LICENSED') {
|
408 | return q();
|
409 | }
|
410 |
|
411 | this.logger.verbose('Waiting to be LICENSED.');
|
412 | return util.tryUntil(this, this.getLicenseTimeout(), isLicensed)
|
413 | .then(() => {
|
414 | this.logger.info('Successfully licensed');
|
415 | return q();
|
416 | })
|
417 | .catch((err) => {
|
418 | this.logger.info('Failed to license', err);
|
419 | return q.reject(new Error('Giving up on licensing via BIG-IQ.'));
|
420 | });
|
421 | }
|
422 |
|
423 | return q.reject(new Error('No resposnse for pool/offerings/key/members'));
|
424 | });
|
425 | }
|
426 |
|
427 | module.exports = BigIq52LicenseProvider;
|