1 | /**
|
2 | * Copyright 2016-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 options = require('commander');
|
21 | const BigIp = require('../lib/bigIp');
|
22 | const Logger = require('../lib/logger');
|
23 | const ipc = require('../lib/ipc');
|
24 | const signals = require('../lib/signals');
|
25 | const util = require('../lib/util');
|
26 | const cryptoUtil = require('../lib/cryptoUtil');
|
27 |
|
28 | (function run() {
|
29 | const runner = {
|
30 |
|
31 | /**
|
32 | * Runs the network setup script
|
33 | *
|
34 | * @param {String[]} argv - The process arguments
|
35 | * @param {Object} testOpts - Options used during testing
|
36 | * @param {Object} testOpts.bigIp - BigIp object to use for testing
|
37 | * @param {Function} cb - Optional cb to call when done
|
38 | */
|
39 | run(argv, testOpts, cb) {
|
40 | const DEFAULT_LOG_FILE = '/tmp/network.log';
|
41 | const ARGS_FILE_ID = `network_${Date.now()}`;
|
42 | const KEYS_TO_MASK = ['-p', '--password', '--set-password', '--set-root-password'];
|
43 | const REQUIRED_OPTIONS = ['host'];
|
44 | const DEFAULT_CIDR = '/24';
|
45 |
|
46 | const OPTIONS_TO_UNDEFINE = [
|
47 | 'password',
|
48 | 'passwordUrl'
|
49 | ];
|
50 |
|
51 | const optionsForTest = {};
|
52 | const vlans = [];
|
53 | const selfIps = [];
|
54 | const routes = [];
|
55 | const mgmtRoutes = [];
|
56 | const loggerOptions = {};
|
57 |
|
58 | let loggableArgs;
|
59 | let logger;
|
60 | let logFileName;
|
61 | let bigIp;
|
62 | let randomUser;
|
63 | let exiting;
|
64 |
|
65 | Object.assign(optionsForTest, testOpts);
|
66 |
|
67 |
|
68 | try {
|
69 | /* eslint-disable max-len */
|
70 |
|
71 | // Can't use getCommonOptions here because of the special reboot handling
|
72 | options
|
73 | .version('4.23.0-beta.3')
|
74 | .option(
|
75 | '--host <ip_address>',
|
76 | 'BIG-IP management IP to which to send commands.'
|
77 | )
|
78 | .option(
|
79 | '-u, --user <user>',
|
80 | 'BIG-IP admin user name. Default is to create a temporary user (this only works when running on the device).'
|
81 | )
|
82 | .option(
|
83 | '-p, --password [password]',
|
84 | 'BIG-IP admin user password. Use this or --password-url. One of these is required when specifying the user.'
|
85 | )
|
86 | .option(
|
87 | '--password-url [password_url]',
|
88 | 'URL (file, http(s)) to location that contains BIG-IP admin user password. Use this or --password. One of these is required when specifying the user.'
|
89 | )
|
90 | .option(
|
91 | '--password-encrypted',
|
92 | 'Indicates that the password is encrypted (either with encryptDataToFile or generatePassword)'
|
93 | )
|
94 | .option(
|
95 | '--port <port>',
|
96 | 'BIG-IP management SSL port to connect to. Default 443.',
|
97 | parseInt
|
98 | )
|
99 | .option(
|
100 | '--background',
|
101 | 'Spawn a background process to do the work. If you are running in cloud init, you probably want this option.'
|
102 | )
|
103 | .option(
|
104 | '--signal <signal>',
|
105 | 'Signal to send when done. Default NETWORK_DONE.'
|
106 | )
|
107 | .option(
|
108 | '--wait-for <signal>',
|
109 | 'Wait for the named signal before running.'
|
110 | )
|
111 | .option(
|
112 | '--log-level <level>',
|
113 | 'Log level (none, error, warn, info, verbose, debug, silly). Default is info.', 'info'
|
114 | )
|
115 | .option(
|
116 | '-o, --output <file>',
|
117 | `Log to file as well as console. This is the default if background process is spawned. Default is ${DEFAULT_LOG_FILE}`
|
118 | )
|
119 | .option(
|
120 | '-e, --error-file <file>',
|
121 | 'Log exceptions to a specific file. Default is /tmp/cloudLibsError.log, or cloudLibsError.log in --output file directory'
|
122 | )
|
123 | .option(
|
124 | '--no-console',
|
125 | 'Do not log to console. Default false (log to console).'
|
126 | )
|
127 | .option(
|
128 | '--single-nic',
|
129 | 'Set db variables for single NIC configuration.'
|
130 | )
|
131 | .option(
|
132 | '--multi-nic',
|
133 | 'Set db variables for multi NIC configuration.'
|
134 | )
|
135 | .option(
|
136 | '--default-gw <gateway_address>',
|
137 | 'Set default gateway to gateway_address.'
|
138 | )
|
139 | .option(
|
140 | '--route <name:name, gw:address, network:network, interface:interface_name>',
|
141 | 'Create arbitrary route with name for destination network via gateway address or interface name',
|
142 | util.mapArray,
|
143 | routes
|
144 | )
|
145 | .option(
|
146 | '--mgmt-route <name:name, gw:address, network:network>',
|
147 | 'Create management route with name for destination network via gateway address.',
|
148 | util.mapArray,
|
149 | mgmtRoutes
|
150 | )
|
151 | .option(
|
152 | '--local-only',
|
153 | 'Create LOCAL_ONLY partition for gateway and assign to traffic-group-local-only.'
|
154 | )
|
155 | .option(
|
156 | '--vlan <name:name, nic:nic, [mtu:mtu], [tag:tag]>',
|
157 | 'Create vlan with name on nic (for example, 1.1). Optionally specify mtu and tag. For multiple vlans, use multiple --vlan entries.',
|
158 | util.mapArray,
|
159 | vlans
|
160 | )
|
161 | .option(
|
162 | '--self-ip <name:name, address:ip_address, vlan:vlan_name, [allow:service1:port1 service2:port2], [trafficGroup:traffic_group_name]>',
|
163 | 'Create self IP with name and ip_address on vlan with optional port lockdown. For multiple self IPs, use multiple --self-ip entries. Default CIDR prefix is 24 if not specified.',
|
164 | util.mapArray,
|
165 | selfIps
|
166 | )
|
167 | .option(
|
168 | '--discovery-address <ip_address>',
|
169 | 'IP address that the BIG-IQ will use for device discovery. This is required for onboarding a BIG-IQ. The IP address must already exist on the BIG-IQ device. For clustering, this should be a Self IP address.'
|
170 | )
|
171 | .option(
|
172 | '--force-reboot',
|
173 | 'Force a reboot at the end. This may be necessary for certain configurations.'
|
174 | )
|
175 | .parse(argv);
|
176 | /* eslint-enable max-len */
|
177 |
|
178 | loggerOptions.console = options.console;
|
179 | loggerOptions.logLevel = options.logLevel;
|
180 | loggerOptions.module = module;
|
181 |
|
182 | if (options.output) {
|
183 | loggerOptions.fileName = options.output;
|
184 | }
|
185 |
|
186 | if (options.errorFile) {
|
187 | loggerOptions.errorFile = options.errorFile;
|
188 | }
|
189 |
|
190 | logger = Logger.getLogger(loggerOptions);
|
191 | ipc.setLoggerOptions(loggerOptions);
|
192 | util.setLoggerOptions(loggerOptions);
|
193 |
|
194 | // Remove specific options with no provided value
|
195 | OPTIONS_TO_UNDEFINE.forEach((opt) => {
|
196 | if (typeof options[opt] === 'boolean') {
|
197 | logger.debug(`No value set for option ${opt}. Removing option.`);
|
198 | options[opt] = undefined;
|
199 | }
|
200 | });
|
201 |
|
202 | // Expose options for test code
|
203 | this.options = options;
|
204 |
|
205 | for (let i = 0; i < REQUIRED_OPTIONS.length; i++) {
|
206 | if (!options[REQUIRED_OPTIONS[i]]) {
|
207 | const error = `${REQUIRED_OPTIONS[i]} is a required command line option.`;
|
208 |
|
209 | ipc.send(signals.CLOUD_LIBS_ERROR);
|
210 |
|
211 | util.logError(error, loggerOptions);
|
212 | util.logAndExit(error, 'error', 1);
|
213 | }
|
214 | }
|
215 |
|
216 | if (options.user && !(options.password || options.passwordUrl)) {
|
217 | const error = 'If specifying --user, --password or --password-url is required.';
|
218 |
|
219 | ipc.send(signals.CLOUD_LIBS_ERROR);
|
220 |
|
221 | util.logError(error, loggerOptions);
|
222 | util.logAndExit(error, 'error', 1);
|
223 | }
|
224 |
|
225 | // When running in cloud init, we need to exit so that cloud init can complete and
|
226 | // allow the BIG-IP services to start
|
227 | if (options.background) {
|
228 | logFileName = options.output || DEFAULT_LOG_FILE;
|
229 | logger.info('Spawning child process to do the work. Output will be in', logFileName);
|
230 | util.runInBackgroundAndExit(process, logFileName);
|
231 | }
|
232 |
|
233 | // Log the input, but don't log passwords
|
234 | loggableArgs = argv.slice();
|
235 | for (let i = 0; i < loggableArgs.length; i++) {
|
236 | if (KEYS_TO_MASK.indexOf(loggableArgs[i]) !== -1) {
|
237 | loggableArgs[i + 1] = '*******';
|
238 | }
|
239 | }
|
240 | logger.info(`${loggableArgs[1]} called with`, loggableArgs.join(' '));
|
241 |
|
242 | if (options.singleNic && options.multiNic) {
|
243 | const error = 'Only one of single-nic or multi-nic can be specified.';
|
244 |
|
245 | ipc.send(signals.CLOUD_LIBS_ERROR);
|
246 |
|
247 | util.logError(error, loggerOptions);
|
248 | util.logAndExit(error, 'error', 1);
|
249 | }
|
250 |
|
251 | // Save args in restart script in case we need to reboot to recover from an error
|
252 | util.saveArgs(argv, ARGS_FILE_ID)
|
253 | .then(() => {
|
254 | if (options.waitFor) {
|
255 | logger.info('Waiting for', options.waitFor);
|
256 | return ipc.once(options.waitFor);
|
257 | }
|
258 | return q();
|
259 | })
|
260 | .then(() => {
|
261 | // Whatever we're waiting for is done, so don't wait for
|
262 | // that again in case of a reboot
|
263 | return util.saveArgs(argv, ARGS_FILE_ID, ['--wait-for']);
|
264 | })
|
265 | .then(() => {
|
266 | logger.info('Network setup starting.');
|
267 | ipc.send(signals.NETWORK_RUNNING);
|
268 |
|
269 | if (!options.user) {
|
270 | logger.info('Generating temporary user');
|
271 | return cryptoUtil.nextRandomUser();
|
272 | }
|
273 |
|
274 | return q(
|
275 | {
|
276 | user: options.user,
|
277 | password: options.password || options.passwordUrl
|
278 | }
|
279 | );
|
280 | })
|
281 | .then((credentials) => {
|
282 | randomUser = credentials.user; // we need this info later to delete it
|
283 |
|
284 | // Create the bigIp client object
|
285 | if (optionsForTest.bigIp) {
|
286 | logger.warn('Using test BIG-IP.');
|
287 | bigIp = optionsForTest.bigIp;
|
288 | return q();
|
289 | }
|
290 |
|
291 | bigIp = new BigIp({ loggerOptions });
|
292 |
|
293 | logger.info('Initializing BIG-IP.');
|
294 | return bigIp.init(
|
295 | options.host,
|
296 | credentials.user,
|
297 | credentials.password,
|
298 | {
|
299 | port: options.port,
|
300 | passwordIsUrl: typeof options.passwordUrl !== 'undefined',
|
301 | passwordEncrypted: options.passwordEncrypted
|
302 | }
|
303 | );
|
304 | })
|
305 | .then(() => {
|
306 | logger.info('Waiting for BIG-IP to be ready.');
|
307 | return bigIp.ready();
|
308 | })
|
309 | .then(() => {
|
310 | logger.info('BIG-IP is ready.');
|
311 |
|
312 | if (options.singleNic || options.multiNic) {
|
313 | logger.info('Setting single/multi NIC options.');
|
314 | return bigIp.modify(
|
315 | '/tm/sys/db/provision.1nic',
|
316 | {
|
317 | value: options.singleNic ? 'enable' : 'forced_enable'
|
318 | }
|
319 | )
|
320 | .then((response) => {
|
321 | logger.debug(response);
|
322 |
|
323 | return bigIp.modify(
|
324 | '/tm/sys/db/provision.1nicautoconfig',
|
325 | {
|
326 | value: 'disable'
|
327 | }
|
328 | );
|
329 | })
|
330 | .then((response) => {
|
331 | logger.debug(response);
|
332 |
|
333 | logger.info('Restarting services.');
|
334 | return bigIp.create(
|
335 | '/tm/util/bash',
|
336 | {
|
337 | command: 'run',
|
338 | utilCmdArgs: "-c 'bigstart restart'"
|
339 | },
|
340 | {
|
341 | noWait: true
|
342 | }
|
343 | );
|
344 | })
|
345 | .then((response) => {
|
346 | logger.debug(response);
|
347 |
|
348 | logger.info('Waiting for BIG-IP to be ready after bigstart restart.');
|
349 | return bigIp.ready();
|
350 | });
|
351 | }
|
352 |
|
353 | return q();
|
354 | })
|
355 | .then((response) => {
|
356 | logger.debug(response);
|
357 |
|
358 | const promises = [];
|
359 | let vlanBody;
|
360 |
|
361 | if (vlans.length > 0) {
|
362 | vlans.forEach((vlan) => {
|
363 | if (!vlan.name || !vlan.nic) {
|
364 | q.reject(new Error('Invalid vlan parameters. name and nic are required'));
|
365 | } else {
|
366 | vlanBody = {
|
367 | name: vlan.name,
|
368 | interfaces: [
|
369 | {
|
370 | name: vlan.nic,
|
371 | tagged: !!vlan.tag
|
372 | }
|
373 | ]
|
374 | };
|
375 |
|
376 | if (vlan.mtu) {
|
377 | vlanBody.mtu = vlan.mtu;
|
378 | }
|
379 |
|
380 | if (vlan.tag) {
|
381 | vlanBody.tag = vlan.tag;
|
382 | }
|
383 |
|
384 | promises.push(
|
385 | {
|
386 | promise: bigIp.create,
|
387 | arguments: [
|
388 | '/tm/net/vlan',
|
389 | vlanBody
|
390 | ],
|
391 | // eslint-disable-next-line max-len
|
392 | message: `Creating vlan ${vlan.name} on interface ${vlan.nic} ${(vlan.mtu ? ` mtu ${vlan.mtu}` : '')} ${(vlan.tag ? ` with tag ${vlan.tag}` : ' untagged')}`
|
393 | }
|
394 | );
|
395 | }
|
396 | });
|
397 |
|
398 | return util.callInSerial(bigIp, promises);
|
399 | }
|
400 |
|
401 | return q();
|
402 | })
|
403 | .then((response) => {
|
404 | logger.debug(response);
|
405 |
|
406 | const promises = [];
|
407 |
|
408 | for (let i = 0; i < selfIps.length; i++) {
|
409 | const selfIp = selfIps[i];
|
410 | if (selfIp.trafficGroup) {
|
411 | const trafficGroup = selfIp.trafficGroup;
|
412 | promises.push(createTrafficGroup(bigIp, trafficGroup));
|
413 | }
|
414 | }
|
415 |
|
416 | q.all(promises);
|
417 | })
|
418 | .then((response) => {
|
419 | logger.debug(response);
|
420 |
|
421 | const promises = [];
|
422 | let selfIpBody;
|
423 | let portLockdown;
|
424 |
|
425 | for (let i = 0; i < selfIps.length; i++) {
|
426 | const selfIp = selfIps[i];
|
427 |
|
428 | if (!selfIp.name || !selfIp.address || !selfIp.vlan) {
|
429 | const message = 'Bad self-ip params. name, address, vlan are required';
|
430 | return q.reject(new Error(message));
|
431 | }
|
432 |
|
433 | let address = selfIp.address;
|
434 |
|
435 | if (address.indexOf('/') === -1) {
|
436 | address += DEFAULT_CIDR;
|
437 | }
|
438 |
|
439 | // general terms (default, all, none) have to be single words
|
440 | // per port terms go in an array
|
441 | portLockdown = 'default';
|
442 | if (selfIp.allow) {
|
443 | portLockdown = selfIp.allow.split(/\s+/);
|
444 | if (
|
445 | portLockdown.length === 1 &&
|
446 | portLockdown[0].indexOf(':') === -1
|
447 | ) {
|
448 | portLockdown = portLockdown[0];
|
449 | }
|
450 | }
|
451 |
|
452 | selfIpBody = {
|
453 | address,
|
454 | name: selfIp.name,
|
455 | vlan: `/Common/${selfIp.vlan}`,
|
456 | allowService: portLockdown
|
457 | };
|
458 |
|
459 | // eslint-disable-next-line max-len
|
460 | let message = `Creating self IP ${selfIp.name} with address ${address} on vlan ${selfIp.vlan} allowing ${(selfIp.allow ? selfIp.allow : 'default')}`;
|
461 |
|
462 | // If traffic group provided, add to create call
|
463 | if (selfIp.trafficGroup) {
|
464 | selfIpBody.trafficGroup = selfIp.trafficGroup;
|
465 | message = `${message} in traffic group ${selfIp.trafficGroup}`;
|
466 | }
|
467 |
|
468 | const continueOnErrorMessage = /(Traffic group \(.+?\) does not exist)/;
|
469 | promises.push(
|
470 | {
|
471 | promise: bigIp.create,
|
472 | arguments: [
|
473 | '/tm/net/self',
|
474 | selfIpBody,
|
475 | undefined,
|
476 | {
|
477 | maxRetries: 60,
|
478 | retryIntervalMs: 1000,
|
479 | continueOnErrorMessage
|
480 | }
|
481 | ],
|
482 | message
|
483 | }
|
484 | );
|
485 | }
|
486 |
|
487 | return promises.length > 0 ? util.callInSerial(bigIp, promises) : q();
|
488 | })
|
489 | .then((response) => {
|
490 | logger.debug(response);
|
491 |
|
492 | // BIG-IQs must set their Discovery Address
|
493 | if (bigIp.isBigIq()) {
|
494 | if (options.discoveryAddress) {
|
495 | logger.info('Setting BIG-IQ Discovery Address to discovery-address option.');
|
496 | return q(options.discoveryAddress);
|
497 | }
|
498 |
|
499 | return bigIp.list('/tm/sys/management-ip')
|
500 | .then((mgmtIp) => {
|
501 | logger.info('Setting BIG-IQ Discovery Address to management-ip address');
|
502 | return q(mgmtIp[0].name.split('/')[0]);
|
503 | });
|
504 | }
|
505 |
|
506 | return q();
|
507 | })
|
508 | .then((response) => {
|
509 | if (bigIp.isBigIq() && response) {
|
510 | logger.info(`BIG-IQ Discovery Address: ${response}`);
|
511 | return bigIp.replace(
|
512 | '/shared/identified-devices/config/discovery',
|
513 | {
|
514 | discoveryAddress: response
|
515 | },
|
516 | undefined,
|
517 | {
|
518 | maxRetries: 60,
|
519 | retryIntervalMs: 1000,
|
520 | continueOnErrorMessage:
|
521 | 'Address does not match a configured self-ip'
|
522 | }
|
523 | );
|
524 | }
|
525 | return q();
|
526 | })
|
527 | .then((response) => {
|
528 | logger.debug(response);
|
529 |
|
530 | if (options.localOnly) {
|
531 | logger.info('Creating LOCAL_ONLY partition.');
|
532 | return bigIp.create(
|
533 | '/tm/sys/folder',
|
534 | {
|
535 | name: 'LOCAL_ONLY',
|
536 | partition: '/',
|
537 | deviceGroup: 'none',
|
538 | trafficGroup: 'traffic-group-local-only'
|
539 | }
|
540 | );
|
541 | }
|
542 |
|
543 | return q();
|
544 | })
|
545 | .then((response) => {
|
546 | logger.debug(response);
|
547 |
|
548 | let routeBody;
|
549 |
|
550 | if (options.defaultGw) {
|
551 | logger.info(`Setting default gateway ${options.defaultGw}`);
|
552 |
|
553 | routeBody = {
|
554 | name: 'default',
|
555 | gw: options.defaultGw
|
556 | };
|
557 |
|
558 | if (options.localOnly) {
|
559 | routeBody.partition = 'LOCAL_ONLY';
|
560 | routeBody.network = 'default';
|
561 | }
|
562 |
|
563 | return bigIp.create(
|
564 | '/tm/net/route',
|
565 | routeBody
|
566 | );
|
567 | }
|
568 |
|
569 | return q();
|
570 | })
|
571 | .then((response) => {
|
572 | logger.debug(response);
|
573 |
|
574 | const promises = [];
|
575 | let routeBody;
|
576 |
|
577 | for (let i = 0; i < mgmtRoutes.length; i++) {
|
578 | const route = mgmtRoutes[i];
|
579 | if (!route.name || !route.gw || !route.network) {
|
580 | const message
|
581 | = 'Bad management route params. Name, gateway, network required';
|
582 | return q.reject(new Error(message));
|
583 | }
|
584 |
|
585 | let network = route.network;
|
586 | if (network.indexOf('/') === -1) {
|
587 | network += DEFAULT_CIDR;
|
588 | }
|
589 |
|
590 | routeBody = {
|
591 | network,
|
592 | name: route.name,
|
593 | gateway: route.gw
|
594 | };
|
595 |
|
596 | promises.push(
|
597 | {
|
598 | promise: bigIp.create,
|
599 | arguments: [
|
600 | '/tm/sys/management-route',
|
601 | routeBody
|
602 | ],
|
603 | message:
|
604 | `Creating management route ${route.name} with gateway ${route.gw}`
|
605 | }
|
606 | );
|
607 | }
|
608 |
|
609 | return promises.length > 0 ? util.callInSerial(bigIp, promises) : q();
|
610 | })
|
611 | .then((response) => {
|
612 | logger.debug(response);
|
613 |
|
614 | const promises = [];
|
615 | let routeBody;
|
616 |
|
617 | for (let i = 0; i < routes.length; i++) {
|
618 | const route = routes[i];
|
619 | if (!route.name || !route.network || (!route.gw && !route.interface)) {
|
620 | return q.reject(new Error(
|
621 | 'Bad route params. Name, network, and (gateway or interface) required'
|
622 | ));
|
623 | }
|
624 |
|
625 | if (route.gw && route.interface) {
|
626 | return q.reject(new Error(
|
627 | 'Bad route params. Should provide only 1 of gateway or interface'
|
628 | ));
|
629 | }
|
630 |
|
631 | let network = route.network;
|
632 | if (network.indexOf('/') === -1) {
|
633 | network += DEFAULT_CIDR;
|
634 | }
|
635 |
|
636 | routeBody = {
|
637 | network,
|
638 | name: route.name
|
639 | };
|
640 |
|
641 | let message = `Creating route ${route.name} with`;
|
642 |
|
643 | if (route.gw) {
|
644 | routeBody.gw = route.gw;
|
645 | message = `${message} gateway ${route.gw}`;
|
646 | } else if (route.interface) {
|
647 | routeBody.interface = route.interface;
|
648 | message = `${message} interface ${route.interface}`;
|
649 | }
|
650 |
|
651 | promises.push(
|
652 | {
|
653 | promise: bigIp.create,
|
654 | arguments: [
|
655 | '/tm/net/route',
|
656 | routeBody
|
657 | ],
|
658 | message
|
659 | }
|
660 | );
|
661 | }
|
662 |
|
663 | return promises.length > 0 ? util.callInSerial(bigIp, promises) : q();
|
664 | })
|
665 | .then((response) => {
|
666 | logger.debug(response);
|
667 | logger.info('Saving config.');
|
668 | return bigIp.save();
|
669 | })
|
670 | .then((response) => {
|
671 | logger.debug(response);
|
672 |
|
673 | if (options.forceReboot) {
|
674 | // After reboot, we just want to send our done signal,
|
675 | // in case any other scripts are waiting on us. So, modify
|
676 | // the saved args for that
|
677 | const ARGS_TO_STRIP = util.getArgsToStripDuringForcedReboot(options);
|
678 | return util.saveArgs(argv, ARGS_FILE_ID, ARGS_TO_STRIP)
|
679 | .then(() => {
|
680 | logger.info('Rebooting and exiting. Will continue after reboot.');
|
681 | return util.reboot(bigIp);
|
682 | });
|
683 | }
|
684 |
|
685 | return q();
|
686 | })
|
687 | .catch((err) => {
|
688 | let message;
|
689 |
|
690 | if (!err) {
|
691 | message = 'unknown reason';
|
692 | } else {
|
693 | message = err.message;
|
694 | }
|
695 |
|
696 | ipc.send(signals.CLOUD_LIBS_ERROR);
|
697 |
|
698 | const error = `Network setup failed: ${message}`;
|
699 | util.logError(error, loggerOptions);
|
700 | util.logAndExit(error, 'error', 1);
|
701 |
|
702 | exiting = true;
|
703 | return q();
|
704 | })
|
705 | .done((response) => {
|
706 | logger.debug(response);
|
707 |
|
708 | if (!options.user) {
|
709 | logger.info('Deleting temporary user');
|
710 | util.deleteUser(randomUser);
|
711 | }
|
712 |
|
713 | if (!options.forceReboot) {
|
714 | util.deleteArgs(ARGS_FILE_ID);
|
715 |
|
716 | if (!exiting) {
|
717 | logger.info('BIG-IP network setup complete.');
|
718 | ipc.send(options.signal || signals.NETWORK_DONE);
|
719 | }
|
720 |
|
721 | if (cb) {
|
722 | cb();
|
723 | }
|
724 | if (!exiting) {
|
725 | util.logAndExit('Network setup finished.');
|
726 | }
|
727 | } else if (cb) {
|
728 | cb();
|
729 | }
|
730 | });
|
731 |
|
732 | // If another script has signaled an error, exit, marking ourselves as DONE
|
733 | ipc.once(signals.CLOUD_LIBS_ERROR)
|
734 | .then(() => {
|
735 | ipc.send(options.signal || signals.NETWORK_DONE);
|
736 | util.logAndExit('ERROR signaled from other script. Exiting');
|
737 | });
|
738 |
|
739 | // If we reboot, exit - otherwise cloud providers won't know we're done.
|
740 | // But, if we're the one doing the reboot, we'll exit on our own through
|
741 | // the normal path.
|
742 | if (!options.forceReboot) {
|
743 | ipc.once('REBOOT')
|
744 | .then(() => {
|
745 | // Make sure the last log message is flushed before exiting.
|
746 | util.logAndExit('REBOOT signaled. Exiting.');
|
747 | });
|
748 | }
|
749 | } catch (err) {
|
750 | if (logger) {
|
751 | logger.error('Network setup error:', err);
|
752 | }
|
753 |
|
754 | if (cb) {
|
755 | cb();
|
756 | }
|
757 | }
|
758 | }
|
759 | };
|
760 |
|
761 | /**
|
762 | * Creates a Traffic Group on BigIP, if the Traffic Group does not exist
|
763 | *
|
764 | * @param {Object} bigIp - bigIp client object
|
765 | * @param {String} trafficGroup - Traffic Group name
|
766 | *
|
767 | * @returns {Promise} Promise that will be resolved when Traffic Group is created,
|
768 | * already exists, or if an error occurs.
|
769 | */
|
770 | function createTrafficGroup(bigIp, trafficGroup) {
|
771 | let createGroup = true;
|
772 | bigIp.list('/tm/cm/traffic-group')
|
773 | .then((response) => {
|
774 | response.forEach((group) => {
|
775 | if (group.name === trafficGroup) {
|
776 | createGroup = false;
|
777 | }
|
778 | });
|
779 | if (createGroup) {
|
780 | return bigIp.create(
|
781 | '/tm/cm/traffic-group',
|
782 | {
|
783 | name: trafficGroup,
|
784 | partition: '/Common'
|
785 | }
|
786 | );
|
787 | }
|
788 | return q();
|
789 | })
|
790 | .catch((err) => {
|
791 | return q.reject(err);
|
792 | });
|
793 | }
|
794 |
|
795 | module.exports = runner;
|
796 |
|
797 | // If we're called from the command line, run
|
798 | // This allows for test code to call us as a module
|
799 | if (!module.parent) {
|
800 | runner.run(process.argv);
|
801 | }
|
802 | }());
|