1 | /**
|
2 | * Copyright 2018 F5 Networks, Inc.
|
3 | *
|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | * you may not use this file except in compliance with the License.
|
6 | * You may obtain a copy of the License at
|
7 | *
|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
9 | *
|
10 | * Unless required by applicable law or agreed to in writing, software
|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 | * See the License for the specific language governing permissions and
|
14 | * limitations under the License.
|
15 | */
|
16 |
|
17 | ;
|
18 |
|
19 | const q = require('q');
|
20 | const util = require('./util');
|
21 | const Logger = require('./logger');
|
22 | const BigIq53LicenseProvider = require('./bigIq53LicenseProvider');
|
23 |
|
24 | const LICENSE_PATH = '/cm/device/tasks/licensing/pool/member-management/';
|
25 | const LICENSE_TIMEOUT = { maxRetries: 40, retryIntervalMs: 5000 };
|
26 |
|
27 | /**
|
28 | * BigIq 5.3 license provider constructor
|
29 | *
|
30 | * @class
|
31 | * @classdesc
|
32 | * Provides ability to get licenses from BIG-IQ 5.3 (and compatible versions).
|
33 | *
|
34 | * @param {Object} bigIp - Base {@link BigIp} object.
|
35 | * @param {Object} [options] - Optional parameters.
|
36 | * @param {Object} [options.logger] - Logger to use. Or, pass loggerOptions to get your own logger.
|
37 | * @param {Object} [options.loggerOptions] - Options for the logger.
|
38 | * See {@link module:logger.getLogger} for details.
|
39 | */
|
40 | function BigIq54LicenseProvider(bigIp, options) {
|
41 | const injectedLogger = options ? options.logger : undefined;
|
42 | let loggerOptions = options ? options.loggerOptions : undefined;
|
43 |
|
44 | this.constructorOptions = {};
|
45 | if (options) {
|
46 | Object.keys(options).forEach((option) => {
|
47 | this.constructorOptions[option] = options[option];
|
48 | });
|
49 | }
|
50 |
|
51 | if (injectedLogger) {
|
52 | this.logger = injectedLogger;
|
53 | util.setLogger(injectedLogger);
|
54 | } else {
|
55 | loggerOptions = loggerOptions || { logLevel: 'none' };
|
56 | loggerOptions.module = module;
|
57 | this.logger = Logger.getLogger(loggerOptions);
|
58 | util.setLoggerOptions(loggerOptions);
|
59 | }
|
60 |
|
61 | this.bigIp = bigIp;
|
62 | }
|
63 |
|
64 | /**
|
65 | * Gets a license from BIG-IQ for an unmanaged BIG-IP
|
66 | *
|
67 | * @param {Object} bigIqControl - iControl object for BIG-IQ
|
68 | * @param {String} poolName - Name of the BIG-IQ license pool to use
|
69 | * @param {String} bigIpMgmtAddress - IP address of BIG-IP management port.
|
70 | * Unused only for display in this API. Default 192.0.2.1.
|
71 | * @param {String} bigIpMgmtPort - IP port of BIG-IP management port.
|
72 | * Unused in this API, but here for consistency.
|
73 | * @param {Object} options - Optional parameters
|
74 | * @param {String} options.cloud - Cloud environment. Accepted values are:
|
75 | * aws, azure, gce, vmware, hyperv, kvm, xen
|
76 | * @param {String} [options.skuKeyword1] - skuKeyword1 parameter for CLPv2 licensing. Default none.
|
77 | * @param {String} [options.skuKeyword2] - skuKeyword2 parameter for CLPv2 licensing. Default none.
|
78 | * @param {String} [options.unitOfMeasure] - unitOfMeasure parameter for CLPv2 licensing. Default none.
|
79 | * @param {String} [options.tenant] - tenant parameter for CLPv2 licensing. Default none.
|
80 | * @param {Boolean} [options.noUnreachable] - Do not use the unreachable API even on BIG-IQs that support it.
|
81 | *
|
82 | *
|
83 | * @returns {Promise} A promise which is resolved when the BIG-IP has been licensed
|
84 | * or rejected if an error occurs.
|
85 | */
|
86 | BigIq54LicenseProvider.prototype.getUnmanagedDeviceLicense = function getUnmanagedDeviceLicense(
|
87 | bigIqControl,
|
88 | poolName,
|
89 | bigIpMgmtAddress,
|
90 | bigIpMgmtPort,
|
91 | options
|
92 | ) {
|
93 | if (options && options.noUnreachable) {
|
94 | this.logger.silly('noUnreachable specified, passing off to 5.3 license API');
|
95 | const licenseProvider = new BigIq53LicenseProvider(
|
96 | this.bigIp,
|
97 | this.constructorOptions
|
98 | );
|
99 | return licenseProvider.getUnmanagedDeviceLicense(
|
100 | bigIqControl,
|
101 | poolName,
|
102 | bigIpMgmtAddress,
|
103 | bigIpMgmtPort,
|
104 | options
|
105 | );
|
106 | }
|
107 |
|
108 | if (!options || !options.cloud) {
|
109 | const message = 'Cloud name is required when licensing from BIG-IQ 5.4';
|
110 | this.logger.info(message);
|
111 | return q.reject(new Error(message));
|
112 | }
|
113 |
|
114 | this.logger.debug('Licensing from pool', poolName);
|
115 | return licenseFromPool.call(this, bigIqControl, poolName, bigIpMgmtAddress, options);
|
116 | };
|
117 |
|
118 | /**
|
119 | * Revokes a license from a BIG-IP
|
120 | *
|
121 | * @param {Object} bigIqControl - iControl object for BIG-IQ
|
122 | * @param {String} poolName - Name of the BIG-IQ license pool to use
|
123 | * @param {String} instance - {@link AutoscaleInstance} to revoke license for
|
124 | * @param {Object} options - Optional parameters
|
125 | * @param {Boolean} [options.noUnreachable] - Do not use the unreachable API even on BIG-IQs that support it.
|
126 | *
|
127 | * @returns {Promise} A promise which is resolved when the BIG-IP license has
|
128 | * been revoked, or rejected if an error occurs.
|
129 | */
|
130 | BigIq54LicenseProvider.prototype.revoke = function revoke(bigIqControl, poolName, instance, options) {
|
131 | if (options && options.noUnreachable) {
|
132 | this.logger.silly('noUnreachable specified, passing off to 5.3 revoke API');
|
133 | const licenseProvider = new BigIq53LicenseProvider(
|
134 | this.bigIp,
|
135 | this.constructorOptions
|
136 | );
|
137 | return licenseProvider.revoke(bigIqControl, poolName, instance, options);
|
138 | }
|
139 |
|
140 | return bigIqControl.create(
|
141 | LICENSE_PATH,
|
142 | {
|
143 | command: 'revoke',
|
144 | licensePoolName: poolName,
|
145 | address: instance.mgmtIp || '192.0.2.1',
|
146 | assignmentType: 'UNREACHABLE',
|
147 | macAddress: instance.macAddress
|
148 | }
|
149 | );
|
150 | };
|
151 |
|
152 | /**
|
153 | * Gets the license timeout to use
|
154 | *
|
155 | * This is here so that it can be overridden by test code
|
156 | *
|
157 | * @returns the license timeout
|
158 | */
|
159 | BigIq54LicenseProvider.prototype.getLicenseTimeout = function getLicenseTimeout() {
|
160 | return LICENSE_TIMEOUT;
|
161 | };
|
162 |
|
163 | function licenseFromPool(bigIqControl, poolName, bigIpMgmtAddress, options) {
|
164 | const hypervisor = options ? options.cloud : undefined;
|
165 | const skuKeyword1 = options ? options.skuKeyword1 : undefined;
|
166 | const skuKeyword2 = options ? options.skuKeyword2 : undefined;
|
167 | const unitOfMeasure = options ? options.unitOfMeasure : undefined;
|
168 | const tenant = options ? options.tenant : undefined;
|
169 | // get our mac address
|
170 | return this.bigIp.deviceInfo()
|
171 | .then((deviceInfo) => {
|
172 | let tenantValue = '';
|
173 | if (tenant) {
|
174 | tenantValue = `${tenant},
|
175 | mgmtAddress=${deviceInfo.managementAddress},hostname=${deviceInfo.hostname}`;
|
176 | } else {
|
177 | tenantValue = `mgmtAddress=${deviceInfo.managementAddress},hostname=${deviceInfo.hostname}`;
|
178 | }
|
179 | const createLicenseTask = function () {
|
180 | return bigIqControl.create(
|
181 | LICENSE_PATH,
|
182 | {
|
183 | hypervisor,
|
184 | skuKeyword1,
|
185 | skuKeyword2,
|
186 | unitOfMeasure,
|
187 | command: 'assign',
|
188 | licensePoolName: poolName,
|
189 | address: bigIpMgmtAddress || '192.0.2.1',
|
190 | assignmentType: 'UNREACHABLE',
|
191 | tenant: tenantValue,
|
192 | macAddress: deviceInfo.baseMac
|
193 | }
|
194 | );
|
195 | };
|
196 |
|
197 | return util.tryUntil(this, util.MEDIUM_RETRY, createLicenseTask);
|
198 | })
|
199 | .then((response) => {
|
200 | this.logger.debug(response);
|
201 |
|
202 | const taskId = response.id;
|
203 |
|
204 | const getLicenseText = function () {
|
205 | return bigIqControl.list(LICENSE_PATH + taskId)
|
206 | .then((taskResponse) => {
|
207 | const status = taskResponse.status;
|
208 | this.logger.verbose('Current licensing task status:', status);
|
209 | if (status === 'FINISHED') {
|
210 | return q(
|
211 | {
|
212 | success: true,
|
213 | licenseText: taskResponse.licenseText
|
214 | }
|
215 | );
|
216 | } else if (status === 'FAILED') {
|
217 | return q(
|
218 | {
|
219 | success: false,
|
220 | errorMessage: taskResponse.errorMessage
|
221 | }
|
222 | );
|
223 | }
|
224 | return q.reject();
|
225 | });
|
226 | };
|
227 |
|
228 | return util.tryUntil(this, this.getLicenseTimeout(), getLicenseText)
|
229 | .then((licenseTextResponse) => {
|
230 | if (licenseTextResponse.success && licenseTextResponse.licenseText) {
|
231 | this.logger.silly('License text', licenseTextResponse.licenseText);
|
232 | return this.bigIp.onboard.installLicense(licenseTextResponse.licenseText);
|
233 | }
|
234 |
|
235 | return q.reject(new Error(licenseTextResponse.errorMessage));
|
236 | })
|
237 | .catch((err) => {
|
238 | this.logger.info('Failed to license:', err && err.message ? err.message : err);
|
239 | return q.reject(err);
|
240 | });
|
241 | });
|
242 | }
|
243 |
|
244 | module.exports = BigIq54LicenseProvider;
|