first alpha

This commit is contained in:
Vyacheslav.Sviridov
2022-06-09 20:52:09 +06:00
parent 8b3e8ae5c8
commit fc41ae955f
54 changed files with 2416 additions and 1976 deletions

40
LICENSE Normal file → Executable file
View File

@@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2021 Vyacheslav Sviridov Copyright (c) 2021 Vyacheslav Sviridov
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

388
README.md Normal file → Executable file
View File

@@ -1,194 +1,194 @@
# Cli application based on ENM AutoProvisioning API # Cli application based on ENM AutoProvisioning API
[![Github version](https://img.shields.io/github/package-json/version/vvsviridov/prvn-cli?label=prvn-cli&color=brightgreen&logo=github)](https://github.com/vvsviridov/prvn-cli) [![Github version](https://img.shields.io/github/package-json/version/vvsviridov/prvn-cli?label=prvn-cli&color=brightgreen&logo=github)](https://github.com/vvsviridov/prvn-cli)
[![Npm version](https://img.shields.io/npm/v/prvn-cli?color=red&logo=npm&label=prvn-cli)](https://www.npmjs.com/package/prvn-cli) [![Npm version](https://img.shields.io/npm/v/prvn-cli?color=red&logo=npm&label=prvn-cli)](https://www.npmjs.com/package/prvn-cli)
## Main goal ## Main goal
A simple CLI interface for AutoProvisioning API. A simple CLI interface for AutoProvisioning API.
## Installation ## Installation
First you need **node.js** which can be downloaded from official site [nodejs.org](https://nodejs.org/en/download/) and installed as described in the docs. First you need **node.js** which can be downloaded from official site [nodejs.org](https://nodejs.org/en/download/) and installed as described in the docs.
Then you can run directly from NPM without installation Then you can run directly from NPM without installation
``` ```
npx prvn-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com npx prvn-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com
``` ```
Or install with NPM Or install with NPM
``` ```
npm i prvn-cli npm i prvn-cli
``` ```
Or download this repository and run from the project root directory ... Or download this repository and run from the project root directory ...
``` ```
npm install npm install
``` ```
... to install dependencies, ... to install dependencies,
``` ```
npm link npm link
``` ```
... to add application to your OS $PATH variable if you want to run it from anywhere. ... to add application to your OS $PATH variable if you want to run it from anywhere.
Now you can launch apllication Now you can launch apllication
``` ```
prvn-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com prvn-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com
``` ```
## Usage ## Usage
Recommended environment is Windows Terminal (not _cmd.exe_) or any shell with rich formatting support. After application successfully launched youll see root content and available commands. Recommended environment is Windows Terminal (not _cmd.exe_) or any shell with rich formatting support. After application successfully launched youll see root content and available commands.
### Help Page ### Help Page
``` ```
> prvn-cli --help > prvn-cli --help
Usage: prvn-cli [options] Usage: prvn-cli [options]
Options: Options:
-V, --version output the version number -V, --version output the version number
-l, --login <letters> ENM User Login -l, --login <letters> ENM User Login
-p, --password <letters> ENM User Password -p, --password <letters> ENM User Password
-u, --url <letters> ENM Url -u, --url <letters> ENM Url
-h, --help display help for command -h, --help display help for command
``` ```
### Connection ### Connection
``` ```
>prvn-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com >prvn-cli -l USERNAME -p PASSWORD -u https://enm.your.company.domain.com
✔ Login in... ✔ Login in...
✔ Getting projects... ✔ Getting projects...
323 projects> (Use arrow keys or type to search) 323 projects> (Use arrow keys or type to search)
────────────── ──────────────
> [new] > [new]
[exit] [exit]
────────────── ──────────────
Project1 (1) 1✅ Project1 (1) 1✅
Project2 (2) 1✅ 1⌛ Project2 (2) 1✅ 1⌛
Project3 (2) 1❌ 1⌚ Project3 (2) 1❌ 1⌚
Project4 (1) 1❌ Project4 (1) 1❌
``` ```
### Working with Projects ### Working with Projects
- `[new]` - Import an Auto Provisioning project to start Auto Provisioning workflows based on the content of the AutoProvisioning project. The Auto Provisioning project file contains project related data in the projectInfo.xml file and node folders which contain configurations required to execute AutoProvisioning use-cases. - `[new]` - Import an Auto Provisioning project to start Auto Provisioning workflows based on the content of the AutoProvisioning project. The Auto Provisioning project file contains project related data in the projectInfo.xml file and node folders which contain configurations required to execute AutoProvisioning use-cases.
- `[exit]` - Exit this app. - `[exit]` - Exit this app.
Start typing and you see only commands and projects matches input. Start typing and you see only commands and projects matches input.
``` ```
323 projects> pro 323 projects> pro
────────────── ──────────────
────────────── ──────────────
> Project1 (1) 1✅ > Project1 (1) 1✅
Project2 (2) 1✅ 1⌛ Project2 (2) 1✅ 1⌛
Project3 (2) 1❌ 1⌚ Project3 (2) 1❌ 1⌚
Project4 (1) 1❌ Project4 (1) 1❌
SubNetwork=ONRM_ROOT_MO> ex SubNetwork=ONRM_ROOT_MO> ex
────────────── ──────────────
>[exit] >[exit]
────────────── ──────────────
``` ```
### Working with Single Project ### Working with Single Project
Select project you want to work with. Select project you want to work with.
Available commands are: Available commands are:
- `[delete]` - Delete of an Auto Provisioning project removes an Auto Provisioning project and all the Auto Provisioning data for nodes within that project. This includes removal or rollback of any ongoing Auto Provisioning workflows within that project. - `[delete]` - Delete of an Auto Provisioning project removes an Auto Provisioning project and all the Auto Provisioning data for nodes within that project. This includes removal or rollback of any ongoing Auto Provisioning workflows within that project.
- `[back]` - Return to projects. - `[back]` - Return to projects.
- `[exit]` - Exit this app. - `[exit]` - Exit this app.
``` ```
323 projects> Project1 (1) 1✅ 323 projects> Project1 (1) 1✅
✔ Getting Project1s status... ✔ Getting Project1s status...
✔ Getting Project1s properties... ✔ Getting Project1s properties...
Project id : Project1 Project id : Project1
Author : Ericsson Author : Ericsson
Creation Date : 2019-01-06 11:23:39 Creation Date : 2019-01-06 11:23:39
Description : Project1 description Description : Project1 description
Nodes : Nodes :
RadioNode1 RadioNode1
RadioNode RadioNode
3432-762-238 3432-762-238
192.168.192.168 192.168.192.168
Successful Successful
Integration Completed Integration Completed
Project1> (Use arrow keys or type to search) Project1> (Use arrow keys or type to search)
────────────── ──────────────
> [delete] > [delete]
[back] [back]
[exit] [exit]
────────────── ──────────────
RadioNode1 RadioNode1
``` ```
### Wotking with Node ### Wotking with Node
Select node ... Select node ...
``` ```
Project1 (RadioNode1) > (Use arrow keys or type to search) Project1 (RadioNode1) > (Use arrow keys or type to search)
────────────── ──────────────
> [status] > [status]
[properties] [properties]
[delete] [delete]
[bind] [bind]
[cancel] [cancel]
[resume] [resume]
[configurations] [configurations]
[siteinstall] [siteinstall]
[back] [back]
(Move up and down to reveal more choices) (Move up and down to reveal more choices)
``` ```
Available commands are: Available commands are:
- `[status]` - Retrieving Auto Provisioning node status returns the node status information for each task that has been executed for the specified node. - `[status]` - Retrieving Auto Provisioning node status returns the node status information for each task that has been executed for the specified node.
- `[properties]` - Retrieving Auto Provisioning node properties returns the node properties for each task that has been executed for the specified node. - `[properties]` - Retrieving Auto Provisioning node properties returns the node properties for each task that has been executed for the specified node.
- `[delete]` - Delete an Auto Provisioning node removes the Auto Provisioning data for a Network Element. If a node is the last node in a project and there are no profiles associated with the project the project will automatically be deleted. - `[delete]` - Delete an Auto Provisioning node removes the Auto Provisioning data for a Network Element. If a node is the last node in a project and there are no profiles associated with the project the project will automatically be deleted.
- `[bind]` - Binding a hardware serial number to a node configuration associates the specified node configurations with a hardware serial number for Zero Touch Integration or Hardware Replace. - `[bind]` - Binding a hardware serial number to a node configuration associates the specified node configurations with a hardware serial number for Zero Touch Integration or Hardware Replace.
- `[cancel]` - Cancelling the auto provisioning activity rolls back an AutoProvisoning workflow for Node Integration. For expansion a node is rolled back to it\s original configuration if additional configurations have been applied to the node. - `[cancel]` - Cancelling the auto provisioning activity rolls back an AutoProvisoning workflow for Node Integration. For expansion a node is rolled back to it\s original configuration if additional configurations have been applied to the node.
- `[resume]` - Resuming the auto provisioning activity recommences an Auto Provisioning workflow that is suspended. - `[resume]` - Resuming the auto provisioning activity recommences an Auto Provisioning workflow that is suspended.
- `[configurations]` - Uploading an auto provisioning configuration replaces a configuration file that was part of the initial Auto Provisioning node configuration. - `[configurations]` - Uploading an auto provisioning configuration replaces a configuration file that was part of the initial Auto Provisioning node configuration.
- `[siteinstall]` - Download Site Installation File (SIF) that is required to be taken on site for LMT Integration or LMT Hardware Replace. - `[siteinstall]` - Download Site Installation File (SIF) that is required to be taken on site for LMT Integration or LMT Hardware Replace.
- `[back]` - Return to project\s nodes. - `[back]` - Return to project\s nodes.
- `[exit]` - Exit this app. - `[exit]` - Exit this app.
## Contribution ## Contribution
1. [Fork it] 1. [Fork it]
2. Create your feature branch (`git checkout -b my-new-feature`) 2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am Add some feature`) 3. Commit your changes (`git commit -am Add some feature`)
4. Push to the branch (`git push origin my-new-feature`) 4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request 5. Create new Pull Request
## Known Issues ## Known Issues
### flickering issue ### flickering issue
In some windows terminal spinner may look flickery. To fix this you need to modify file ./node_modules/ora/index.js In some windows terminal spinner may look flickery. To fix this you need to modify file ./node_modules/ora/index.js
Add 1 to _clearLine()_ on this line Add 1 to _clearLine()_ on this line
```javascript ```javascript
this.stream.clearLine(1); this.stream.clearLine(1);
``` ```
## Credits ## Credits
[Contact Me](https://github.com/vvsviridov/) to request new feature or bugs reporting. [Contact Me](https://github.com/vvsviridov/) to request new feature or bugs reporting.
## Changes ## Changes
1.0.0 - is released 1.0.0 - is released

210
bin/enm-cli.js Normal file → Executable file
View File

@@ -1,98 +1,112 @@
#!/usr/bin/env node #!/usr/bin/env node
const { Command, Option } = require('commander') const { Command, Option } = require('commander')
const pkg = require('../package.json') const pkg = require('../package.json')
const inquirer = require('inquirer') const inquirer = require('inquirer')
const { isEmpty } = require('../util/validation') const path = require('path')
require('dotenv').config() const { isEmpty } = require('../util/validation')
const AutoProvisioning = require('../lib/components/AutoProvisioning/AutoProvisioning') require('dotenv').config({ path: [...__dirname.split(path.sep).slice(0,-1), '.env'].join(path.sep) })
const TopologyBrowser = require('../lib/components/TopologyBrowser/TopologyBrowser')
const AutoProvisioning = require('../lib/applications/AutoProvisioning/AutoProvisioning')
const logError = require('../util/logError') const TopologyBrowser = require('../lib/applications/TopologyBrowser/TopologyBrowser')
const applications = ['tplg', 'prvn'] const logError = require('../util/logError')
const program = new Command() if (process.env.NODE_ENV === 'development') {
program process.on('uncaughtException', function (exception) {
.version(pkg.version) console.log(exception);
// .option('-l, --login <letters>', 'ENM User Login') })
// .option('-p, --password <letters>', 'ENM User Password') }
.addOption(new Option('-l, --login <letters>', 'ENM User Login').env('LOGIN'))
.addOption(new Option('-p, --password <letters>', 'ENM User Password').env('PASSWORD')) const applications = [
.addOption(new Option('-a, --application <letters>', 'Start specified application') {
.choices(applications) id: 'tplg',
.default('tplg') appClass: TopologyBrowser,
) name: 'Topology Browser',
.requiredOption('-u, --url <letters>', 'ENM Url') },
.parse(process.argv) {
id: 'prvn',
appClass: AutoProvisioning,
const options = program.opts() name: 'Auto Provisioning',
}
]
const appIds = applications.map(item => item.id)
async function promptUsername() {
const input = await inquirer.prompt([ const program = new Command()
{ program
type: 'input', .version(pkg.version)
name: 'value', .addOption(new Option('-l, --login <letters>', 'ENM User Login').env('LOGIN'))
suffix: chalk.bgGreen('?'), .addOption(new Option('-p, --password <letters>', 'ENM User Password').env('PASSWORD'))
message: 'Type ENM login', .addOption(new Option('-a, --application <letters>', 'Start specified application')
validate: isEmpty, .choices(appIds)
} )
]) .requiredOption('-u, --url <valid URL>', 'ENM Url')
return input.value .parse(process.argv)
}
const options = program.opts()
async function promptPassword() {
const input = await inquirer.prompt([
{ async function promptUsername() {
type: 'password', const input = await inquirer.prompt([
name: 'value', {
message: `Type ${options.login}'s ENM password`, type: 'input',
validate: isEmpty, name: 'value',
} prefix: '👤',
]) message: 'Type ENM login:',
return input.value validate: isEmpty,
} }
])
return input.value
async function selectApplication() { }
let selectedApp
if (options.application && options.application in applications) {
selectedApp = options.application async function promptPassword() {
} else { const input = await inquirer.prompt([
const input = await inquirer.prompt([ {
{ type: 'password',
type: 'list', name: 'value',
name: 'application', prefix: '🔑',
suffix: '💾', message: 'Type ENM password:',
message, validate: isEmpty,
choices: applications }
}]) ])
selectedApp = input.application return input.value
} }
return {
tplg: TopologyBrowser,
prvn: AutoProvisioning async function selectApplication() {
}[selectedApp] let selectedApp
} if (options.application && appIds.includes(options.application)) {
selectedApp = options.application
async function main() { } else {
try { const input = await inquirer.prompt([
const app = new selectApplication()(options.login || await promptUsername(), options.password || await promptPassword(), options.url) {
const result = await app.login() type: 'list',
const { code } = result name: 'application',
if (code === 'SUCCESS') { prefix: '💾',
await app.inputHandler() message: 'Select Application:',
await app.logout() choices: applications.map(item => ({name: item.name, value: item.id, short: item.id}))
} }])
} catch (error) { selectedApp = input.application
logError(error) }
} const { appClass } = applications.find(item => item.id === selectedApp)
} const login = options.login || await promptUsername()
const password = options.password || await promptPassword()
; (async () => await main())() return new appClass(login, password, options.url)
}
async function main() {
try {
const app = await selectApplication()
await app.login()
await app.inputHandler()
await app.logout()
} catch (error) {
logError(error)
}
}
;(async () => await main())()

View File

@@ -1,90 +1,89 @@
const ENM = require('../ENM/ENM') const ENM = require('../../components/ENM')
const { getProjects, getProjectData, deleteProject, newProject } = require('./projects') const { getProjects, getProjectData, deleteProject, newProject } = require('./projects')
const { const {
getNode, getNode,
getNodeStatus, getNodeStatus,
getNodeProperties, getNodeProperties,
bindNode, bindNode,
cancelNode, cancelNode,
resumeNode, resumeNode,
configurationsNode, configurationsNode,
siteinstallNode siteinstallNode
} = require('./nodes') } = require('./nodes')
const inputHandler = require('./inputHandler') const inputHandler = require('./inputHandler')
const createNext = require('../../../util/createNext') const createNext = require('../../../util/createNext')
class AutoProvisioning extends ENM { class AutoProvisioning extends ENM {
constructor(username, password, url) { constructor(username, password, url) {
super(username, password, url) super(username, password, url)
this.appUrl = '/auto-provisioning/v1' this.appUrl = '/auto-provisioning/v1'
this.projects = null this.projects = null
this.projectIndex = -1 this.projectIndex = -1
this.nodes = null this.nodes = null
this.nodeIndex = -1 this.nodeIndex = -1
this.commands = null this.prompt = ''
this.choices = null this.help = 'No results...'
this.help = 'No results...' }
}
async getProjects() {
async getProjects() { return await getProjects.call(this)
return await getProjects.call(this) }
}
async getProjectData() {
async getProjectData() { return await getProjectData.call(this)
return await getProjectData.call(this) }
}
async newProject() {
async newProject() { await newProject.call(this)
await newProject.call(this) }
}
async deleteProject() {
async deleteProject() { await deleteProject.call(this)
await deleteProject.call(this) }
}
async getNode() {
async getNode() { return await getNode.call(this)
return await getNode.call(this) }
}
async getNodeStatus() {
async getNodeStatus() { await getNodeStatus.call(this)
await getNodeStatus.call(this) }
}
async getNodeProperties() {
async getNodeProperties() { await getNodeProperties.call(this)
await getNodeProperties.call(this) }
}
async bindNode() {
async bindNode() { await bindNode.call(this)
await bindNode.call(this) }
}
async cancelNode() {
async cancelNode() { await cancelNode.call(this)
await cancelNode.call(this) }
}
async resumeNode() {
async resumeNode() { await resumeNode.call(this)
await resumeNode.call(this) }
}
async configurationsNode() {
async configurationsNode() { await configurationsNode.call(this)
await configurationsNode.call(this) }
}
async siteinstallNode() {
async siteinstallNode() { await siteinstallNode.call(this)
await siteinstallNode.call(this) }
}
async next(input) {
async next(input) { return createNext.call(this, input ? input : '')
return createNext.call(this, input ? input : '') }
}
async inputHandler() {
async inputHandler() { await inputHandler.call(this)
await inputHandler.call(this) }
}
}
}
module.exports = AutoProvisioning module.exports = AutoProvisioning

View File

@@ -1,107 +1,96 @@
const inquirer = require('inquirer') const inquirer = require('inquirer')
const chalk = require('chalk') const chalk = require('chalk')
const logError = require('../../../util/logError') const logError = require('../../../util/logError')
const { isEmpty } = require('../../../util/validation') const { isEmpty } = require('../../../util/validation')
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt')) inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
async function commandOther(prvn, prompt, command) { async function commandOther(prvn, command) {
const choosedIndex = prvn.choices.indexOf(command) const choosedIndex = prvn.choices.indexOf(command)
if (choosedIndex !== -1) { if (choosedIndex !== -1) {
if (prvn.nodes) { if (prvn.nodes) {
prvn.nodeIndex = choosedIndex prvn.nodeIndex = choosedIndex
prompt = prvn.getNode() prvn.getNode()
} else { } else {
prvn.projectIndex = choosedIndex prvn.projectIndex = choosedIndex
prompt = await prvn.getProjectData() await prvn.getProjectData()
} }
} }
return prompt }
}
async function handleCommand(prvn, command) {
async function handleCommand(prvn, prompt, command) { const [, cmd] = command.match(/\[(\w+)\]/) || [, command]
const [, cmd] = command.match(/\[(\w+)\]/) || [, command] switch (cmd) {
switch (cmd) {
case 'exit':
case 'exit': prvn.prompt = ''
return break
case 'new': case 'new':
await prvn.newProject() await prvn.newProject()
break break
case 'back': case 'back':
prompt = prvn.nodeIndex ? await prvn.getProjects() : await prvn.getProjectData() prvn.nodeIndex ? await prvn.getProjects() : await prvn.getProjectData()
break break
case 'delete': case 'delete':
prvn.nodeIndex ? prvn.deleteNode() : await prvn.deleteProject() prvn.nodeIndex ? prvn.deleteNode() : await prvn.deleteProject()
break break
case 'status': case 'status':
await prvn.getNodeStatus() await prvn.getNodeStatus()
break break
case 'properties': case 'properties':
await prvn.getNodeProperties() await prvn.getNodeProperties()
break break
case 'bind': case 'bind':
await prvn.bindNode() await prvn.bindNode()
break break
case 'cancel': case 'cancel':
await prvn.cancelNode() await prvn.cancelNode()
break break
case 'resume': case 'resume':
await prvn.resumeNode() await prvn.resumeNode()
break break
case 'configurations': case 'configurations':
await prvn.configurationsNode() await prvn.configurationsNode()
break break
case 'siteinstall': case 'siteinstall':
await prvn.siteinstallNode() await prvn.siteinstallNode()
break break
default: default:
prompt = await commandOther(prvn, prompt, command) await commandOther(prvn, command)
} }
return prompt }
}
async function inputHandler() {
async function inputHandlerLoop(prvn) { await this.getProjects()
let prompt = await prvn.getProjects() while (true) {
let prefix = '' try {
while (true) { const input = await inquirer.prompt([
try { {
const input = await inquirer.prompt([ type: 'autocomplete',
{ name: 'command',
type: 'autocomplete', message: chalk.bold.blue(this.prompt),
name: 'command', pageSize: 10,
message: chalk.bold.blue(prompt), prefix: '',
pageSize: 10, suffix: chalk.bold.blue('>'),
prefix: chalk.bold.grey(prefix), validate: isEmpty,
suffix: chalk.bold.blue('>'), source: async (answers, input) => await this.next(input),
validate: isEmpty, emptyText: this.help,
source: async (answers, input) => await prvn.next(input), }
emptyText: prvn.help, ])
} await handleCommand(this, input.command)
]) if (!this.prompt) break
prompt = await handleCommand(prvn, prompt, input.command) } catch (error) {
if (!prompt) break logError(error)
} catch (error) { }
logError(error) }
} }
}
}
async function inputHandler() {
try {
await inputHandlerLoop(this)
} catch (error) {
logError(error)
}
}
module.exports = inputHandler module.exports = inputHandler

View File

@@ -1,165 +1,165 @@
const fs = require('fs') const fs = require('fs')
const fsPromises = require('fs').promises const fsPromises = require('fs').promises
const path = require('path') const path = require('path')
const FormData = require('form-data') const FormData = require('form-data')
const chalk = require('chalk') const chalk = require('chalk')
const inquirer = require('inquirer') const inquirer = require('inquirer')
const { logNodeStatus, logNodeProperties } = require('../../../util/logNode') const { logNodeStatus, logNodeProperties } = require('../../../util/logNode')
const { isValidHardwareId } = require('../../../util/validation') const { isValidHardwareId } = require('../../../util/validation')
const nodeCommands = [ const nodeCommands = [
'status', 'properties', 'delete', 'status', 'properties', 'delete',
'bind', 'cancel', 'resume', 'bind', 'cancel', 'resume',
'configurations', 'siteinstall', 'back', 'configurations', 'siteinstall', 'back',
'exit' 'exit'
] ]
const nodeCommandsHelp = [ const nodeCommandsHelp = [
'[status] - Retrieving Auto Provisioning node status returns the node status information for each task that has been executed for the specified node.', '[status] - Retrieving Auto Provisioning node status returns the node status information for each task that has been executed for the specified node.',
'[properties] - Retrieving Auto Provisioning node properties returns the node properties for each task that has been executed for the specified node.', '[properties] - Retrieving Auto Provisioning node properties returns the node properties for each task that has been executed for the specified node.',
'[delete] - Delete an Auto Provisioning node removes the Auto Provisioning data for a Network Element. If a node is the last node in a project and there are no profiles associated with the project the project will automatically be deleted.', '[delete] - Delete an Auto Provisioning node removes the Auto Provisioning data for a Network Element. If a node is the last node in a project and there are no profiles associated with the project the project will automatically be deleted.',
'[bind] - Binding a hardware serial number to a node configuration associates the specified node configurations with a hardware serial number for Zero Touch Integration or Hardware Replace.', '[bind] - Binding a hardware serial number to a node configuration associates the specified node configurations with a hardware serial number for Zero Touch Integration or Hardware Replace.',
'[cancel] - Cancelling the auto provisioning activity rolls back an AutoProvisoning workflow for Node Integration. For expansion a node is rolled back to it\'s original configuration if additional configurations have been applied to the node.', '[cancel] - Cancelling the auto provisioning activity rolls back an AutoProvisoning workflow for Node Integration. For expansion a node is rolled back to it\'s original configuration if additional configurations have been applied to the node.',
'[resume] - Resuming the auto provisioning activity recommences an Auto Provisioning workflow that is suspended.', '[resume] - Resuming the auto provisioning activity recommences an Auto Provisioning workflow that is suspended.',
'[configurations] - Uploading an auto provisioning configuration replaces a configuration file that was part of the initial Auto Provisioning node configuration.', '[configurations] - Uploading an auto provisioning configuration replaces a configuration file that was part of the initial Auto Provisioning node configuration.',
'[siteinstall] - Download Site Installation File (SIF) that is required to be taken on site for LMT Integration or LMT Hardware Replace.', '[siteinstall] - Download Site Installation File (SIF) that is required to be taken on site for LMT Integration or LMT Hardware Replace.',
'[back] - Return to project\'s nodes.', '[back] - Return to project\'s nodes.',
'[exit] - Exit this app.', '[exit] - Exit this app.',
] ]
async function getNode() { async function getNode() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
this.commands = nodeCommands this.commands = nodeCommands
this.help = nodeCommandsHelp.join('\n ') this.help = nodeCommandsHelp.join('\n ')
this.choices = [] // this.nodes.filter(item => item !== nodeId) this.choices = [] // this.nodes.filter(item => item !== nodeId)
return `${projectId} (${nodeId}) ` this.prompt = `${projectId} (${nodeId}) `
} }
async function getNodeStatus() { async function getNodeStatus() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const axiosConfig = { const axiosConfig = {
text: `Getting ${nodeId}'s status...`, text: `Getting ${nodeId}'s status...`,
method: 'get', method: 'get',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}` url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}`
} }
const { data: { statusEntries } } = await this.httpClient.request(axiosConfig) const { data: { statusEntries } } = await this.httpClient.request(axiosConfig)
logNodeStatus(statusEntries) logNodeStatus(statusEntries)
} }
async function getNodeProperties() { async function getNodeProperties() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const axiosConfig = { const axiosConfig = {
text: `Getting ${nodeId}'s properties...`, text: `Getting ${nodeId}'s properties...`,
method: 'get', method: 'get',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}?filter=properties` url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}?filter=properties`
} }
const { data: { attributes, attributeGroups } } = await this.httpClient.request(axiosConfig) const { data: { attributes, attributeGroups } } = await this.httpClient.request(axiosConfig)
logNodeProperties(attributes, attributeGroups) logNodeProperties(attributes, attributeGroups)
} }
async function bindNode() { async function bindNode() {
const hardwareId = await inquirer.prompt([ const hardwareId = await inquirer.prompt([
{ {
type: 'input', type: 'input',
name: 'value', name: 'value',
suffix: chalk.bgGreen('?'), suffix: chalk.bgGreen('?'),
message: 'Type hardwareId', message: 'Type hardwareId',
validate: input => isValidHardwareId(input), validate: input => isValidHardwareId(input),
} }
]) ])
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const axiosConfig = { const axiosConfig = {
text: `Binding ${hardwareId} to ${nodeId}...`, text: `Binding ${hardwareId} to ${nodeId}...`,
method: 'put', method: 'put',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/bind`, url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/bind`,
data: { data: {
hardwareId, hardwareId,
}, },
} }
const { statusText } = await this.httpClient.request(axiosConfig) const { statusText } = await this.httpClient.request(axiosConfig)
console.log(chalk.bgGreen(`Binding ${statusText}`)) console.log(chalk.bgGreen(`Binding ${statusText}`))
} }
async function cancelNode() { async function cancelNode() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const axiosConfig = { const axiosConfig = {
text: `Canceling ${nodeId}...`, text: `Canceling ${nodeId}...`,
method: 'post', method: 'post',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/cancel`, url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/cancel`,
} }
const { statusText } = await this.httpClient.request(axiosConfig) const { statusText } = await this.httpClient.request(axiosConfig)
console.log(chalk.bgGreen(`Canceling ${statusText}`)) console.log(chalk.bgGreen(`Canceling ${statusText}`))
} }
async function resumeNode() { async function resumeNode() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const axiosConfig = { const axiosConfig = {
text: `Resuming ${nodeId}...`, text: `Resuming ${nodeId}...`,
method: 'post', method: 'post',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/resume`, url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/resume`,
} }
const { statusText } = await this.httpClient.request(axiosConfig) const { statusText } = await this.httpClient.request(axiosConfig)
console.log(chalk.bgGreen(`Resuming ${statusText}`)) console.log(chalk.bgGreen(`Resuming ${statusText}`))
} }
async function configurationsNode() { async function configurationsNode() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const fileNameInput = await inquirer.prompt([{ const fileNameInput = await inquirer.prompt([{
type: 'file-tree-selection', type: 'file-tree-selection',
name: 'nodeFile', name: 'nodeFile',
message: 'Choose a node file...', message: 'Choose a node file...',
}]) }])
const formData = new FormData() const formData = new FormData()
formData.append('file', fs.createReadStream(fileNameInput.nodeFile)) formData.append('file', fs.createReadStream(fileNameInput.nodeFile))
const axiosConfig = { const axiosConfig = {
text: `Uploading ${nodeId} configuration to ${projectId}...`, text: `Uploading ${nodeId} configuration to ${projectId}...`,
method: 'put', method: 'put',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/configurations`, url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/configurations`,
headers: formData.getHeaders(), headers: formData.getHeaders(),
data: formData data: formData
} }
const { statusText } = await this.httpClient.request(axiosConfig) const { statusText } = await this.httpClient.request(axiosConfig)
console.log(chalk.bgGreen(`Resuming ${statusText}`)) console.log(chalk.bgGreen(`Resuming ${statusText}`))
} }
async function siteinstallNode() { async function siteinstallNode() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const nodeId = this.nodes[this.nodeIndex] const nodeId = this.nodes[this.nodeIndex]
const saveFileName = path.join(process.cwd(), `Site_Install_${nodeId}.xml`) const saveFileName = path.join(process.cwd(), `Site_Install_${nodeId}.xml`)
const axiosConfig = { const axiosConfig = {
text: `Downloading site install file ${nodeId}...`, text: `Downloading site install file ${nodeId}...`,
method: 'get', method: 'get',
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/configurations/siteinstall`, url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/configurations/siteinstall`,
} }
const { data } = await this.httpClient.request(axiosConfig) const { data } = await this.httpClient.request(axiosConfig)
await fsPromises.writeFile(saveFileName, data) await fsPromises.writeFile(saveFileName, data)
console.log(chalk.bgGreen(`Download site install file to ${saveFileName}`)) console.log(chalk.bgGreen(`Download site install file to ${saveFileName}`))
} }
module.exports = { module.exports = {
getNode, getNode,
getNodeStatus, getNodeStatus,
getNodeProperties, getNodeProperties,
bindNode, bindNode,
cancelNode, cancelNode,
resumeNode, resumeNode,
configurationsNode, configurationsNode,
siteinstallNode siteinstallNode
} }

View File

@@ -1,122 +1,122 @@
const inquirer = require('inquirer') const inquirer = require('inquirer')
const chalk = require('chalk') const chalk = require('chalk')
const inquirerFileTreeSelection = require('inquirer-file-tree-selection-prompt') const inquirerFileTreeSelection = require('inquirer-file-tree-selection-prompt')
const fs = require('fs') const fs = require('fs')
const FormData = require('form-data') const FormData = require('form-data')
const logProject = require('../../../util/logProject') const logProject = require('../../../util/logProject')
inquirer.registerPrompt('file-tree-selection', inquirerFileTreeSelection) inquirer.registerPrompt('file-tree-selection', inquirerFileTreeSelection)
const projectsCommands = ['new', 'exit'] const projectsCommands = ['new', 'exit']
const projectsCommandsHelp = [ const projectsCommandsHelp = [
'[new] - Import an Auto Provisioning project to start Auto Provisioning workflows based on the content of the AutoProvisioning project. The Auto Provisioning project file contains project related data in the projectInfo.xml file and node folders which contain configurations required to execute AutoProvisioning use-cases.', '[new] - Import an Auto Provisioning project to start Auto Provisioning workflows based on the content of the AutoProvisioning project. The Auto Provisioning project file contains project related data in the projectInfo.xml file and node folders which contain configurations required to execute AutoProvisioning use-cases.',
'[exit] - Exit this app.', '[exit] - Exit this app.',
'Or choose a project from list...', 'Or choose a project from list...',
] ]
const projectCommands = ['delete', 'back', 'exit'] const projectCommands = ['delete', 'back', 'exit']
const projectCommandsHelp = [ const projectCommandsHelp = [
'[delete] - Delete of an Auto Provisioning project removes an Auto Provisioning project and all the Auto Provisioning data for nodes within that project. This includes removal or rollback of any ongoing Auto Provisioning workflows within that project.', '[delete] - Delete of an Auto Provisioning project removes an Auto Provisioning project and all the Auto Provisioning data for nodes within that project. This includes removal or rollback of any ongoing Auto Provisioning workflows within that project.',
'[back] - Return to projects.', '[back] - Return to projects.',
'[exit] - Exit this app.', '[exit] - Exit this app.',
'Or choose a node from list...', 'Or choose a node from list...',
] ]
async function getProjects() { async function getProjects() {
const axiosConfig = { const axiosConfig = {
text: 'Getting projects...', text: 'Getting projects...',
method: 'get', method: 'get',
url: `${this.appUrl}/projects` url: `${this.appUrl}/projects`
} }
const response = await this.httpClient.request(axiosConfig) const response = await this.httpClient.request(axiosConfig)
this.projects = response.data this.projects = response.data
this.choices = projectsChoices(this.projects) this.choices = projectsChoices(this.projects)
this.commands = projectsCommands this.commands = projectsCommands
this.help = projectsCommandsHelp.join('\n ') this.help = projectsCommandsHelp.join('\n ')
this.nodes = null this.nodes = null
return `${this.projects.length} projects` this.prompt = `${this.projects.length} projects`
} }
async function getProjectData() { async function getProjectData() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const axiosConfig = { const axiosConfig = {
text: `Getting ${projectId}'s status...`, text: `Getting ${projectId}'s status...`,
method: 'get', method: 'get',
url: `${this.appUrl}/projects/${projectId}` url: `${this.appUrl}/projects/${projectId}`
} }
const { data: { nodeSummary } } = await this.httpClient.request(axiosConfig) const { data: { nodeSummary } } = await this.httpClient.request(axiosConfig)
axiosConfig.text = `Getting ${projectId}'s properties...` axiosConfig.text = `Getting ${projectId}'s properties...`
axiosConfig.url += '?filter=properties' axiosConfig.url += '?filter=properties'
const { data } = await this.httpClient.request(axiosConfig) const { data } = await this.httpClient.request(axiosConfig)
logProject(data, nodeSummary) logProject(data, nodeSummary)
this.nodes = nodeSummary.map(node => node.id) this.nodes = nodeSummary.map(node => node.id)
this.choices = this.nodes this.choices = this.nodes
this.commands = projectCommands this.commands = projectCommands
this.help = projectCommandsHelp.join('\n ') this.help = projectCommandsHelp.join('\n ')
this.nodeIndex = -1 this.nodeIndex = -1
return projectId this.prompt = projectId
} }
async function newProject() { async function newProject() {
const fileNameInput = await inquirer.prompt([{ const fileNameInput = await inquirer.prompt([{
type: 'file-tree-selection', type: 'file-tree-selection',
name: 'projectFile', name: 'projectFile',
message: 'Choose a project file...', message: 'Choose a project file...',
}]) }])
const formData = new FormData() const formData = new FormData()
formData.append('file', fs.createReadStream(fileNameInput.projectFile)) formData.append('file', fs.createReadStream(fileNameInput.projectFile))
const axiosConfig = { const axiosConfig = {
text: `Creating project...`, text: `Creating project...`,
method: 'post', method: 'post',
url: `${this.appUrl}/projects`, url: `${this.appUrl}/projects`,
headers: formData.getHeaders(), headers: formData.getHeaders(),
data: formData data: formData
} }
const { data: { id } } = await this.httpClient.request(axiosConfig) const { data: { id } } = await this.httpClient.request(axiosConfig)
console.log(chalk.bgGreen(`Project ${id} created!`)) console.log(chalk.bgGreen(`Project ${id} created!`))
await this.getProjectData(id) await this.getProjectData(id)
} }
async function deleteProject() { async function deleteProject() {
const { projectId } = this.projects[this.projectIndex] const { projectId } = this.projects[this.projectIndex]
const axiosConfig = { const axiosConfig = {
text: `Deleting project ${projectId}...`, text: `Deleting project ${projectId}...`,
method: 'delete', method: 'delete',
url: `${this.appUrl}/projects/${projectId}` url: `${this.appUrl}/projects/${projectId}`
} }
const { statusText } = await this.httpClient.request(axiosConfig) const { statusText } = await this.httpClient.request(axiosConfig)
console.log(chalk.bgGreen(`Delete ${statusText}`)) console.log(chalk.bgGreen(`Delete ${statusText}`))
this.choices = projectsChoices(this.projects) this.choices = projectsChoices(this.projects)
} }
function projectsChoices(projects) { function projectsChoices(projects) {
return projects.map(project => { return projects.map(project => {
const { const {
projectId, projectId,
numberOfNodes, numberOfNodes,
integrationPhaseSummary: { integrationPhaseSummary: {
cancelled, cancelled,
failed, failed,
inProgress, inProgress,
successful, successful,
suspended, suspended,
}, },
} = project } = project
const statusList = [ const statusList = [
successful && successful + '✅', successful && successful + '✅',
inProgress && inProgress + '⌛', inProgress && inProgress + '⌛',
suspended && suspended + '⌚', suspended && suspended + '⌚',
cancelled && cancelled + '❌', cancelled && cancelled + '❌',
failed && failed + '⛔', failed && failed + '⛔',
] ]
const space = ' '.repeat(Math.max(14, projectId.length + 2) - projectId.length) const space = ' '.repeat(Math.max(14, projectId.length + 2) - projectId.length)
return `${chalk.bold(projectId)}${space}(${numberOfNodes}) ${statusList.filter(item => item).join(' ')}` return `${chalk.bold(projectId)}${space}(${numberOfNodes}) ${statusList.filter(item => item).join(' ')}`
}) })
} }
module.exports = { getProjects, getProjectData, deleteProject, newProject } module.exports = { getProjects, getProjectData, deleteProject, newProject }

View File

@@ -0,0 +1,154 @@
const logAttributes = require('../../../util/logAttributes')
const inputHandler = require('./inputHandler')
const alarms = require('./commands/alarms')
const sync = require('./commands/sync')
const initialPrompt = require('./commands/initialPrompt')
const nextObjects = require('./commands/nextObjects')
const nextAttributes = require('./commands/nextAttributes')
const setIdByCommand = require('./commands/setIdByCommand')
const show = require('./commands/show')
const up = require('./commands/up')
const config = require('./commands/config')
const end = require('./commands/end')
const setAttribute = require('./commands/setAttribute')
const description = require('./commands/description')
const get = require('./commands/get')
const set = require('./commands/set')
const commit = require('./commands/commit')
const home = require('./commands/home')
const search = require('./commands/search')
const goToFdn = require('./commands/goToFdn')
const createNext = require('../../../util/createNext')
const { mainHelp, configHelp } = require('./help')
const ENM = require('../../components/ENM')
const chalk = require('chalk')
class TopologyBrowser extends ENM {
constructor(username, password, url) {
super(username, password, url)
this.objectUrl = '/persistentObject/'
this.alarmUrl = '/alarmcontroldisplayservice/alarmMonitoring/alarmoperations/'
this.nodeTypesUrl = '/modelInfo/model/nodeTypes/'
this.nodeSearchUrl = '/managedObjects/search/v2'
this.nodePoIdsUrl = '/managedObjects/getPosByPoIds'
this.currentPoId = 0
this.nextPoId = 1
this.fdn = ''
this.childrens = []
this.poIds = []
this.isConfig = false
this.attributes = []
this.nextVariants = null
this.attributesData = []
this.attribute = null
this.networkDetails = null
this.configSet = []
this.includeNonPersistent = false
this.configCommands = ['commit', 'check', 'end', 'exit']
this.help = mainHelp
}
async initialPrompt() {
await initialPrompt.call(this)
}
async next(input) {
await this.nextVariants(input)
return createNext.call(this, input ? input : '')
}
async nextObjects(input) {
return await nextObjects.call(this, input)
}
async nextAttributes(input) {
return await nextAttributes.call(this, input)
}
setIdByCommand(command) {
return setIdByCommand.call(this, command)
}
async show(filter) {
await show.call(this, filter)
}
up() {
return up.call(this)
}
async config(fdn) {
this.help = configHelp
return await config.call(this, fdn)
}
end() {
this.help = mainHelp
end.call(this)
}
setAttribute(attribute) {
return setAttribute.call(this, attribute)
}
description() {
description.call(this)
}
get(fdn) {
get.call(this, fdn)
}
async set() {
await set.call(this)
}
async commit(fdn) {
return await commit.call(this, fdn)
}
check() {
logAttributes(this.fdn.replace(/\((\w+)\)/g, ''), this.configSet)
}
home() {
home.call(this)
}
async search() {
await search.call(this)
}
async goToFdn(fromFdn, targetFdn) {
return await goToFdn.call(this, fromFdn, targetFdn)
}
persistent() {
this.includeNonPersistent = !this.includeNonPersistent
console.log(chalk.yellow(`Include Non Persistent Atrributes Set to: ${this.includeNonPersistent}`))
}
async alarms(fdn) {
await alarms.call(this, fdn)
}
async sync(fdn) {
await sync.call(this, fdn)
}
async inputHandler() {
await inputHandler.call(this)
}
getPrompt() {
if (this.fdn.length >= 67) {
return `...${this.fdn.slice(-65)}`
}
return this.fdn
}
}
module.exports = TopologyBrowser

View File

@@ -1,127 +1,127 @@
const colors = require('colors') const chalk = require('chalk')
const inquirer = require('inquirer') const inquirer = require('inquirer')
const requestWrapper = require('../../util/requestWrapper') const logAlarm = require('../../../../util/logAlarm')
const logAlarm = require('../../util/logAlarm') const eventTimeToString = require('../../../../util/eventTimeToString')
const eventTimeToString = require('../../util/eventTimeToString')
const closeAlarms = { name: chalk.yellow('Close Alarms'), value: -1}
const closeAlarms = { name: 'Close Alarms'.yellow, value: -1}
function logTotal(total) {
function logTotal(total) { if (total === 0) {
if (total === 0) { console.log(chalk.green(`Total Alarms: ${total}`))
console.log(`Total Alarms: ${total}`.green) return
return }
} console.log(chalk.yellow(`Total Alarms: ${total}`))
console.log(`Total Alarms: ${total}`.yellow) }
}
function parsePoIdResponse(response) {
function parsePoIdResponse(response) { let total
let total let eventPoIds
let eventPoIds response.data.forEach(item => {
response.data.forEach(item => { if (item.eventPoIds) {
if (item.eventPoIds) { eventPoIds = item.eventPoIds
eventPoIds = item.eventPoIds return
return }
} if (typeof item.total === 'number') {
if (typeof item.total === 'number') { total = item.total
total = item.total return
return }
} })
}) return [total, eventPoIds]
return [total, eventPoIds] }
}
async function getPoIds(url, nodes) {
async function getPoIds(url, nodes) { const axiosConfig = {
const axiosConfig = { text: 'Getting Alarms...',
method: 'post', method: 'post',
url, url,
data: { data: {
filters: '', filters: '',
category: 'All', category: 'All',
nodes, nodes,
recordLimit: 5000, recordLimit: 5000,
tableSettings: 'fdn#true#false,alarmingObject#true#false,presentSeverity#true#false,eventTime#true#false,insertTime#true#false,specificProblem#true#false', tableSettings: 'fdn#true#false,alarmingObject#true#false,presentSeverity#true#false,eventTime#true#false,insertTime#true#false,specificProblem#true#false',
timestamp: + new Date(), timestamp: + new Date(),
sortCriteria: [ sortCriteria: [
{ {
attribute: 'insertTime', attribute: 'insertTime',
mode: 'desc' mode: 'desc'
} }
], ],
advFilters: '' advFilters: ''
} }
} }
const response = await requestWrapper(axiosConfig, 'Getting Alarms...') const response = await this.httpClient.request(axiosConfig)
if (!Array.isArray(response.data)) return if (!Array.isArray(response.data)) return
const [total, eventPoIds] = parsePoIdResponse(response) const [total, eventPoIds] = parsePoIdResponse(response)
logTotal(total) logTotal(total)
if (eventPoIds) return eventPoIds.toString() if (eventPoIds) return eventPoIds.toString()
} }
async function getFields(url, eventPoIds) { async function getFields(url, eventPoIds) {
const axiosConfig = { const axiosConfig = {
method: 'post', text: 'Getting Alarms Data...',
url, method: 'post',
data: { url,
eventPoIds, data: {
tableSettings: '', eventPoIds,
timestamp: + new Date(), tableSettings: '',
filters: '', timestamp: + new Date(),
category: 'All' filters: '',
} category: 'All'
} }
const response = await requestWrapper(axiosConfig, 'Getting Alarms Data...') }
return response.data const response = await this.httpClient.request(axiosConfig)
} return response.data
}
async function alarmChoices(alarmList, input) {
const filter = input ? input : '' function alarmChoices(alarmList, input) {
return alarmList const filter = input ? input : ''
.map(al => { return alarmList
const { eventPoIdAsLong, alarmingObject, presentSeverity, eventTime, specificProblem } = al .map(al => {
return { const { eventPoIdAsLong, alarmingObject, presentSeverity, eventTime, specificProblem } = al
name: `${presentSeverity}\t${eventTimeToString(eventTime)}\t${alarmingObject}\t${specificProblem}`, return {
value: eventPoIdAsLong name: `${presentSeverity}\t${eventTimeToString(eventTime)}\t${alarmingObject}\t${specificProblem}`,
} value: eventPoIdAsLong
}) }
.filter(al => al.name.toLowerCase().includes(filter.toLowerCase())) })
.concat(closeAlarms) .filter(al => al.name.toLowerCase().includes(filter.toLowerCase()))
} .concat(closeAlarms)
}
async function alarmsLoop(alarmList) {
while (true) { async function alarmsLoop(alarmList) {
const input = await inquirer.prompt([ while (true) {
{ const input = await inquirer.prompt([
type: 'autocomplete', {
name: 'alarm', type: 'autocomplete',
message: 'Select Alarm:', name: 'alarm',
pageSize: 10, message: 'Select Alarm:',
source: async (answers, input) => await alarmChoices(alarmList, input) pageSize: 10,
} source: async (answers, input) => alarmChoices(alarmList, input)
]) }
if (input.alarm === closeAlarms.value) break ])
logAlarm(alarmList, input.alarm) if (input.alarm === closeAlarms.value) break
} logAlarm(alarmList, input.alarm)
} }
}
async function alarms(fdn) {
const meContextFind = fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/) async function alarms() {
if (!meContextFind) { const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
console.log('No alarming object in FDN!'.yellow) if (!meContextFind) {
return throw new Error('No alarming object in FDN!')
} }
const eventPoIds = await getPoIds(`${this.alarmUrl}eventpoids`, meContextFind[2]) const eventPoIds = await getPoIds.call(this, `${this.alarmUrl}eventpoids`, meContextFind[2])
if (!eventPoIds) return if (!eventPoIds) return
const alarmList = await getFields(`${this.alarmUrl}getalarmlist/fields`, eventPoIds) const alarmList = await getFields.call(this, `${this.alarmUrl}getalarmlist/fields`, eventPoIds)
await alarmsLoop(alarmList) await alarmsLoop(alarmList)
} }
module.exports = alarms module.exports = alarms

View File

@@ -0,0 +1,35 @@
const logAttributes = require('../../../../util/logAttributes')
const logCommit = require('../../../../util/logCommit')
async function commit() {
if (!this.configSet.length) {
throw new Error('Configuration is empty❗')
}
this.fdn = this.fdn.replace(/\((\w+)\)/g, '')
logAttributes(this.fdn, this.configSet)
const axiosConfig = {
text: 'Commiting Config...',
method: 'put',
url: `${this.objectUrl}${this.currentPoId}`,
data: {
poId: this.currentPoId,
fdn: this.fdn,
attributes: this.configSet,
},
headers: {
'Content-Type': 'application/json'
}
}
const response = await this.httpClient.request(axiosConfig)
if (response.data) {
logCommit(response.data)
} else {
throw new Error('No data or response❗')
}
this.configSet.length = 0
this.end()
}
module.exports = commit

View File

@@ -1,39 +1,38 @@
const requestWrapper = require('../../util/requestWrapper') const logDetails = require('../../../../util/logDetails')
const logDetails = require('../../util/logDetails')
async function config() {
async function config(fdn) { this.isConfig = true
this.isConfig = true const axiosConfig = {
const axiosConfig = { text: 'Reading Attributes...',
method: 'get', method: 'get',
url: `${this.objectUrl}${this.currentPoId}`, url: `${this.objectUrl}${this.currentPoId}`,
params: { params: {
includeNonPersistent: this.includeNonPersistent, includeNonPersistent: this.includeNonPersistent,
stringifyLong: true stringifyLong: true
} }
} }
const responseA = await requestWrapper(axiosConfig, 'Reading Attributes...') const responseA = await this.httpClient.request(axiosConfig)
if (!responseA.data.attributes) { if (!responseA.data.attributes) {
console.log('Can\'t read attributes'.red) throw new Error('Can\'t read attributes')
return fdn }
} const { attributes, namespace, namespaceVersion, neType, neVersion, networkDetails, type} = responseA.data
const { attributes, namespace, namespaceVersion, neType, neVersion, networkDetails, type} = responseA.data axiosConfig.text = 'Reading Attributes Data...'
axiosConfig.url = `${this.objectUrl}model/${neType}/${neVersion}/${namespace}/${type}/${namespaceVersion}/attributes` axiosConfig.url = `${this.objectUrl}model/${neType}/${neVersion}/${namespace}/${type}/${namespaceVersion}/attributes`
axiosConfig.params = { axiosConfig.params = {
includeNonPersistent: this.includeNonPersistent includeNonPersistent: this.includeNonPersistent
} }
const responseD = await requestWrapper(axiosConfig, 'Reading Attributes Data...') const responseD = await this.httpClient.request(axiosConfig)
if (!responseD.data.attributes) { if (!responseD.data.attributes) {
console.log('Can\'t read attributes data'.red) throw new Error('Can\'t read attributes data')
return fdn }
} this.networkDetails = networkDetails
this.networkDetails = networkDetails logDetails(networkDetails)
logDetails(networkDetails) this.attributes = attributes
this.attributes = attributes this.nextVariants = async (input) => await this.nextAttributes(input)
this.nextVariants = async (input) => await this.nextAttributes(input) this.attributesData = responseD.data.attributes
this.attributesData = responseD.data.attributes this.fdn = `${this.fdn}(config)`
return `${fdn}(config)` }
}
module.exports = config module.exports = config

View File

@@ -0,0 +1,21 @@
const chalk = require('chalk')
const logAttributeData = require('../../../../util/logAttributeData')
function description() {
const attributeData = this.attributesData.find(item => item.key === this.attribute)
if (attributeData) {
logAttributeData(attributeData)
if (attributeData.complexRef) {
console.log(`${chalk.magenta(attributeData.type)}
${chalk.cyan(attributeData.complexRef.key)}: ${chalk.grey(attributeData.complexRef.description)}
`)
attributeData.complexRef.attributes.forEach(attr => logAttributeData(attr))
}
} else {
throw new Error('Attribute Not Found❗')
}
}
module.exports = description

View File

@@ -1,9 +1,11 @@
function end() { function end() {
this.isConfig = false this.fdn = this.fdn.replace(/\((\w+)\)/g, '')
this.attribute = null this.isConfig = false
this.configSet.length = 0 this.attribute = null
this.nextVariants = async (input) => await this.nextObjects(input) this.configSet.length = 0
} this.configCommands = this.configCommands.filter(cmd => !['get', 'set', 'description'].includes(cmd) )
this.nextVariants = async (input) => await this.nextObjects(input)
}
module.exports = end module.exports = end

View File

@@ -0,0 +1,23 @@
const logAttributes = require('../../../../util/logAttributes')
const banner = require('../../../../util/banner')
const chalk = require('chalk')
function get() {
const syncStatus = this.networkDetails.find(item => item.key === 'syncStatus')
if (syncStatus && syncStatus.value === 'UNSYNCHRONIZED') {
console.log(chalk.yellow(`
${syncStatus.key}: ${syncStatus.value}`))
}
const attribute = this.attributes.find(item => item.key === this.attribute)
if (!attribute) {
throw new Error(`Attribute not Found: ${this.attribute}`)
}
const attributeData = this.attributesData.find(item => item.key === this.attribute)
logAttributes(this.fdn, [attribute])
console.log(` ${chalk.green('Type: ') + attributeData['type']} ${attributeData['defaultValue'] ? chalk.yellow('Default: ') + attributeData['defaultValue'] : ''}
`)
if (attributeData.constraints && attributeData.constraints.orderingConstraint) banner(attributeData)
}
module.exports = get

View File

@@ -1,28 +1,29 @@
const requestWrapper = require('../../util/requestWrapper') async function goToFdn(targetFdn) {
if (!targetFdn) {
throw new Error('Valid FDN must be supplied❗')
async function fdn(fromFdn, targetFdn) { }
const axiosConfig = { const axiosConfig = {
method: 'get', text: 'Browsing to FDN...',
url: `${this.objectUrl}fdn/${targetFdn}`, method: 'get',
} url: `${this.objectUrl}fdn/${targetFdn}`,
const response1 = await requestWrapper(axiosConfig, 'Browsing to FDN...') }
if (!response1.data.fdn) return fromFdn const response1 = await this.httpClient.request(axiosConfig)
this.poIds.length = 0 this.poIds.length = 0
const { fdn, poId } = response1.data const { fdn, poId } = response1.data
this.currentPoId = poId this.currentPoId = poId
this.nextPoId = poId this.nextPoId = poId
this.nextVariants = async (input) => await this.nextObjects(input) this.nextVariants = async (input) => await this.nextObjects(input)
axiosConfig.url = `${this.objectUrl}network/${this.currentPoId}/subTrees` axiosConfig.text = 'Building FDN path...'
const response2 = await requestWrapper(axiosConfig, 'Building FDN path...') axiosConfig.url = `${this.objectUrl}network/${this.currentPoId}/subTrees`
if (response2.data) { const response2 = await this.httpClient.request(axiosConfig)
if (response2.data.treeNodes.length > 1) { if (response2.data) {
response2.data.treeNodes.slice(0, -1).forEach((node) => { if (response2.data.treeNodes.length > 1) {
this.poIds.push(node.poId) response2.data.treeNodes.slice(0, -1).forEach((node) => {
}) this.poIds.push(node.poId)
} })
this.childrens = null }
} this.childrens = []
return fdn }
} this.fdn = fdn
module.exports = fdn }
module.exports = goToFdn

View File

@@ -1,8 +1,9 @@
function home() { function home() {
this.nextPoId = this.poIds.shift() this.fdn = this.fdn.split(',', 1)[0]
this.poIds.length = 0 this.nextPoId = this.poIds.shift()
this.nextVariants = async (input) => await this.nextObjects(input) this.poIds.length = 0
} this.nextVariants = async (input) => await this.nextObjects(input)
}
module.exports = home
module.exports = home

View File

@@ -1,23 +1,22 @@
const colors = require('colors') const chalk = require("chalk")
const requestWrapper = require('../../util/requestWrapper')
async function initialPrompt() {
async function initialPrompt() { const axiosConfig = {
const axiosConfig = { text: 'Starting Topology Browser...',
method: 'get', method: 'get',
url: `${this.objectUrl}network/-1?relativeDepth=0:-2&childDepth=1` url: `${this.objectUrl}network/-1?relativeDepth=0:-2&childDepth=1`
} }
const response = await requestWrapper(axiosConfig, 'Starting Topology Browser...') const response = await this.httpClient.request(axiosConfig)
if (!response.data.treeNodes) { if (!response.data.treeNodes) {
console.log('Nothing in initial promt!'.red) throw new Error('Nothing in initial promt‼')
return }
} const { moType, moName, poId } = response.data.treeNodes[0]
const { moType, moName, poId } = response.data.treeNodes[0] this.currentPoId = poId
this.currentPoId = poId this.nextPoId = poId
this.nextPoId = poId this.nextVariants = async (input) => await this.nextObjects(input)
this.nextVariants = async (input) => await this.nextObjects(input) this.fdn = `${moType}=${moName}`
return `${moType}=${moName}` }
}
module.exports = initialPrompt module.exports = initialPrompt

View File

@@ -0,0 +1,14 @@
async function nextAttributes(input) {
const filter = input ? input : ''
this.commands = this.configCommands
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
this.choices = this.attributes
.map(item => item.key)
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
.sort((a, b) => a > b ? 1 : -1)
}
module.exports = nextAttributes

View File

@@ -0,0 +1,57 @@
const chalk = require("chalk")
const logError = require('../../../../util/logError')
const otherCommands = ['show', 'config', 'up', 'home', 'fdn', 'search', 'persistent', 'alarms', 'sync', 'exit']
function getSyncStatus(child) {
if (child.syncStatus === 'SYNCHRONIZED') return '\t\t✅'
if (child.syncStatus === 'UNSYNCHRONIZED') return '\t\t⏳'
return child.syncStatus ? '\t\t❓' : ''
}
function getRadioAccessTechnology(child) {
return child.radioAccessTechnology ? ' ' + chalk.bold.cyan(child.radioAccessTechnology.join(' ')) : ''
}
async function networkRequest() {
try {
const axiosConfig = {
text: 'Loading network data...',
method: 'get',
url: `${this.objectUrl}network/${this.currentPoId}`
}
const { data: { treeNodes } } = await this.httpClient.request(axiosConfig)
if (treeNodes) {
this.childrens = treeNodes[0].childrens
}
} catch (error) {
logError(error)
}
}
async function nextObjects(input){
const filter = input ? input : ''
if (this.currentPoId !== this.nextPoId || this.childrens.length === 0) {
this.currentPoId = this.nextPoId
this.poIds.push(this.currentPoId)
await networkRequest.call(this)
}
this.commands = otherCommands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase()))
this.choices = this.childrens
.map(child => {
const st = getSyncStatus(child)
const rt = getRadioAccessTechnology(child)
const ne = child.neType ? ' ' + chalk.dim.gray(child.neType) : ''
return `${child.moType}=${child.moName}${st}${rt}${ne}`
})
.filter(child => child.toLowerCase().includes(filter.toLowerCase()))
.concat(filter.startsWith('show') ? [filter] : [])
.concat(filter.startsWith('fdn') ? [filter] : [])
}
module.exports = nextObjects

View File

@@ -0,0 +1,127 @@
const chalk = require('chalk')
const inquirer = require('inquirer')
const { isValidNodeName } = require('../../../../util/validation')
async function getNodeTypes() {
const axiosConfig = {
text: 'Getting Node Types...',
method: 'get',
url: this.nodeTypesUrl,
}
const response = await this.httpClient.request(axiosConfig)
return response.data
}
async function getPoIds(query) {
const axiosConfig = {
text: 'Searching Nodes...',
method: 'get',
url: this.nodeSearchUrl,
headers: {
'X-Netex-Scoping-Panel': true,
},
params: {
query,
orderby: 'moName',
orderdirection: 'asc',
}
}
const { data: { objects } } = await this.httpClient.request(axiosConfig)
return objects.map(item => item.id)
}
async function getNodesData(poList) {
const axiosConfig = {
text: 'Getting Nodes Data...',
method: 'post',
url: this.nodePoIdsUrl,
data: {
poList,
defaultMappings: ["syncStatus","managementState","radioAccessTechnology","parentNeType"],
attributeMappings:[{"moType":"MeContext","attributeNames":["neType"]}]
}
}
const response = await this.httpClient.request(axiosConfig)
return response.data
}
function resultOutput(result) {
console.log('')
result.forEach(item => {
const {
fdn,
attributes,
cmSyncStatus,
radioAccessTechnology,
} = item
console.log(`${cmSyncStatus === 'SYNCHRONIZED' ? '✅' : '⏳'} ${fdn} ${chalk.dim.grey(attributes.neType)} ${chalk.bold.cyan(radioAccessTechnology.join(' '))}`)
})
console.log('')
}
function filterTypes(input, nodeTypes){
const filter = input ? input : ''
return nodeTypes.filter(
item => item
.toLowerCase()
.includes(filter.toLowerCase())
)
}
function buildQuery(nodeName, nodeType, rat) {
const ratFilter = rat.length !== 0
? ` filter by radioAccessTechnology ${rat.length === 1 ? 'containing ' : 'contains any of '}${rat.join(',')}`
: ''
const typeFilter = nodeType === 'ALL'
? `with name = ${nodeName}`
: `of type ${nodeType} where name = ${nodeName}`
return `select all nodes ${nodeName && typeFilter}${ratFilter}`
}
async function search() {
const nodeTypes = await getNodeTypes.call(this)
nodeTypes.push('ALL')
const { nodeName, nodeType, rat } = await inquirer.prompt([
{
type: 'autocomplete',
name: 'nodeType',
message: 'Select Node Type (default ALL):',
pageSize: 10,
default: 'ALL',
source: async (answers, input) => filterTypes(input, nodeTypes)
},
{
type: 'checkbox',
name: 'rat',
message: 'Select Radio Access Technologies (default all):',
choices: [
{name: '2G'},
{name: '3G'},
{name: '4G'},
{name: '5G'},
]
},
{
type: 'input',
name: 'nodeName',
message: 'Type a Node Name:',
validate: isValidNodeName
}
])
const poList = await getPoIds.call(this, buildQuery(nodeName, nodeType, rat))
if (poList.length === 0) {
throw new Error('Nodes not found❗')
}
const result = await getNodesData.call(this, poList)
resultOutput(result)
}
module.exports = search

View File

@@ -1,28 +1,26 @@
const colors = require('colors') const inputByType = require('../inputValue')
const inputByType = require('../inputValue')
async function set() {
async function set() { const attributeData = this.attributesData.find(item => item.key === this.attribute)
const attributeData = this.attributesData.filter(item => item.key === this.attribute)[0] if (!attributeData) return
if (!attributeData) return if (attributeData.writeBehavior === 'NOT_ALLOWED' || attributeData.immutable) {
if (attributeData.writeBehavior === 'NOT_ALLOWED' || attributeData.immutable) { throw new Error('Attribute Is ReadOnly')
console.log('Attribute Is ReadOnly'.yellow) }
return if (this.isConfig) {
} const found = this.configSet.find(item => item.key === this.attribute)
if (this.isConfig) { const { value } = await inputByType(attributeData)
const found = this.configSet.filter(item => item.key === this.attribute)[0] if (found) {
const { value } = await inputByType(attributeData) found.value = value
if (found) { } else {
found.value = value this.configSet.push(
} else { {
this.configSet.push( key: this.attribute,
{ value,
key: this.attribute, datatype: attributeData.type,
value, }
datatype: attributeData.type, )
} }
) }
} }
} module.exports = set
}
module.exports = set

View File

@@ -0,0 +1,12 @@
function setAttribute(attribute) {
if (!this.attributes) return false
if (!this.attributes.find(item => item.key === attribute)) return false
if (!this.attribute) {
['get', 'set', 'description'].forEach(cmd => this.configCommands.push(cmd))
}
this.attribute = attribute
return true
}
module.exports = setAttribute

View File

@@ -1,11 +1,11 @@
function setIdByCommand(command) { function setIdByCommand(command) {
const nextChild = this.childrens.filter(child => `${child.moType}=${child.moName}` === command)[0] const nextChild = this.childrens.find(child => `${child.moType}=${child.moName}` === command)
if (nextChild) { if (nextChild) {
this.nextPoId = nextChild.poId this.nextPoId = nextChild.poId
return true return true
} }
return false return false
} }
module.exports = setIdByCommand module.exports = setIdByCommand

View File

@@ -1,20 +1,20 @@
const requestWrapper = require('../../util/requestWrapper') const logAttributes = require('../../../../util/logAttributes')
const logAttributes = require('../../util/logAttributes')
async function show(filter) {
async function show(filter) { const axiosConfig = {
const axiosConfig = { text: 'Getting Atrributes...',
method: 'get', method: 'get',
url: `${this.objectUrl}${this.currentPoId}`, url: `${this.objectUrl}${this.currentPoId}`,
params: { params: {
includeNonPersistent: this.includeNonPersistent, includeNonPersistent: this.includeNonPersistent,
stringifyLong: true stringifyLong: true
} }
} }
const response = await requestWrapper(axiosConfig) const response = await this.httpClient.request(axiosConfig)
if (!response.data.fdn || !response.data.attributes) return if (!response.data.fdn || !response.data.attributes) return
logAttributes(response.data.fdn, response.data.attributes.filter(item => item.key.match(filter))) logAttributes(response.data.fdn, response.data.attributes.filter(item => item.key.match(filter)))
} }
module.exports = show module.exports = show

View File

@@ -0,0 +1,26 @@
const chalk = require("chalk")
async function sync() {
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
if (!meContextFind) {
throw new Error('No sync object in FDN!')
}
const actionUrl = `${this.objectUrl}v1/perform-mo-action/NetworkElement=${meContextFind[2]},CmFunction=1?actionName=sync`
const axiosConfig = {
text: 'Initiate Node Sync...',
method: 'post',
url: actionUrl,
headers: {
'Content-Type': 'application/json'
},
}
const response = await this.httpClient.request(axiosConfig)
console.log(`
${chalk.bold(response.data.body)}
${chalk.green(response.data.title)}
`)
}
module.exports = sync

View File

@@ -1,12 +1,13 @@
function up() { function up() {
if (this.poIds.length > 1) { if (this.poIds.length > 1) {
this.poIds.pop() this.poIds.pop()
this.nextPoId = this.poIds.pop() this.nextPoId = this.poIds.pop()
this.currentPoId = this.poIds[this.poIds.length - 1] this.currentPoId = this.poIds[this.poIds.length - 1]
return true this.fdn = this.fdn.split(',').slice(0, -1).join(',')
} return
return false }
} throw new Error('There\'s no way up!')
}
module.exports = up
module.exports = up

View File

@@ -0,0 +1,25 @@
const mainHelp = [
'',
'show [<valid regex>] - shows current object\'s attributes filtered with regex',
'config - enters config mode',
'up - navigate up one level',
'fdn [<valid FDN>] - navigate to FDN',
'home - navigate to root folder',
'alarms - show alarms',
'sync - initiate node CM synchronization',
'persistent - toggle persistent attributes inclusion',
'exit - logout and exit application',
].join('\n')
const configHelp = [
'',
'set - set attribute\'s value',
'get - get attribute\'s value',
'commit - commiting changes to the network',
'check - view configuration changes',
'end - exit config mode without commiting',
'exit - logout and exit application',
].join('\n')
module.exports = { mainHelp, configHelp }

View File

@@ -0,0 +1,109 @@
const inquirer = require('inquirer')
const chalk = require('chalk')
const logError = require('../../../util/logError')
const { isEmpty } = require('../../../util/validation')
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
function commandOther(tplg, command) {
const spl = command.split(/\s+/).find(item => item)
if (tplg.setIdByCommand(spl)) {
tplg.fdn = `${tplg.fdn},${spl}`
} else if (tplg.setAttribute(command)) {
tplg.fdn = tplg.fdn.replace(/\((\w+)\)/g, `(${command})`)
} else {
throw new Error('Command Unrecognized❗')
}
}
async function handleCommand(tplg, command) {
const [cmd, param] = command.split(/\s+/)
const cmdMatch = cmd.match(/\[(\w+)\]/)
const cmdName = cmdMatch ? cmdMatch[1] : cmd
switch (cmdName) {
case 'exit':
tplg.fdn = ''
break
case 'show':
await tplg.show(param ? param.trim() : '')
break
case 'config':
await tplg.config()
break
case 'set':
await tplg.set()
break
case 'commit':
await tplg.commit()
break
case 'up':
tplg.up()
break
case 'get':
tplg.get()
break
case 'check':
tplg.check()
break
case 'end':
tplg.end()
break
case 'home':
tplg.home()
break
case 'search':
await tplg.search()
break
case 'description':
tplg.description()
break
case 'persistent':
tplg.persistent()
break
case 'fdn':
await tplg.goToFdn(param ? param.trim() : '')
break
case 'alarms':
await tplg.alarms()
break
case 'sync':
await tplg.sync()
break
default:
commandOther(tplg, command)
}
}
async function inputHandler() {
await this.initialPrompt()
while (true) {
try {
const input = await inquirer.prompt([
{
type: 'autocomplete',
name: 'command',
message: chalk.blue(this.getPrompt()),
pageSize: 10,
prefix: '',
suffix: this.isConfig ? chalk.blue('#') : chalk.blue('>'),
validate: isEmpty,
source: async (answers, input) => await this.next(input),
emptyText: this.help,
}
])
await handleCommand(this, input.command)
if (!this.fdn) break
} catch (error) {
logError(error)
}
}
}
module.exports = inputHandler

View File

@@ -1,156 +1,157 @@
const colors = require('colors') const chalk = require('chalk')
const inquirer = require('inquirer') const inquirer = require('inquirer')
const banner = require('../util/banner') const banner = require('../../../util/banner')
const { isValidNumber, isValidString, checkValueRangeConstraints } = require('../util/validation') const { isValidNumber, isValidString, checkValueRangeConstraints } = require('../../../util/validation')
async function inputInteger(attributeData) { async function inputInteger(attributeData) {
const message = `${attributeData.key.yellow} { ${attributeData.unit ? attributeData.unit : 'parrots'.gray} } (${attributeData.type}): ` const message = `${chalk.yellow(attributeData.key)} { ${attributeData.unit ? attributeData.unit : chalk.gray('parrots')} } (${attributeData.type}): `
const input = await inquirer.prompt([ const input = await inquirer.prompt([
{ {
type: 'input', type: 'input',
name: 'value', name: 'value',
suffix: '?'.green, suffix: chalk.green('?'),
message, message,
default: attributeData.defaultValue, default: attributeData.defaultValue,
validate: input => isValidNumber(input, attributeData.constraints), validate: input => isValidNumber(input, attributeData.constraints),
} }
]) ])
return +input.value return +input.value
} }
async function inputEnumRef(attributeData) { async function inputEnumRef(attributeData) {
const message = `Select Value For ${attributeData.key.yellow}: ` const message = `Select Value For ${chalk.yellow(attributeData.key)}: `
const input = await inquirer.prompt([ const input = await inquirer.prompt([
{ {
type: 'list', type: 'list',
name: 'value', name: 'value',
suffix: '?'.green, suffix: chalk.green('?'),
message, message,
choices: attributeData.enumeration.enumMembers.map(item => ({ name: `${item.key} (${item.description})`, value: item.key, short: item.key })), choices: attributeData.enumeration.enumMembers.map(item => ({ name: `${item.key} (${item.description})`, value: item.key, short: item.key })),
default: attributeData.defaultValue, default: attributeData.defaultValue,
} }
]) ])
return input.value return input.value
} }
async function inputBoolean(attributeData) { async function inputBoolean(attributeData) {
const variants = ['true', 'false'] const variants = ['true', 'false']
if (attributeData.constraints.nullable) variants.push('null') if (attributeData.constraints.nullable) variants.push('null')
const message = `Select Value For ${attributeData.key.yellow}:` const message = `Select Value For ${chalk.yellow(attributeData.key)}:`
const input = await inquirer.prompt([ const input = await inquirer.prompt([
{ {
type: 'list', type: 'list',
name: 'value', name: 'value',
suffix: '?'.green, suffix: chalk.green('?'),
message, message,
choices: variants, choices: variants,
default: String(attributeData.defaultValue), default: String(attributeData.defaultValue),
} }
]) ])
return { return {
true: true, true: true,
false: false, false: false,
null: null null: null
}[input.value] }[input.value]
} }
async function inputString(attributeData) { async function inputString(attributeData) {
const message = `${attributeData.key.yellow} (${attributeData.type}): ` const message = `${chalk.yellow(attributeData.key)} (${attributeData.type}): `
const input = await inquirer.prompt([ const input = await inquirer.prompt([
{ {
type: 'input', type: 'input',
name: 'value', name: 'value',
suffix: '?'.green, suffix: chalk.green('?'),
message, message,
default: attributeData.defaultValue, default: attributeData.defaultValue,
validate: input => isValidString(input, attributeData.constraints), validate: input => isValidString(input, attributeData.constraints),
} }
]) ])
return input.value return input.value
} }
async function inputList(attributeData) { async function inputList(attributeData) {
const message = `${attributeData.key.yellow} List Of (${attributeData.listReference.type}) Size: ` const message = `${chalk.yellow(attributeData.key)} List Of (${attributeData.listReference.type}) Size: `
const result = [] const result = []
const { value } = await inquirer.prompt([ const { value } = await inquirer.prompt([
{ {
type: 'number', type: 'number',
name: 'value', name: 'value',
suffix: '?'.green, suffix: chalk.green('?'),
message, message,
default: 'null', default: 'null',
validate: input => +input > 0 || input === 'null', validate: input => +input > 0 || input === 'null',
} }
]) ])
if (value === 'null') return null if (value === 'null') return null
const checkResult = checkValueRangeConstraints(value, attributeData.constraints) const checkResult = checkValueRangeConstraints(value, attributeData.constraints)
if (checkResult) return checkResult if (checkResult) return checkResult
if (attributeData.constraints.orderingConstraint) banner(attributeData) if (attributeData.constraints.orderingConstraint) banner(attributeData)
await inputListValues(attributeData, value, result) await inputListValues(attributeData, value, result)
return result.value ? result.value : result return result.value ? result.value : result
} }
async function inputListValues(attributeData, listSize, result) { async function inputListValues(attributeData, listSize, result) {
for (let i = 0; i < listSize; i++) { for (let i = 0; i < listSize; i++) {
let input let input
if (!attributeData.listReference.key) { if (!attributeData.listReference.key) {
attributeData.listReference.key = `${attributeData.key} [${i}]` attributeData.listReference.key = `${attributeData.key} [${i}]`
input = await inputByType(attributeData.listReference) input = await inputByType(attributeData.listReference)
attributeData.listReference.key = null attributeData.listReference.key = null
} else { } else {
input = await inputByType(attributeData.listReference) input = await inputByType(attributeData.listReference)
} }
if (attributeData.constraints.uniqueMembers) { if (attributeData.constraints.uniqueMembers) {
if (result.indexOf(input.value) !== -1) { if (result.indexOf(input.value) !== -1) {
console.log('>>Array Values Should Be Unique'.red) console.log(chalk.red('>>Array Values Should Be Unique'))
i-- i--
continue continue
} }
} }
result.push(input.value) result.push(input.value)
} }
} }
async function inputComplexRef(attributeData) { async function inputComplexRef(attributeData) {
const result = [] const result = []
const attributes = attributeData.complexRef.attributes.slice() const attributes = attributeData.complexRef.attributes.slice()
while (attributes.length > 0) { while (attributes.length > 0) {
const item = attributes.shift() const item = attributes.shift()
const { value } = await inputByType(item) const { value } = await inputByType(item)
result.push({ result.push({
key: item.key, key: item.key,
value, value,
datatype: item.type datatype: item.type
}) })
} }
return result return result
} }
async function inputByType(typeReference) { async function inputByType(typeReference) {
const inputs = { const inputs = {
INTEGER: inputInteger, INTEGER: inputInteger,
SHORT: inputInteger, SHORT: inputInteger,
ENUM_REF: inputEnumRef, LONG: inputInteger,
BOOLEAN: inputBoolean, ENUM_REF: inputEnumRef,
STRING: inputString, BOOLEAN: inputBoolean,
MO_REF: inputString, STRING: inputString,
LIST: inputList, MO_REF: inputString,
COMPLEX_REF: inputComplexRef LIST: inputList,
} COMPLEX_REF: inputComplexRef
if (inputs[typeReference.type]) { }
const input = await inputs[typeReference.type](typeReference) if (inputs[typeReference.type]) {
return { value: input } const input = await inputs[typeReference.type](typeReference)
} return { value: input }
banner(typeReference) }
} banner(typeReference)
}
module.exports = inputByType
module.exports = inputByType

View File

@@ -1,48 +1,52 @@
const axios = require('axios') const axios = require('axios')
const chalk = require('chalk') const https = require('https')
const https = require('https') const axiosCookieJarSupport = require('axios-cookiejar-support').default
const axiosCookieJarSupport = require('axios-cookiejar-support').default const tough = require('tough-cookie')
const tough = require('tough-cookie') const logError = require('../../util/logError')
const SpinnerWithCounter = require('../../../util/SpinnerWithCounter') const SpinnerWithCounter = require('./SpinnerWithCounter')
axiosCookieJarSupport(axios) axiosCookieJarSupport(axios)
let spinner = new SpinnerWithCounter() let spinner = new SpinnerWithCounter()
function beforeRequest(config) { function beforeRequest(config) {
const {text = 'Executing request...', ...newConfig} = config const {text = 'Executing request...', ...newConfig} = config
spinner.start(text) spinner.start(text)
return newConfig return newConfig
} }
function errorRequest(error) { function errorRequest(error) {
spinner.fail() spinner.fail()
return Promise.reject(error) if (401 === error.response.status) {
} logError(error)
process.exit(1)
}
function beforeResponse(response) { return Promise.reject(error)
spinner.succeed() }
return response
}
function beforeResponse(response) {
spinner.succeed()
function axiosHttpClient(url) { return response
const axiosClient = axios.create({ }
baseURL: url,
httpsAgent: new https.Agent({
rejectUnauthorized: false function axiosHttpClient(url) {
}), const axiosClient = axios.create({
withCredentials: true, baseURL: url,
jar: new tough.CookieJar(), httpsAgent: new https.Agent({
}) rejectUnauthorized: false
axiosClient.interceptors.request.use(beforeRequest, errorRequest) }),
axiosClient.interceptors.response.use(beforeResponse, errorRequest) withCredentials: true,
return axiosClient jar: new tough.CookieJar(),
} })
axiosClient.interceptors.request.use(beforeRequest, errorRequest)
axiosClient.interceptors.response.use(beforeResponse, errorRequest)
return axiosClient
}
module.exports = axiosHttpClient module.exports = axiosHttpClient

63
lib/components/ENM/ENM.js → lib/components/ENM.js Normal file → Executable file
View File

@@ -1,32 +1,33 @@
const chalk = require('chalk') const axiosHttpClient = require('./AxiosHttpClient')
const axiosHttpClient = require('../AxiosHttpClient/AxiosHttpClient')
class ENM {
class ENM { constructor(username, password, url) {
constructor(username, password, url) { this.logoutUrl = '/logout'
this.logoutUrl = '/logout' this.loginUrl = `/login?IDToken1=${username}&IDToken2=${password}`
this.loginUrl = `/login?IDToken1=${username}&IDToken2=${password}` this.commands = null
this.httpClient = axiosHttpClient(url) this.choices = null
} this.httpClient = axiosHttpClient(url)
}
async login() {
const axiosConfig = { async login() {
text: 'Login in...', const axiosConfig = {
method: 'post', text: 'Login in...',
url: this.loginUrl method: 'post',
} url: this.loginUrl
const response = await this.httpClient.request(axiosConfig) }
return response.data const response = await this.httpClient.request(axiosConfig)
} return response.data
}
async logout() {
const axiosConfig = { async logout() {
text: 'Logout...', const axiosConfig = {
method: 'get', text: 'Logout...',
url: this.logoutUrl method: 'get',
} url: this.logoutUrl
await this.httpClient.request(axiosConfig) }
} await this.httpClient.request(axiosConfig)
} }
}
module.exports = ENM module.exports = ENM

View File

@@ -1,35 +1,35 @@
const ora = require('ora') const ora = require('ora')
class SpinnerWithCounter { class SpinnerWithCounter {
constructor() { constructor() {
this.spinner = null this.spinner = null
this.counter = 0 this.counter = 0
} }
start(text) { start(text) {
if (!this.spinner) { if (!this.spinner) {
this.spinner = ora(text) this.spinner = ora(text)
this.spinner.start() this.spinner.start()
} }
this.counter = ++this.counter this.counter = ++this.counter
} }
succeed() { succeed() {
this.counter = --this.counter this.counter = --this.counter
if (this.spinner && this.counter === 0) { if (this.spinner && this.counter === 0) {
this.spinner.succeed() this.spinner.succeed()
this.spinner = null this.spinner = null
} }
} }
fail() { fail() {
this.counter = --this.counter this.counter = --this.counter
if (this.spinner && this.counter === 0) { if (this.spinner && this.counter === 0) {
this.spinner.fail() this.spinner.fail()
this.spinner = null this.spinner = null
} }
} }
} }
module.exports = SpinnerWithCounter module.exports = SpinnerWithCounter

View File

@@ -1,132 +0,0 @@
const logAttributes = require('../util/logAttributes')
const inputHandler = require('./inputHandler')
const alarms = require('../commands/alarms')
const sync = require('../commands/sync')
const initialPrompt = require('../commands/initialPrompt')
const nextObjects = require('../commands/nextObjects')
const nextAttributes = require('../commands/nextAttributes')
const setIdByCommand = require('../commands/setIdByCommand')
const show = require('../commands/show')
const up = require('../commands/up')
const config = require('../commands/config')
const end = require('../commands/end')
const setAttribute = require('../commands/setAttribute')
const description = require('../commands/description')
const get = require('../commands/get')
const set = require('../commands/set')
const commit = require('../commands/commit')
const home = require('../commands/home')
const fdn = require('../commands/fdn')
const ENM = require('../ENM/ENM')
class TopologyBrowser extends ENM {
constructor(username, password, url) {
super(username, password, url)
this.objectUrl = '/persistentObject/'
this.alarmUrl = '/alarmcontroldisplayservice/alarmMonitoring/alarmoperations/'
this.currentPoId = 0
this.nextPoId = 1
this.childrens = null
this.poIds = []
this.isConfig = false
this.attributes = null
this.nextVariants = null
this.attributesData = null
this.attribute = null
this.networkDetails = null
this.configSet = []
this.includeNonPersistent = false
this.configCommands = ['commit', 'check', 'end', 'persistent', 'exit']
this.help = 'No results...'
}
async initialPrompt() {
return await initialPrompt.call(this)
}
async next(input) {
return await this.nextVariants(input)
}
async nextObjects(input) {
return await nextObjects.call(this, input)
}
async nextAttributes(input) {
return await nextAttributes.call(this, input)
}
setIdByCommand(command) {
return setIdByCommand.call(this, command)
}
async show(filter) {
await show.call(this, filter)
}
up() {
return up.call(this)
}
async config(fdn) {
return await config.call(this, fdn)
}
end() {
end.call(this)
}
setAttribute(attribute) {
return setAttribute.call(this, attribute)
}
description() {
description.call(this)
}
get(fdn) {
get.call(this, fdn)
}
async set() {
await set.call(this)
}
async commit(fdn) {
return await commit.call(this, fdn)
}
check(fdn) {
logAttributes(fdn, this.configSet)
}
home() {
home.call(this)
}
async fdn(fromFdn, targetFdn) {
return await fdn.call(this, fromFdn, targetFdn)
}
persistent() {
this.includeNonPersistent = !this.includeNonPersistent
console.log(`Include Non Persistent Atrributes Set to: ${this.includeNonPersistent}`.yellow)
}
async alarms(fdn) {
await alarms.call(this, fdn)
}
async sync(fdn) {
await sync.call(this, fdn)
}
async inputHandler() {
await inputHandler.call(this)
}
}
module.exports = TopologyBrowser

View File

@@ -1,34 +0,0 @@
const colors = require('colors')
const logAttributes = require('../../util/logAttributes')
const requestWrapper = require('../../util/requestWrapper')
const logCommit = require('../../util/logCommit')
async function commit(fdn) {
logAttributes(fdn, this.configSet)
this.configSet.forEach(item => delete item.from)
const axiosConfig = {
method: 'put',
url: `${this.objectUrl}${this.currentPoId}`,
data: {
poId: this.currentPoId,
fdn,
attributes: this.configSet,
},
headers: {
'Content-Type': 'application/json'
}
}
const response = await requestWrapper(axiosConfig, 'Commiting Config...')
if (response.data) {
logCommit(response.data)
} else {
console.log('No data or response!'.red)
}
this.configSet.length = 0
this.end()
return fdn
}
module.exports = commit

View File

@@ -1,21 +0,0 @@
const colors = require('colors')
const logAttributeData = require('../../util/logAttributeData')
function description() {
const attributeData = this.attributesData.filter(item => item.key === this.attribute)[0]
if (attributeData) {
logAttributeData(attributeData)
if (attributeData.complexRef) {
console.log(`${attributeData.type.magenta}
${attributeData.complexRef.key.cyan}: ${attributeData.complexRef.description.grey}
`)
attributeData.complexRef.attributes.forEach(attr => logAttributeData(attr))
}
} else {
console.log('Attribute Not Found!'.yellow)
}
}
module.exports = description

View File

@@ -1,20 +0,0 @@
const colors = require('colors')
const logAttributes = require('../../util/logAttributes')
const banner = require('../../util/banner')
function get(fdn) {
const syncStatus = this.networkDetails.filter(item => item.key === 'syncStatus')[0]
if (syncStatus && syncStatus.value === 'UNSYNCHRONIZED') {
console.log(`
${syncStatus.key}: ${syncStatus.value}`.yellow)
}
const attribute = this.attributes.filter(item => item.key === this.attribute)[0]
const attributeData = this.attributesData.filter(item => item.key === this.attribute)[0]
logAttributes(fdn, [attribute])
console.log(` ${'Type: '.green + attributeData['type']} ${attributeData['defaultValue'] ? 'Default: '.yellow + attributeData['defaultValue'] : ''}
`)
if (attributeData.constraints && attributeData.constraints.orderingConstraint) banner(attributeData)
}
module.exports = get

View File

@@ -1,14 +0,0 @@
async function nextAttributes(input) {
const filter = input ? input : ''
let result = this.attributes.map(item => item.key).sort((a, b) => a > b ? 1 : -1)
.concat(this.configCommands)
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
if (result.includes(filter)) {
result = result.filter(item => item !== filter)
result.unshift(filter)
}
return result
}
module.exports = nextAttributes

View File

@@ -1,34 +0,0 @@
const requestWrapper = require('../../util/requestWrapper')
const otherCommands = ['show', 'config', 'up', 'home', 'fdn', 'persistent', 'alarms', 'sync', 'exit']
async function nextObjects(input){
const filter = input ? input : ''
if (this.currentPoId !== this.nextPoId || !this.childrens) {
this.currentPoId = this.nextPoId
this.poIds.push(this.currentPoId)
const axiosConfig = {
method: 'get',
url: `${this.objectUrl}network/${this.currentPoId}`
}
const response = await requestWrapper(axiosConfig)
if (response.data.treeNodes) {
this.childrens = response.data.treeNodes[0].childrens
}
}
let result = this.childrens
.map(child => `${child.moType}=${child.moName}`)
.concat(otherCommands)
.filter(child => child.toLowerCase().includes(filter.toLowerCase()))
.concat(filter.startsWith('show') ? [filter] : [])
.concat(filter.startsWith('fdn') ? [filter] : [])
if (result.includes(filter)) {
result = result.filter(item => item !== filter)
result.unshift(filter)
}
return result
}
module.exports = nextObjects

View File

@@ -1,14 +0,0 @@
function setAttribute(attribute) {
if (!this.attributes) return false
if (!this.attributes.filter(item => item.key === attribute)[0]) return false
if (!this.attribute) {
this.configCommands.push('get')
this.configCommands.push('set')
this.configCommands.push('description')
}
this.attribute = attribute
return true
}
module.exports = setAttribute

View File

@@ -1,30 +0,0 @@
const colors = require('colors')
const requestWrapper = require('../../util/requestWrapper')
async function sync(fdn) {
const meContextFind = fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
if (!meContextFind) {
console.log('No sync object in FDN!'.yellow)
return
}
const actionUrl = `${this.objectUrl}v1/perform-mo-action/NetworkElement=${meContextFind[2]},CmFunction=1?actionName=sync`
const axiosConfig = {
method: 'post',
url: actionUrl,
headers: {
'Content-Type': 'application/json'
},
}
const response = await requestWrapper(axiosConfig, 'Initiate Node Sync...')
if (response.status === 200) {
console.log(`
${response.data.body.bold}
${response.data.title.green}
`)
}
}
module.exports = sync

View File

@@ -1,136 +0,0 @@
const inquirer = require('inquirer')
const colors = require('colors')
const logError = require('../util/logError')
const { isEmpty } = require('../util/validation')
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
function buildPrompt(fdn) {
if (fdn.length >= 67) {
return { prefix: '...', prompt: fdn.slice(-65) }
}
return { prefix: '', prompt: fdn }
}
function commndUp(tplg, fdn) {
if (tplg.up()) {
fdn = fdn.split(',').slice(0, -1).join(',')
} else {
console.log('There\'s no way up!'.yellow)
}
return fdn
}
function commandOther(tplg, fdn, command) {
if (tplg.setIdByCommand(command)) {
fdn = `${fdn},${command}`
} else if (tplg.setAttribute(command)) {
fdn = fdn.replace(/\((\w+)\)/g, `(${command})`)
} else {
console.log('Command Unrecognized❗'.red)
}
return fdn
}
async function handleCommand(tplg, fdn, command) {
const [cmd, param] = command.split(/\s+/)
switch (cmd) {
case 'exit':
return
case 'show':
await tplg.show(param ? param.trim() : '')
break
case 'config':
fdn = await tplg.config(fdn)
break
case 'set':
await tplg.set()
break
case 'commit':
fdn = await tplg.commit(fdn.replace(/\((\w+)\)/g, ''))
break
case 'up':
fdn = commndUp(tplg, fdn)
break
case 'get':
tplg.get(fdn)
break
case 'check':
tplg.check(fdn.replace(/\((\w+)\)/g, ''))
break
case 'end':
tplg.end()
fdn = fdn.replace(/\((\w+)\)/g, '')
break
case 'home':
tplg.home()
fdn = fdn.split(',', 1)[0]
break
case 'description':
tplg.description()
break
case 'persistent':
tplg.persistent()
break
case 'fdn':
fdn = await tplg.fdn(fdn, param ? param.trim() : '')
break
case 'alarms':
await tplg.alarms(fdn)
break
case 'sync':
await tplg.sync(fdn)
break
default:
fdn = commandOther(tplg, fdn, command)
}
return fdn
}
async function inputHandlerLoop(tplg) {
let prompt = await tplg.initialPrompt()
let fdn = prompt
let prefix = ''
while (true) {
try {
const input = await inquirer.prompt([
{
type: 'autocomplete',
name: 'command',
message: tplg.isConfig ? prompt.blue.underline : prompt.blue,
pageSize: 10,
prefix: prefix.gray,
suffix: tplg.isConfig ? '#'.blue : '>'.blue,
validate: isEmpty,
source: async (answers, input) => await tplg.next(input),
emptyText: prvn.help,
}
])
fdn = await handleCommand(tplg, fdn, input.command)
if (!fdn) break
({ prefix, prompt } = buildPrompt(fdn))
} catch (error) {
logError(error)
}
}
}
async function inputHandler() {
try {
await inputHandlerLoop(this)
} catch (error) {
logError(error)
}
}
module.exports = inputHandler

86
package.json Normal file → Executable file
View File

@@ -1,43 +1,43 @@
{ {
"name": "enm-cli", "name": "enm-cli",
"version": "1.0.0", "version": "1.0.0",
"description": "This is a cli application for Ericsson Network Manager (ENM)", "description": "This is a cli application for Ericsson Network Manager (ENM)",
"bin": "./bin/enm-cli.js", "bin": "./bin/enm-cli.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"keywords": [ "keywords": [
"Ericsson", "Ericsson",
"ENM", "ENM",
"Network", "Network",
"Manager", "Manager",
"LTE", "LTE",
"3G", "3G",
"4G", "4G",
"5G", "5G",
"Automation", "Automation",
"GSM", "GSM",
"UMTS", "UMTS",
"OSS", "OSS",
"Cellular", "Cellular",
"REST", "REST",
"AutoProvisioning", "AutoProvisioning",
"TopologyBrowser", "TopologyBrowser",
"API" "API"
], ],
"author": "vvsviridov", "author": "vvsviridov",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^0.21.2", "axios": "^0.21.2",
"axios-cookiejar-support": "^1.0.1", "axios-cookiejar-support": "^1.0.1",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"commander": "^9.1.0", "commander": "^9.1.0",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"inquirer": "^8.2.2", "inquirer": "^8.2.2",
"inquirer-autocomplete-prompt": "^2.0.0", "inquirer-autocomplete-prompt": "^2.0.0",
"inquirer-file-tree-selection-prompt": "^1.0.18", "inquirer-file-tree-selection-prompt": "^1.0.18",
"ora": "^5.4.1", "ora": "^5.4.1",
"tough-cookie": "^4.0.0" "tough-cookie": "^4.0.0"
} }
} }

11
util/banner.js Executable file
View File

@@ -0,0 +1,11 @@
function banner(params) {
console.log(`
If you see this, pls, send me this message:
${JSON.stringify(params, null, 2)}
If you see this, pls, send me this message:
`)
}
module.exports = banner

32
util/createNext.js Normal file → Executable file
View File

@@ -1,17 +1,17 @@
const inquirer = require('inquirer') const inquirer = require('inquirer')
function createNext(filter) { function createNext(filter) {
const separator = new inquirer.Separator() const separator = new inquirer.Separator()
const commands = this.commands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase())) const commands = this.commands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase()))
const choices = this.choices.filter(choice => choice.toLowerCase().includes(filter.toLowerCase())) const choices = this.choices.filter(choice => choice.toLowerCase().includes(filter.toLowerCase()))
return [ return [
separator, ...choices,
...commands.map(cmd => `[${cmd}]`), separator,
separator, ...commands.map(cmd => `[${cmd}]`),
...choices, separator,
] ]
} }
module.exports = createNext module.exports = createNext

10
util/eventTimeToString.js Executable file
View File

@@ -0,0 +1,10 @@
function eventTimeToString(eventTime) {
if (!eventTime) return ''
return new Date(eventTime).toISOString().slice(0, -1).split('T').join(' ')
// return `${eventDateTime.toLocaleDateString()} ${eventDateTime.toLocaleTimeString()}`
}
module.exports = eventTimeToString

26
util/logAlarm.js Executable file
View File

@@ -0,0 +1,26 @@
const chalk = require('chalk')
const eventTimeToString = require('./eventTimeToString')
const timeValues = [
'eventTime',
'insertTime',
'ceaseTime',
'ackTime',
]
function logAlarm(alarmList, eventPoId) {
const alarm = alarmList.filter(item => item.eventPoIdAsLong === eventPoId)[0]
timeValues.forEach(value => alarm[value] = eventTimeToString(alarm[value]))
console.log(
JSON.stringify(alarm, null, 2)
.replace(/["(){}\[\]]/mg, '')
.replace(/,$/mg, '')
.replace(/^(\s{2}\w+):/mg, chalk.green('$1:'))
)
}
module.exports = logAlarm

84
util/logAttributeData.js Executable file
View File

@@ -0,0 +1,84 @@
const chalk = require('chalk')
function logDefaultValue(value) {
return value ? ` default: ${value}` : ''
}
function logAttribute(key, attribute, output) {
let attrName = key.replace(/([A-Z])/g, ' $1')
if (attribute !== undefined && attribute !== '') {
output.push(`${chalk.blue(attrName.toLocaleUpperCase())}
${attribute}
`)
}
}
function logConstraints(constraints, output) {
output.push(`${chalk.blue(Object.keys({constraints}).pop().toLocaleUpperCase())}`)
if (constraints.valueRangeConstraints) {
constraints.valueRangeConstraints.forEach(item => {
output.push(` ${chalk.yellow('Range')}: ${item.minValue}..${item.maxValue}`)
})
}
['nullable', 'validContentRegex', 'valueResolution'].forEach(key => {
if (Object.keys(constraints).includes(key)) {
output.push(` ${chalk.yellow(key.replace(/([A-Z])/g, ' $1').replace(/^([a-z])/g, (l) => l.toUpperCase()))}: ${constraints[key]}`)
}
})
}
function logEnumeration(enumeration, output) {
output.push(`${chalk.blue(Object.keys({enumeration}).pop().toLocaleUpperCase())}
${chalk.cyan(enumeration.key)}
${enumeration.description}`)
enumeration.enumMembers.forEach(item => output.push(` ${chalk.yellow(item.key)} (${item.value}): -- ${chalk.gray(item.description)}`))
}
function logList(listReference, output) {
output.push(`${chalk.blue(Object.keys({listReference}).pop().toLocaleUpperCase())}
${listReference.type}`)
if (listReference.constraints){
logConstraints(listReference.constraints, output)
}
}
function logAttributeData(attributeData) {
const attributeDataKeys = [
'key',
'type',
'defaultValue',
'description',
'trafficDisturbances',
'unit',
'multiplicationFactor',
'immutable',
'precondition',
'dependencies',
'sideEffects',
'activeChoiceCase',
]
const output = [`
${chalk.yellow.bold(attributeData['key'])}: ${chalk.green(attributeData['type'])} ${logDefaultValue(attributeData['defaultValue'])}
`]
attributeDataKeys.slice(3).forEach((key) => logAttribute(key, attributeData[key], output))
if (attributeData.constraints) {
logConstraints(attributeData.constraints, output)
}
if (attributeData.enumeration) {
logEnumeration(attributeData.enumeration, output)
}
if (attributeData.listReference) {
logList(attributeData.listReference, output)
}
console.log(output.join('\n') + '\n')
}
module.exports = logAttributeData

34
util/logAttributes.js Executable file
View File

@@ -0,0 +1,34 @@
const chalk = require('chalk')
function transformAttributes(element) {
if (Array.isArray(element)) {
return element.map(item => transformAttributes(item))
}
if (Array.isArray(element.value)) {
return { [element.key]: transformAttributes(element.value) }
}
return element.key ? { [element.key]: element.value } : element
}
function colorize(attributes) {
const sorted = attributes.sort ? attributes.sort((a, b) => a.key < b.key ? -1 : 1) : attributes
return JSON.stringify(transformAttributes(sorted), null, 1)
.replace(/["(){}\[\]]/mg, '')
.replace(/^\s*,*\n/mg, '')
.replace(/,$/mg, '')
.replace(/^(\s{2}\w+):/mg, chalk.green('$1:'))
.replace(/^(\s{4}\w+):/mg, chalk.yellow('$1:'))
.replace(/^(\s{5}\w+):/mg, chalk.cyan('$1:'))
}
function logAttributes(fdn, attributes) {
const output = `
${chalk.yellow.bold('FDN')}: ${chalk.bold(fdn)}
${colorize(attributes)}`
console.log(output)
}
module.exports = logAttributes

10
util/logCommit.js Executable file
View File

@@ -0,0 +1,10 @@
const chalk = require('chalk')
function logCommit(commitResult) {
if (commitResult.title === 'Success') {
console.log(chalk.green(commitResult.title))
}
}
module.exports = logCommit

11
util/logDetails.js Executable file
View File

@@ -0,0 +1,11 @@
const chalk = require('chalk')
function logDetails(networkDetails) {
const output = networkDetails.map(details => ` ${chalk.gray(details.key)}: ${details.value === 'UNSYNCHRONIZED' ? '⌛ ' + chalk.yellow(details.value): chalk.gray(details.value)}`)
console.log(`
${output.join('\n')}
`)
}
module.exports = logDetails

82
util/logError.js Normal file → Executable file
View File

@@ -1,29 +1,53 @@
const chalk = require('chalk') const chalk = require('chalk')
function logError(err) { function logError(err) {
if (err.response && err.response.data && err.response.data.errorTitle) { try {
const { if (!err.response) {
errorTitle = 'Error', const {
errorBody = 'No error body', name = 'Error',
errorDetails = null message = 'No error message',
} = err.response.data stack = null,
console.log(` } = err
⚠️ ${chalk.bold.bgRed(errorTitle)} console.log(`
${chalk.yellow(errorBody || '')}${errorDetails ? '\n' + errorDetails.toString() : ''} ${chalk.bold.bgRed(name)}
`) ${chalk.yellow(message)}${chalk.dim(stack && process.env.NODE_ENV === 'development' ? '\n' + stack : '')}
} else { `)
const { return
name = 'Error', }
message = 'No error message', if (err.response.data) {
stack = null, const { data } = err.response
} = err // other http error
console.log(` let errorTitle = `${err.response.status}: ${err.response.statusText}`
${chalk.bold.bgRed(name)} let errorBody = data.code
${chalk.yellow(message)} let errorDetails = data.message
${chalk.dim(stack && process.env.NODE_ENV === 'development' ? stack : '')} if (typeof data !== 'object') {
`) errorBody = data
} }
} if (data.errorCode) {
errorBody = `${data.errorCode}: ${data.userMessage.title}`
errorDetails = data.userMessage.body
module.exports = logError }
// prvn error
if (data.errorTitle) {
errorTitle = data.errorTitle
errorBody = data.errorBody
errorDetails = data.errorDetails
}
// tplg error
if (data.title) {
errorTitle = `${data.errorCode}: ${data.title}`
errorBody = data.body
errorDetails = data.detail
}
console.log(`
⚠️ ${chalk.bold.bgRed(errorTitle)}
${chalk.yellow(errorBody)}${errorDetails ? '\n' + errorDetails.toString() : ''}
`)
}
} catch (error) {
console.log(error)
}
}
module.exports = logError

96
util/logNode.js Normal file → Executable file
View File

@@ -1,49 +1,49 @@
const chalk = require('chalk') const chalk = require('chalk')
function logNodeStatus(statusEntries, indent = 4) { function logNodeStatus(statusEntries, indent = 4) {
if (!statusEntries) { if (!statusEntries) {
throw new Error('No node status entries!') throw new Error('No node status entries')
} }
console.log('') console.log('')
statusEntries.forEach(entry => { statusEntries.forEach(entry => {
const { task, progress, timestamp, additionalInfo } = entry const { task, progress, timestamp, additionalInfo } = entry
console.log(`${' '.repeat(indent)}${chalk.cyan(task)} ${progress} at ${chalk.italic.gray(timestamp)}`) console.log(`${' '.repeat(indent)}${chalk.cyan(task)} ${progress} at ${chalk.italic.gray(timestamp)}`)
if (additionalInfo) { if (additionalInfo) {
additionalInfo.trim().split('\n').forEach(info => { additionalInfo.trim().split('\n').forEach(info => {
console.log(`${' '.repeat(indent + 2) + chalk.dim(info)}`) console.log(`${' '.repeat(indent + 2) + chalk.dim(info)}`)
}) })
} }
}) })
console.log('') console.log('')
} }
function logNodeProperties(attributes, attributeGroups, indent = 4) { function logNodeProperties(attributes, attributeGroups, indent = 4) {
if (!attributeGroups) { if (!attributeGroups) {
throw new Error('No node attribute groups!') throw new Error('No node attribute groups')
} }
console.log('') console.log('')
logAttributes(attributes) logAttributes(attributes)
console.log('') console.log('')
attributeGroups.forEach(attributeGroup => { attributeGroups.forEach(attributeGroup => {
const { type, properties } = attributeGroup const { type, properties } = attributeGroup
console.log(`${' '.repeat(indent)}${chalk.bold.yellow(type + '↓')}`) console.log(`${' '.repeat(indent)}${chalk.bold.yellow(type + '↓')}`)
logAttributes(properties, indent + 2) logAttributes(properties, indent + 2)
console.log('') console.log('')
}) })
} }
function logAttributes(attributes, indent = 4) { function logAttributes(attributes, indent = 4) {
if (!attributes) { if (!attributes) {
throw new Error('No node attributes!') throw new Error('No node attributes')
} }
attributes.forEach(attribute => { attributes.forEach(attribute => {
const { name, value } = attribute const { name, value } = attribute
console.log(`${' '.repeat(indent)}${name ? chalk.bold.cyan(name + ': ') : ''}${value}`) console.log(`${' '.repeat(indent)}${name ? chalk.bold.cyan(name + ': ') : ''}${value}`)
}) })
} }
module.exports = { logNodeStatus, logNodeProperties } module.exports = { logNodeStatus, logNodeProperties }

70
util/logProject.js Normal file → Executable file
View File

@@ -1,36 +1,36 @@
const chalk = require('chalk') const chalk = require('chalk')
function logProject(data, nodeSummary) { function logProject(data, nodeSummary) {
if (!data || !nodeSummary) { if (!data || !nodeSummary) {
throw new Error('No project data or node summary!') throw new Error('No project data or node summary!')
} }
const { const {
id: projectId, id: projectId,
description, description,
creator, creator,
creationDate, creationDate,
nodes, nodes,
} = data } = data
console.log(` console.log(`
${chalk.italic.yellowBright('Project id')} : ${chalk.bold.inverse(projectId)} ${chalk.italic.yellowBright('Project id')} : ${chalk.bold.inverse(projectId)}
${chalk.italic.yellowBright('Author')} : ${chalk.underline(creator)} ${chalk.italic.yellowBright('Author')} : ${chalk.underline(creator)}
${chalk.italic.yellowBright('Creation Date')} : ${chalk.dim.gray(creationDate)} ${chalk.italic.yellowBright('Creation Date')} : ${chalk.dim.gray(creationDate)}
${chalk.italic.yellowBright('Description')} : ${description} ${chalk.italic.yellowBright('Description')} : ${description}
${chalk.italic.yellowBright('Nodes')} : ${chalk.italic.yellowBright('Nodes')} :
`) `)
nodes.forEach(node => { nodes.forEach(node => {
const { id: nodeId, type, identifier, ipAddress } = node const { id: nodeId, type, identifier, ipAddress } = node
const { status, state } = nodeSummary.find(item => item.id === nodeId) const { status, state } = nodeSummary.find(item => item.id === nodeId)
console.log(` ${chalk.cyan(nodeId)} console.log(` ${chalk.cyan(nodeId)}
${type} ${type}
${identifier} ${identifier}
${ipAddress} ${ipAddress}
${status === 'Successful' ? chalk.greenBright(status) : chalk.redBright(status) } ${status === 'Successful' ? chalk.greenBright(status) : chalk.redBright(status) }
${state} ${state}
`) `)
}) })
} }
module.exports = logProject module.exports = logProject

73
util/validation.js Normal file → Executable file
View File

@@ -1,10 +1,63 @@
const chalk = require('chalk') const chalk = require('chalk')
const isEmpty = input => (input === '' ? chalk.bgRed('Empty Inputs not Allowed') : true) const isEmpty = input => (input === '' ? chalk.bgRed('Empty Inputs not Allowed') : true)
const isValidHardwareId = input => (input.match(/[A-HJ-NP-Z0-9]{13}/) ? true : chalk.bgRed(`The Ericsson Hardware Serial Number frame consists of 13 alphanumeric characters. const isValidHardwareId = input => (input.match(/[A-HJ-NP-Z0-9]{13}/) ? true : chalk.bgRed(`The Ericsson Hardware Serial Number frame consists of 13 alphanumeric characters.
Character set is the letters A-Z with the exception of O and I, and digits 0-9.`)) Character set is the letters A-Z with the exception of O and I, and digits 0-9.`))
const isValidNumber = (input, constraints) => {
module.exports = { isEmpty, isValidHardwareId } if (constraints) {
const test = input ? input : ''
if (!constraints.nullable && test === 'null') return chalk.red('Value Can\'t Be a null')
if (constraints.nullable && test === 'null') return true
if (!test.toString().match(/-?[0-9]+/)) return chalk.red('Input Is Not a Number')
const checkResult = checkValueRangeConstraints(+test, constraints)
if (checkResult) return checkResult
}
return true
}
const isValidString = (input, constraints) => {
if (constraints) {
const test = input ? input : ''
if (!constraints.nullable && test === 'null') return chalk.red('Value Can\'t Be a null')
if (constraints.nullable && test === 'null') return true
if (constraints.validContentRegex && !test.match(constraints.validContentRegex)) return chalk.red('Input Doesn\'t Match RegEx')
const checkResult = checkValueRangeConstraints(test.length, constraints)
if (checkResult) return checkResult
}
return true
}
const checkValueRangeConstraints = (value, constraints) => {
let min
let max
if (constraints.valueRangeConstraints) {
const inRange = constraints.valueRangeConstraints.some(item => {
min = item.minValue
max = item.maxValue
return min <= value && max >= value
})
errStrArr = constraints.valueRangeConstraints.map(item => `${item.minValue}..${item.maxValue}`)
if (!inRange) return chalk.red(`Input is Outside Allowed Range: ${errStrArr.join(', ')}`)
}
}
const isValidNodeName = (input) => {
if (input.match('[a-zA-Z0-9-_.\\/:$]+') || input === '') {
return true
}
return chalk.red('Node Name is Invalid')
}
module.exports = {
isEmpty,
isValidHardwareId,
isValidNumber,
isValidString,
checkValueRangeConstraints,
isValidNodeName,
}