@itentialopensource/adapter-meraki 0.7.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +8 -0
- package/README.md +84 -11
- package/adapter.js +469 -1
- package/adapterBase.js +24 -2
- package/entities/.generic/action.json +5 -5
- package/error.json +6 -0
- package/package.json +18 -13
- package/pronghorn.json +192 -0
- package/propertiesSchema.json +15 -0
- package/refs?service=git-upload-pack +0 -0
- package/report/updateReport1642739939352.json +95 -0
- package/sampleProperties.json +4 -1
- package/test/integration/adapterTestIntegration.js +9 -1
- package/test/unit/adapterBaseTestUnit.js +5 -2
- package/test/unit/adapterTestUnit.js +618 -3
- package/utils/addAuth.js +94 -0
- package/utils/basicGet.js +1 -14
- package/utils/entitiesToDB.js +224 -0
- package/utils/modify.js +1 -1
- package/utils/patches2bundledDeps.js +90 -0
- package/utils/removeHooks.js +20 -0
- package/utils/tbScript.js +14 -8
- package/utils/tbUtils.js +98 -19
- package/utils/troubleshootingAdapter.js +2 -26
package/CHANGELOG.md
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
|
2
|
+
## 0.8.0 [01-21-2022]
|
3
|
+
|
4
|
+
* migration to the latest foundation and broker ready
|
5
|
+
|
6
|
+
See merge request itentialopensource/adapters/sd-wan/adapter-meraki!15
|
7
|
+
|
8
|
+
---
|
9
|
+
|
2
10
|
## 0.7.3 [06-08-2021]
|
3
11
|
|
4
12
|
* Update Pronghorn.json and adapter.js for passing body directly to api call
|
package/README.md
CHANGED
@@ -125,7 +125,8 @@ This section defines **all** the properties that are available for the adapter,
|
|
125
125
|
},
|
126
126
|
"healthcheck": {
|
127
127
|
"type": "startup",
|
128
|
-
"frequency": 300000
|
128
|
+
"frequency": 300000,
|
129
|
+
"query_object": {}
|
129
130
|
},
|
130
131
|
"request": {
|
131
132
|
"number_redirects": 0,
|
@@ -250,6 +251,7 @@ The healthcheck properties defines the API that runs the healthcheck to tell the
|
|
250
251
|
| ------- | ------- |
|
251
252
|
| type | Required. The type of health check to run. |
|
252
253
|
| frequency | Required if intermittent. Defines how often the health check should run. Measured in milliseconds. Default is 300000.|
|
254
|
+
| query_object | Query parameters to be added to the adapter healthcheck call.|
|
253
255
|
|
254
256
|
### Request Properties
|
255
257
|
|
@@ -398,7 +400,7 @@ There are several node scripts that now accompany the adapter. These scripts are
|
|
398
400
|
| npm run troubleshoot | Provides a way to troubleshoot the adapter - runs connectivity, healthcheck and basic get.|
|
399
401
|
| npm run connectivity | Provides a connectivity check to the Meraki system.|
|
400
402
|
| npm run healthcheck | Checks whether the configured healthcheck call works to Meraki.|
|
401
|
-
| npm run basicget | Checks whether the
|
403
|
+
| npm run basicget | Checks whether the basic get calls works to Meraki.|
|
402
404
|
|
403
405
|
## Installing an Itential Product Adapter
|
404
406
|
|
@@ -412,21 +414,21 @@ if the @itentialopensource directory does not exist, create it:
|
|
412
414
|
mkdir @itentialopensource
|
413
415
|
```
|
414
416
|
|
415
|
-
|
417
|
+
2. Clone the adapter into your IAP environment.
|
416
418
|
|
417
419
|
```bash
|
418
420
|
cd \@itentialopensource
|
419
421
|
git clone git@gitlab.com:\@itentialopensource/adapters/adapter-meraki
|
420
422
|
```
|
421
423
|
|
422
|
-
|
424
|
+
3. Run the adapter install script.
|
423
425
|
|
424
426
|
```bash
|
425
427
|
cd adapter-meraki
|
426
428
|
npm run adapter:install
|
427
429
|
```
|
428
430
|
|
429
|
-
|
431
|
+
4. Restart IAP
|
430
432
|
|
431
433
|
```bash
|
432
434
|
systemctl restart pronghorn
|
@@ -446,7 +448,7 @@ Depending on where your code is located, this process is different.
|
|
446
448
|
Adapter should be placed into: /opt/pronghorn/current/node_modules/\@itentialopensource
|
447
449
|
```
|
448
450
|
|
449
|
-
|
451
|
+
2. Follow Steps 3-4 (above) to install an Itential adapter to load your properties, dependencies and restart IAP.
|
450
452
|
|
451
453
|
## Using this Adapter
|
452
454
|
|
@@ -494,7 +496,7 @@ The `verifyCapability` call verifies the adapter can perform the provided action
|
|
494
496
|
verifyCapability(entityType, actionType, entityId, callback)
|
495
497
|
```
|
496
498
|
|
497
|
-
The `updateEntityCache` call will update the entity cache.
|
499
|
+
The `updateEntityCache` call will update the entity cache.
|
498
500
|
```js
|
499
501
|
updateEntityCache()
|
500
502
|
```
|
@@ -548,6 +550,77 @@ getQueue(callback)
|
|
548
550
|
|
549
551
|
Specific adapter calls are built based on the API of the Meraki. The Adapter Builder creates the proper method comments for generating JS-DOC for the adapter. This is the best way to get information on the calls.
|
550
552
|
|
553
|
+
|
554
|
+
## Extending/Enhancing the Adapter
|
555
|
+
|
556
|
+
### Adding a Second Instance of an Adapter
|
557
|
+
|
558
|
+
You can add a second instance of this adapter without adding new code on the file system. To do this go into the IAP Admin Essentials and add a new service config for this adapter. The two instances of the adapter should have unique ids. In addition, they should point to different instances of the other system. For example, they should be configured to talk to different hosts.
|
559
|
+
|
560
|
+
### Adding Adapter Calls
|
561
|
+
|
562
|
+
There are multiple ways to add calls to an existing adapter.
|
563
|
+
|
564
|
+
The easiest way would be to use the Adapter Builder update process. This process takes in a Swagger or OpenAPI document, allows you to select the calls you want to add and then generates a zip file that can be used to update the adapter. Once you have the zip file simple put it in the adapter direcctory and execute `npm run adapter:update`.
|
565
|
+
|
566
|
+
```bash
|
567
|
+
mv updatePackage.zip adapter-meraki
|
568
|
+
cd adapter-meraki
|
569
|
+
npm run adapter:update
|
570
|
+
```
|
571
|
+
|
572
|
+
If you do not have a Swagger or OpenAPI document, you can use a Postman Collection and convert that to an OpenAPI document using APIMatic and then follow the first process.
|
573
|
+
|
574
|
+
If you want to manually update the adapter that can also be done the key thing is to make sure you update all of the right files. Within the entities directory you will find 1 or more entities. You can create a new entity or add to an existing entity. Each entity has an action.json file, any new call will need to be put in the action.json file. It will also need to be added to the enum for the ph_request_type in the appropriate schema files. Once this configuration is complete you will need to add the call to the adapter.js file and in order to make it available as a workflow task in IAP, it should also be added to the pronghorn.json file. You can optionally add it to the unit and integration test files. There is more information on how to work on each of these files in the Adapter Technical Resources on Dev Site [HERE](https://developer.itential.io/adapters-resources/)
|
575
|
+
|
576
|
+
```text
|
577
|
+
Files to update
|
578
|
+
* entities/<entity>/action.json: add an action
|
579
|
+
* entities/<entity>/schema.json (or the schema defined on the action): add action to the enum for ph_request_type
|
580
|
+
* adapter.js: add the new method and make sure it calls the proper entity and action
|
581
|
+
* pronghorn.json: add the new method
|
582
|
+
* test/unit/adapterTestUnit.js (optional but best practice): add unit test(s) - function is there, any required parameters error when not passed in
|
583
|
+
* test/integration/adapterTestIntegration.js (optional but best practice): add integration test
|
584
|
+
```
|
585
|
+
|
586
|
+
### Adding Adapter Properties
|
587
|
+
|
588
|
+
While changing adapter properties is done in the service instance configuration section of IAP, adding properties has to be done in the adapter. To add a property you should edit the propertiesSchema.json with the proper information for the property. In addition, you should modify the sampleProperties to have the new property in it.
|
589
|
+
|
590
|
+
```text
|
591
|
+
Files to update
|
592
|
+
* propertiesSchema.json: add the new property and how it is defined
|
593
|
+
* sampleProperties: add the new property with a default value
|
594
|
+
* test/unit/adapterTestUnit.js (optional but best practice): add the property to the global properties
|
595
|
+
* test/integration/adapterTestIntegration.js (optional but best practice): add the property to the global properties
|
596
|
+
```
|
597
|
+
|
598
|
+
### Changing Adapter Authentication
|
599
|
+
|
600
|
+
Often an adapter is built before knowing the authentication and authentication process can also change over time. The adapter supports many different kinds of authentication but it does require configuration. Some forms of authentication can be defined entirely with the adapter properties but others require configuration.
|
601
|
+
|
602
|
+
```text
|
603
|
+
Files to update
|
604
|
+
* entities/<entity>/action.json: change the getToken action as needed
|
605
|
+
* entities/<entity>/schemaTokenReq.json: add input parameters (external name is name in other system)
|
606
|
+
* entities/<entity>/schemaTokenResp.json: add response parameters (external name is name in other system)
|
607
|
+
* propertiesSchema.json: add any new property and how it is defined
|
608
|
+
* sampleProperties: add any new property with a default value
|
609
|
+
* test/unit/adapterTestUnit.js (optional but best practice): add the property to the global properties
|
610
|
+
* test/integration/adapterTestIntegration.js (optional but best practice): add the property to the global properties
|
611
|
+
```
|
612
|
+
|
613
|
+
### Enhancing Adapter Integration Tests
|
614
|
+
|
615
|
+
The adapter integration tests are written to be able to test in either stub (standalone) mode or integrated to the other system. However, if integrating to the other system, you may need to provide better data than what the adapter provides by default as that data is likely to fail for create and update. To provide better data, edit the adapter integration test file. Make sure you do not remove the marker and keep custom code below the marker so you do not impact future migrations. Once the edits are complete, run the integration test as it instructs you to above. When you run integrated to the other system, you can also save mockdata for future use by changing the isSaveMockData flag to true.
|
616
|
+
|
617
|
+
```text
|
618
|
+
Files to update
|
619
|
+
* test/integration/adapterTestIntegration.js: add better data for the create and update calls so that they will not fail.
|
620
|
+
```
|
621
|
+
|
622
|
+
As mentioned previously, for most of these changes as well as other possible changes, there is more information on how to work on an adapter in the Adapter Technical Resources on Dev Site [HERE](https://developer.itential.io/adapters-resources/)
|
623
|
+
|
551
624
|
## Troubleshooting the Adapter
|
552
625
|
|
553
626
|
Run `npm run troubleshoot` to start the interactive troubleshooting process. The command allows user to verify and update connection, authentication as well as healthcheck configuration. After that it will test these properties by sending HTTP request to the endpoint. If the tests pass, it will persist these changes into IAP.
|
@@ -566,26 +639,26 @@ User also have the option to run individual command to perform specific test
|
|
566
639
|
npm run troubleshoot
|
567
640
|
```
|
568
641
|
|
569
|
-
|
642
|
+
2. Verify the adapter properties are set up correctly.
|
570
643
|
|
571
644
|
```text
|
572
645
|
Go into the Itential Platform GUI and verify/update the properties
|
573
646
|
```
|
574
647
|
|
575
|
-
|
648
|
+
3. Verify there is connectivity between the Itential Platform Server and Meraki Server.
|
576
649
|
|
577
650
|
```text
|
578
651
|
ping the ip address of Meraki server
|
579
652
|
try telnet to the ip address port of Meraki
|
580
653
|
```
|
581
654
|
|
582
|
-
|
655
|
+
4. Verify the credentials provided for Meraki.
|
583
656
|
|
584
657
|
```text
|
585
658
|
login to Meraki using the provided credentials
|
586
659
|
```
|
587
660
|
|
588
|
-
|
661
|
+
5. Verify the API of the call utilized for Meraki Healthcheck.
|
589
662
|
|
590
663
|
```text
|
591
664
|
Go into the Itential Platform GUI and verify/update the properties
|
package/adapter.js
CHANGED
@@ -85,7 +85,7 @@ class Meraki extends AdapterBaseCl {
|
|
85
85
|
* @getWorkflowFunctions
|
86
86
|
*/
|
87
87
|
getWorkflowFunctions(inIgnore) {
|
88
|
-
let myIgnore = [];
|
88
|
+
let myIgnore = ['hasEntities', 'hasDevices'];
|
89
89
|
if (!inIgnore && Array.isArray(inIgnore)) {
|
90
90
|
myIgnore = inIgnore;
|
91
91
|
} else if (!inIgnore && typeof inIgnore === 'string') {
|
@@ -249,6 +249,24 @@ class Meraki extends AdapterBaseCl {
|
|
249
249
|
}
|
250
250
|
}
|
251
251
|
|
252
|
+
/**
|
253
|
+
* @summary moves entites into Mongo DB
|
254
|
+
*
|
255
|
+
* @function moveEntitiesToDB
|
256
|
+
* @param {getCallback} callback - a callback function to return the result (Generics)
|
257
|
+
* or the error
|
258
|
+
*/
|
259
|
+
moveEntitiesToDB(callback) {
|
260
|
+
const origin = `${this.id}-adapter-moveEntitiesToDB`;
|
261
|
+
log.trace(origin);
|
262
|
+
try {
|
263
|
+
return super.moveEntitiesToDB(callback);
|
264
|
+
} catch (err) {
|
265
|
+
log.error(`${origin}: ${err}`);
|
266
|
+
return callback(null, err);
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
252
270
|
/**
|
253
271
|
* @summary Determines if this adapter supports the specific entity
|
254
272
|
*
|
@@ -528,6 +546,456 @@ class Meraki extends AdapterBaseCl {
|
|
528
546
|
}
|
529
547
|
}
|
530
548
|
|
549
|
+
/* BROKER CALLS */
|
550
|
+
/**
|
551
|
+
* @summary Determines if this adapter supports any in a list of entities
|
552
|
+
*
|
553
|
+
* @function hasEntities
|
554
|
+
* @param {String} entityType - the entity type to check for
|
555
|
+
* @param {Array} entityList - the list of entities we are looking for
|
556
|
+
*
|
557
|
+
* @param {Callback} callback - A map where the entity is the key and the
|
558
|
+
* value is true or false
|
559
|
+
*/
|
560
|
+
hasEntities(entityType, entityList, callback) {
|
561
|
+
const origin = `${this.id}-adapter-hasEntities`;
|
562
|
+
log.trace(origin);
|
563
|
+
|
564
|
+
switch (entityType) {
|
565
|
+
case 'Device':
|
566
|
+
return this.hasDevices(entityList, callback);
|
567
|
+
default:
|
568
|
+
return callback(null, `${this.id} does not support entity ${entityType}`);
|
569
|
+
}
|
570
|
+
}
|
571
|
+
|
572
|
+
/**
|
573
|
+
* @summary Helper method for hasEntities for the specific device case
|
574
|
+
*
|
575
|
+
* @param {Array} deviceList - array of unique device identifiers
|
576
|
+
* @param {Callback} callback - A map where the device is the key and the
|
577
|
+
* value is true or false
|
578
|
+
*/
|
579
|
+
hasDevices(deviceList, callback) {
|
580
|
+
const origin = `${this.id}-adapter-hasDevices`;
|
581
|
+
log.trace(origin);
|
582
|
+
|
583
|
+
const findings = deviceList.reduce((map, device) => {
|
584
|
+
// eslint-disable-next-line no-param-reassign
|
585
|
+
map[device] = false;
|
586
|
+
log.debug(`In reduce: ${JSON.stringify(map)}`);
|
587
|
+
return map;
|
588
|
+
}, {});
|
589
|
+
const apiCalls = deviceList.map((device) => new Promise((resolve) => {
|
590
|
+
this.getDevice(device, (result, error) => {
|
591
|
+
if (error) {
|
592
|
+
log.debug(`In map error: ${JSON.stringify(device)}`);
|
593
|
+
return resolve({ name: device, found: false });
|
594
|
+
}
|
595
|
+
log.debug(`In map: ${JSON.stringify(device)}`);
|
596
|
+
return resolve({ name: device, found: true });
|
597
|
+
});
|
598
|
+
}));
|
599
|
+
Promise.all(apiCalls).then((results) => {
|
600
|
+
results.forEach((device) => {
|
601
|
+
findings[device.name] = device.found;
|
602
|
+
});
|
603
|
+
log.debug(`FINDINGS: ${JSON.stringify(findings)}`);
|
604
|
+
return callback(findings);
|
605
|
+
}).catch((errors) => {
|
606
|
+
log.error('Unable to do device lookup.');
|
607
|
+
return callback(null, { code: 503, message: 'Unable to do device lookup.', error: errors });
|
608
|
+
});
|
609
|
+
}
|
610
|
+
|
611
|
+
/**
|
612
|
+
* @summary Get Appliance that match the deviceName
|
613
|
+
*
|
614
|
+
* @function getDevice
|
615
|
+
* @param {String} deviceName - the deviceName to find (required)
|
616
|
+
*
|
617
|
+
* @param {getCallback} callback - a callback function to return the result
|
618
|
+
* (appliance) or the error
|
619
|
+
*/
|
620
|
+
getDevice(deviceName, callback) {
|
621
|
+
const meth = 'adapter-getDevice';
|
622
|
+
const origin = `${this.id}-${meth}`;
|
623
|
+
log.trace(origin);
|
624
|
+
|
625
|
+
if (this.suspended && this.suspendMode === 'error') {
|
626
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'AD.600', [], null, null, null);
|
627
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
628
|
+
return callback(null, errorObj);
|
629
|
+
}
|
630
|
+
|
631
|
+
/* HERE IS WHERE YOU VALIDATE DATA */
|
632
|
+
if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
|
633
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
|
634
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
635
|
+
return callback(null, errorObj);
|
636
|
+
}
|
637
|
+
|
638
|
+
try {
|
639
|
+
// need to get the device so we can convert the deviceName to an id
|
640
|
+
// !! if we can do a lookup by name the getDevicesFiltered may not be necessary
|
641
|
+
const opts = {
|
642
|
+
filter: {
|
643
|
+
name: deviceName
|
644
|
+
}
|
645
|
+
};
|
646
|
+
return this.getDevicesFiltered(opts, (devs, ferr) => {
|
647
|
+
// if we received an error or their is no response on the results return an error
|
648
|
+
if (ferr) {
|
649
|
+
return callback(null, ferr);
|
650
|
+
}
|
651
|
+
if (devs.list.length < 1) {
|
652
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
|
653
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
654
|
+
return callback(null, errorObj);
|
655
|
+
}
|
656
|
+
// get the uuid from the device
|
657
|
+
const { uuid } = devs.list[0];
|
658
|
+
|
659
|
+
// !! using Generic makes it easier on the Adapter Builder (just need to change the path)
|
660
|
+
// !! you can also replace with a specific call if that is easier
|
661
|
+
const uriPath = `/call/toget/device/${uuid}`;
|
662
|
+
return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
|
663
|
+
// if we received an error or their is no response on the results return an error
|
664
|
+
if (error) {
|
665
|
+
return callback(null, error);
|
666
|
+
}
|
667
|
+
if (!result.response || !result.response.applianceMo) {
|
668
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', ['getDevice'], null, null, null);
|
669
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
670
|
+
return callback(null, errorObj);
|
671
|
+
}
|
672
|
+
|
673
|
+
// return the response
|
674
|
+
// !! format the data we send back
|
675
|
+
// !! these fields are config manager fields you need to map to the data we receive
|
676
|
+
const thisDevice = result.response;
|
677
|
+
thisDevice.name = thisDevice.systemName;
|
678
|
+
thisDevice.ostype = `System-${thisDevice.systemType}`;
|
679
|
+
thisDevice.port = thisDevice.systemPort;
|
680
|
+
thisDevice.ipaddress = thisDevice.systemIP;
|
681
|
+
return callback(thisDevice);
|
682
|
+
});
|
683
|
+
});
|
684
|
+
} catch (ex) {
|
685
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
|
686
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
687
|
+
return callback(null, errorObj);
|
688
|
+
}
|
689
|
+
}
|
690
|
+
|
691
|
+
/**
|
692
|
+
* @summary Get Appliances that match the filter
|
693
|
+
*
|
694
|
+
* @function getDevicesFiltered
|
695
|
+
* @param {Object} options - the data to use to filter the appliances (optional)
|
696
|
+
*
|
697
|
+
* @param {getCallback} callback - a callback function to return the result
|
698
|
+
* (appliances) or the error
|
699
|
+
*/
|
700
|
+
getDevicesFiltered(options, callback) {
|
701
|
+
const meth = 'adapter-getDevicesFiltered';
|
702
|
+
const origin = `${this.id}-${meth}`;
|
703
|
+
log.trace(origin);
|
704
|
+
|
705
|
+
// verify the required fields have been provided
|
706
|
+
if (options === undefined || options === null || options === '' || options.length === 0) {
|
707
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['options'], null, null, null);
|
708
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
709
|
+
return callback(null, errorObj);
|
710
|
+
}
|
711
|
+
log.debug(`Device Filter Options: ${JSON.stringify(options)}`);
|
712
|
+
|
713
|
+
// TODO - get pagination working
|
714
|
+
// const nextToken = options.start;
|
715
|
+
// const maxResults = options.limit;
|
716
|
+
|
717
|
+
// set up the filter of Device Names
|
718
|
+
let filterName = [];
|
719
|
+
if (options && options.filter && options.filter.name) {
|
720
|
+
// when this hack is removed, remove the lint ignore above
|
721
|
+
if (Array.isArray(options.filter.name)) {
|
722
|
+
// eslint-disable-next-line prefer-destructuring
|
723
|
+
filterName = options.filter.name;
|
724
|
+
} else {
|
725
|
+
filterName = [options.filter.name];
|
726
|
+
}
|
727
|
+
}
|
728
|
+
|
729
|
+
// TODO - get sort and order working
|
730
|
+
/*
|
731
|
+
if (options && options.sort) {
|
732
|
+
reqObj.uriOptions.sort = JSON.stringify(options.sort);
|
733
|
+
}
|
734
|
+
if (options && options.order) {
|
735
|
+
reqObj.uriOptions.order = options.order;
|
736
|
+
}
|
737
|
+
*/
|
738
|
+
try {
|
739
|
+
// !! using Generic makes it easier on the Adapter Builder (just need to change the path)
|
740
|
+
// !! you can also replace with a specific call if that is easier
|
741
|
+
const uriPath = '/call/toget/devices';
|
742
|
+
return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
|
743
|
+
// if we received an error or their is no response on the results return an error
|
744
|
+
if (error) {
|
745
|
+
return callback(null, error);
|
746
|
+
}
|
747
|
+
if (!result.response) {
|
748
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', ['getDevicesFiltered'], null, null, null);
|
749
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
750
|
+
return callback(null, errorObj);
|
751
|
+
}
|
752
|
+
|
753
|
+
// !! go through the response - may have to look for sub object
|
754
|
+
// handle an array of devices
|
755
|
+
if (Array.isArray(result.response)) {
|
756
|
+
const myDevices = [];
|
757
|
+
|
758
|
+
for (let d = 0; d < result.response.length; d += 1) {
|
759
|
+
// !! format the data we send back
|
760
|
+
// !! these fields are config manager fields you need to map to the data we receive
|
761
|
+
const thisDevice = result.response;
|
762
|
+
thisDevice.name = thisDevice.systemName;
|
763
|
+
thisDevice.ostype = `System-${thisDevice.systemType}`;
|
764
|
+
thisDevice.port = thisDevice.systemPort;
|
765
|
+
thisDevice.ipaddress = thisDevice.systemIP;
|
766
|
+
|
767
|
+
// if there is no filter - return the device
|
768
|
+
if (filterName.length === 0) {
|
769
|
+
myDevices.push(thisDevice);
|
770
|
+
} else {
|
771
|
+
// if we have to match a filter
|
772
|
+
let found = false;
|
773
|
+
for (let f = 0; f < filterName.length; f += 1) {
|
774
|
+
if (thisDevice.name.indexOf(filterName[f]) >= 0) {
|
775
|
+
found = true;
|
776
|
+
break;
|
777
|
+
}
|
778
|
+
}
|
779
|
+
// matching device
|
780
|
+
if (found) {
|
781
|
+
myDevices.push(thisDevice);
|
782
|
+
}
|
783
|
+
}
|
784
|
+
}
|
785
|
+
log.debug(`${origin}: Found #${myDevices.length} devices.`);
|
786
|
+
log.debug(`Devices: ${JSON.stringify(myDevices)}`);
|
787
|
+
return callback({ total: myDevices.length, list: myDevices });
|
788
|
+
}
|
789
|
+
// handle a single device response
|
790
|
+
// !! format the data we send back
|
791
|
+
// !! these fields are config manager fields you need to map to the data we receive
|
792
|
+
const thisDevice = result.response;
|
793
|
+
thisDevice.name = thisDevice.systemName;
|
794
|
+
thisDevice.ostype = `System-${thisDevice.systemType}`;
|
795
|
+
thisDevice.port = thisDevice.systemPort;
|
796
|
+
thisDevice.ipaddress = thisDevice.systemIP;
|
797
|
+
|
798
|
+
// if there is no filter - return the device
|
799
|
+
if (filterName.length === 0) {
|
800
|
+
log.debug(`${origin}: Found #1 device.`);
|
801
|
+
log.debug(`Device: ${JSON.stringify(thisDevice)}`);
|
802
|
+
return callback({ total: 1, list: [thisDevice] });
|
803
|
+
}
|
804
|
+
|
805
|
+
// if there is a filter need to check for matching device
|
806
|
+
let found = false;
|
807
|
+
for (let f = 0; f < filterName.length; f += 1) {
|
808
|
+
if (thisDevice.name.indexOf(filterName[f]) >= 0) {
|
809
|
+
found = true;
|
810
|
+
break;
|
811
|
+
}
|
812
|
+
}
|
813
|
+
// matching device
|
814
|
+
if (found) {
|
815
|
+
log.debug(`${origin}: Found #1 device.`);
|
816
|
+
log.debug(`Device Found: ${JSON.stringify(thisDevice)}`);
|
817
|
+
return callback({ total: 1, list: [thisDevice] });
|
818
|
+
}
|
819
|
+
// not a matching device
|
820
|
+
log.debug(`${origin}: No matching device found.`);
|
821
|
+
return callback({ total: 0, list: [] });
|
822
|
+
});
|
823
|
+
} catch (ex) {
|
824
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
|
825
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
826
|
+
return callback(null, errorObj);
|
827
|
+
}
|
828
|
+
}
|
829
|
+
|
830
|
+
/**
|
831
|
+
* @summary Gets the status for the provided appliance
|
832
|
+
*
|
833
|
+
* @function isAlive
|
834
|
+
* @param {String} deviceName - the deviceName of the appliance. (required)
|
835
|
+
*
|
836
|
+
* @param {configCallback} callback - callback function to return the result
|
837
|
+
* (appliance isAlive) or the error
|
838
|
+
*/
|
839
|
+
isAlive(deviceName, callback) {
|
840
|
+
const meth = 'adapter-isAlive';
|
841
|
+
const origin = `${this.id}-${meth}`;
|
842
|
+
log.trace(origin);
|
843
|
+
|
844
|
+
// verify the required fields have been provided
|
845
|
+
if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
|
846
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
|
847
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
848
|
+
return callback(null, errorObj);
|
849
|
+
}
|
850
|
+
|
851
|
+
try {
|
852
|
+
// need to get the device so we can convert the deviceName to an id
|
853
|
+
// !! if we can do a lookup by name the getDevicesFiltered may not be necessary
|
854
|
+
const opts = {
|
855
|
+
filter: {
|
856
|
+
name: deviceName
|
857
|
+
}
|
858
|
+
};
|
859
|
+
return this.getDevicesFiltered(opts, (devs, ferr) => {
|
860
|
+
// if we received an error or their is no response on the results return an error
|
861
|
+
if (ferr) {
|
862
|
+
return callback(null, ferr);
|
863
|
+
}
|
864
|
+
if (devs.list.length < 1) {
|
865
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
|
866
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
867
|
+
return callback(null, errorObj);
|
868
|
+
}
|
869
|
+
// get the uuid from the device
|
870
|
+
const { uuid } = devs.list[0];
|
871
|
+
|
872
|
+
// !! using Generic makes it easier on the Adapter Builder (just need to change the path)
|
873
|
+
// !! you can also replace with a specific call if that is easier
|
874
|
+
const uriPath = `/call/toget/status/${uuid}`;
|
875
|
+
return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
|
876
|
+
// if we received an error or their is no response on the results return an error
|
877
|
+
if (error) {
|
878
|
+
return callback(null, error);
|
879
|
+
}
|
880
|
+
// !! should update this to make sure we are checking for the appropriate object/field
|
881
|
+
if (!result.response || !result.response.returnObj || !Object.hasOwnProperty.call(result.response.returnObj, 'statusField')) {
|
882
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Invalid Response', ['isAlive'], null, null, null);
|
883
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
884
|
+
return callback(null, errorObj);
|
885
|
+
}
|
886
|
+
|
887
|
+
// !! return the response - Update to the appropriate object/field
|
888
|
+
return callback(!result.response.returnObj.statusField);
|
889
|
+
});
|
890
|
+
});
|
891
|
+
} catch (ex) {
|
892
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
|
893
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
894
|
+
return callback(null, errorObj);
|
895
|
+
}
|
896
|
+
}
|
897
|
+
|
898
|
+
/**
|
899
|
+
* @summary Gets a config for the provided Appliance
|
900
|
+
*
|
901
|
+
* @function getConfig
|
902
|
+
* @param {String} deviceName - the deviceName of the appliance. (required)
|
903
|
+
* @param {String} format - the desired format of the config. (optional)
|
904
|
+
*
|
905
|
+
* @param {configCallback} callback - callback function to return the result
|
906
|
+
* (appliance config) or the error
|
907
|
+
*/
|
908
|
+
getConfig(deviceName, format, callback) {
|
909
|
+
const meth = 'adapter-getConfig';
|
910
|
+
const origin = `${this.id}-${meth}`;
|
911
|
+
log.trace(origin);
|
912
|
+
|
913
|
+
// verify the required fields have been provided
|
914
|
+
if (deviceName === undefined || deviceName === null || deviceName === '' || deviceName.length === 0) {
|
915
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Missing Data', ['deviceName'], null, null, null);
|
916
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
917
|
+
return callback(null, errorObj);
|
918
|
+
}
|
919
|
+
|
920
|
+
try {
|
921
|
+
// need to get the device so we can convert the deviceName to an id
|
922
|
+
// !! if we can do a lookup by name the getDevicesFiltered may not be necessary
|
923
|
+
const opts = {
|
924
|
+
filter: {
|
925
|
+
name: deviceName
|
926
|
+
}
|
927
|
+
};
|
928
|
+
return this.getDevicesFiltered(opts, (devs, ferr) => {
|
929
|
+
// if we received an error or their is no response on the results return an error
|
930
|
+
if (ferr) {
|
931
|
+
return callback(null, ferr);
|
932
|
+
}
|
933
|
+
if (devs.list.length < 1) {
|
934
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, `Did Not Find Device ${deviceName}`, [], null, null, null);
|
935
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
936
|
+
return callback(null, errorObj);
|
937
|
+
}
|
938
|
+
// get the uuid from the device
|
939
|
+
const { uuid } = devs.list[0];
|
940
|
+
|
941
|
+
// !! using Generic makes it easier on the Adapter Builder (just need to change the path)
|
942
|
+
// !! you can also replace with a specific call if that is easier
|
943
|
+
const uriPath = `/call/toget/config/${uuid}`;
|
944
|
+
return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
|
945
|
+
// if we received an error or their is no response on the results return an error
|
946
|
+
if (error) {
|
947
|
+
return callback(null, error);
|
948
|
+
}
|
949
|
+
|
950
|
+
// return the result
|
951
|
+
const newResponse = {
|
952
|
+
response: JSON.stringify(result.response, null, 2)
|
953
|
+
};
|
954
|
+
return callback(newResponse);
|
955
|
+
});
|
956
|
+
});
|
957
|
+
} catch (ex) {
|
958
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
|
959
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
960
|
+
return callback(null, errorObj);
|
961
|
+
}
|
962
|
+
}
|
963
|
+
|
964
|
+
/**
|
965
|
+
* @summary Gets the device count from the system
|
966
|
+
*
|
967
|
+
* @function getCount
|
968
|
+
*
|
969
|
+
* @param {getCallback} callback - callback function to return the result
|
970
|
+
* (count) or the error
|
971
|
+
*/
|
972
|
+
getCount(callback) {
|
973
|
+
const meth = 'adapter-getCount';
|
974
|
+
const origin = `${this.id}-${meth}`;
|
975
|
+
log.trace(origin);
|
976
|
+
|
977
|
+
// verify the required fields have been provided
|
978
|
+
|
979
|
+
try {
|
980
|
+
// !! using Generic makes it easier on the Adapter Builder (just need to change the path)
|
981
|
+
// !! you can also replace with a specific call if that is easier
|
982
|
+
const uriPath = '/call/toget/count';
|
983
|
+
return this.genericAdapterRequest(uriPath, 'GET', {}, {}, {}, (result, error) => {
|
984
|
+
// if we received an error or their is no response on the results return an error
|
985
|
+
if (error) {
|
986
|
+
return callback(null, error);
|
987
|
+
}
|
988
|
+
|
989
|
+
// return the result
|
990
|
+
return callback({ count: result.response });
|
991
|
+
});
|
992
|
+
} catch (ex) {
|
993
|
+
const errorObj = this.requestHandlerInst.formatErrorObject(this.id, meth, 'Caught Exception', null, null, null, ex);
|
994
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
995
|
+
return callback(null, errorObj);
|
996
|
+
}
|
997
|
+
}
|
998
|
+
|
531
999
|
/**
|
532
1000
|
* @callback healthCallback
|
533
1001
|
* @param {Object} result - the result of the get request (contains an id and a status)
|