mirror of
https://github.com/vvsviridov/enm-cli.git
synced 2025-10-23 08:22:21 +00:00
shell app improvements
This commit is contained in:
@@ -12,6 +12,7 @@ require('dotenv').config({ path: [...__dirname.split(path.sep).slice(0, -1), '.e
|
|||||||
const AutoProvisioning = require('../lib/applications/AutoProvisioning/AutoProvisioning')
|
const AutoProvisioning = require('../lib/applications/AutoProvisioning/AutoProvisioning')
|
||||||
const TopologyBrowser = require('../lib/applications/TopologyBrowser/TopologyBrowser')
|
const TopologyBrowser = require('../lib/applications/TopologyBrowser/TopologyBrowser')
|
||||||
const BulkImport = require('../lib/applications/BulkImport/BulkImport')
|
const BulkImport = require('../lib/applications/BulkImport/BulkImport')
|
||||||
|
const ShellTerminal = require('../lib/applications/ShellTerminal/ShellTerminal')
|
||||||
|
|
||||||
const logError = require('../util/logError')
|
const logError = require('../util/logError')
|
||||||
|
|
||||||
@@ -35,6 +36,11 @@ const applications = [
|
|||||||
appClass: BulkImport,
|
appClass: BulkImport,
|
||||||
name: 'Bulk Import',
|
name: 'Bulk Import',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'shll',
|
||||||
|
appClass: ShellTerminal,
|
||||||
|
name: 'Shell Terminal',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
const appIds = applications.map(item => item.id)
|
const appIds = applications.map(item => item.id)
|
||||||
|
|
||||||
|
19
lib/applications/ShellTerminal/ShellTerminal.js
Executable file
19
lib/applications/ShellTerminal/ShellTerminal.js
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
const ENM = require('../../components/ENM')
|
||||||
|
|
||||||
|
const inputHandler = require('./inputHandler')
|
||||||
|
|
||||||
|
|
||||||
|
class ShellTerminal extends ENM {
|
||||||
|
constructor(username, password, url) {
|
||||||
|
super(username, password, url)
|
||||||
|
this.fdn = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async inputHandler() {
|
||||||
|
await inputHandler.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = ShellTerminal
|
48
lib/applications/ShellTerminal/inputHandler.js
Executable file
48
lib/applications/ShellTerminal/inputHandler.js
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
const inquirer = require('inquirer')
|
||||||
|
const { amos, scripting, wfcli } = require('./terminals')
|
||||||
|
|
||||||
|
|
||||||
|
async function inputHandler() {
|
||||||
|
const input = await inquirer.prompt([{
|
||||||
|
type: 'list',
|
||||||
|
name: 'vm',
|
||||||
|
prefix: '💻',
|
||||||
|
message: 'Select Terminal:',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Shell Terminal on Scripting',
|
||||||
|
value: 'scripting',
|
||||||
|
short: 'scripting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Shell Terminal on AMOS',
|
||||||
|
value: 'amos',
|
||||||
|
short: 'amos',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'WinFIOL CLI',
|
||||||
|
value: 'wfcli',
|
||||||
|
short: 'wfcli',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}])
|
||||||
|
|
||||||
|
switch (input.vm) {
|
||||||
|
case 'amos':
|
||||||
|
await amos.call(this)
|
||||||
|
break
|
||||||
|
case 'scripting':
|
||||||
|
await scripting.call(this)
|
||||||
|
break
|
||||||
|
case 'wfcli':
|
||||||
|
await wfcli.call(this)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (process.stdin.isRaw) {
|
||||||
|
process.stdin.setRawMode(false)
|
||||||
|
}
|
||||||
|
process.stdin.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = inputHandler
|
130
lib/applications/ShellTerminal/terminals.js
Executable file
130
lib/applications/ShellTerminal/terminals.js
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
|
||||||
|
const amosUrl = '/amos-service/amos/launch/info'
|
||||||
|
|
||||||
|
|
||||||
|
async function getNeType() {
|
||||||
|
const axiosConfig = {
|
||||||
|
text: 'Getting Network Element Type...',
|
||||||
|
method: 'get',
|
||||||
|
url: `${this.objectUrl}${this.currentPoId}`,
|
||||||
|
params: {
|
||||||
|
includeNonPersistent: false,
|
||||||
|
stringifyLong: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data: { neType } } = await this.httpClient.request(axiosConfig)
|
||||||
|
return neType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function getActions(type, neType) {
|
||||||
|
const axiosConfig = {
|
||||||
|
text: 'Matching Actions...',
|
||||||
|
method: 'post',
|
||||||
|
url: '/rest/v1/apps/action-matches',
|
||||||
|
data: {
|
||||||
|
application: 'topologybrowser',
|
||||||
|
multipleSelection: false,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
dataType: 'ManagedObject',
|
||||||
|
properties: [
|
||||||
|
{ name: 'type', value: type },
|
||||||
|
{ name: 'type', value: type },
|
||||||
|
{ name: 'neType', value: neType },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { data: { actions } } = await this.httpClient.request(axiosConfig)
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function amos() {
|
||||||
|
const axiosConfig = {
|
||||||
|
text: 'Launching AMOS...',
|
||||||
|
method: 'get',
|
||||||
|
url: amosUrl,
|
||||||
|
}
|
||||||
|
const amosCommand = { command: 'shell' }
|
||||||
|
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
|
||||||
|
if (meContextFind) {
|
||||||
|
const type = meContextFind[1]
|
||||||
|
const nodeName = meContextFind[2]
|
||||||
|
const neType = await getNeType.call(this)
|
||||||
|
const actions = await getActions.call(this, type, neType)
|
||||||
|
const hasAmos = actions.find(action => action.applicationId === 'advancedmoscripting')
|
||||||
|
if (hasAmos) {
|
||||||
|
axiosConfig['params'] = { poid: this.currentPoId }
|
||||||
|
amosCommand.command = 'amos'
|
||||||
|
amosCommand['poid'] = nodeName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const response = await this.httpClient.request(axiosConfig)
|
||||||
|
await this.webSocketSession(response.config.headers, '/terminal-websocket/command', amosCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function scripting() {
|
||||||
|
const axiosConfig = {
|
||||||
|
text: 'Launching Scripting Shell...',
|
||||||
|
method: 'get',
|
||||||
|
url: amosUrl,
|
||||||
|
}
|
||||||
|
const response = await this.httpClient.request(axiosConfig)
|
||||||
|
await this.webSocketSession(response.config.headers, '/scripting-terminal-ws/command', { command: 'scripting' })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function wfcli() {
|
||||||
|
const axiosConfig = {
|
||||||
|
text: 'Launching WinFIOL CLI...',
|
||||||
|
method: 'get',
|
||||||
|
url: `/neconnection-service/v1/networkelement-data/${this.currentPoId}`,
|
||||||
|
}
|
||||||
|
const wfcliCommand = { command: 'wfcli' }
|
||||||
|
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
|
||||||
|
if (meContextFind) {
|
||||||
|
const type = meContextFind[1]
|
||||||
|
const neType = await getNeType.call(this)
|
||||||
|
const actions = await getActions.call(this, type, neType)
|
||||||
|
const hasWinfiol = actions.find(action => action.applicationId === 'winfiol')
|
||||||
|
if (hasWinfiol) {
|
||||||
|
wfcliCommand['poid'] = this.currentPoId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let response
|
||||||
|
try {
|
||||||
|
response = await this.httpClient.request(axiosConfig)
|
||||||
|
} catch (error) {
|
||||||
|
response = error.response
|
||||||
|
}
|
||||||
|
await this.webSocketSession(response.config.headers, '/winfiol-websocket/command', wfcliCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function nodecli() {
|
||||||
|
const axiosConfig = {
|
||||||
|
text: 'Launching Node CLI...',
|
||||||
|
method: 'get',
|
||||||
|
url: `/oss/idm/usermanagement/users/${this.username}?username=true`,
|
||||||
|
}
|
||||||
|
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
|
||||||
|
if (!meContextFind) {
|
||||||
|
throw new Error('Can\'t launch node cli here❗')
|
||||||
|
}
|
||||||
|
const type = meContextFind[1]
|
||||||
|
const neType = await getNeType.call(this)
|
||||||
|
const actions = await getActions.call(this, type, neType)
|
||||||
|
const hasNodeCli = actions.find(action => action.applicationId === 'nodecli')
|
||||||
|
if (!hasNodeCli) {
|
||||||
|
throw new Error('Can\'t launch node cli here❗')
|
||||||
|
}
|
||||||
|
const response = await this.httpClient.request(axiosConfig)
|
||||||
|
await this.webSocketSession(response.config.headers, '/nodecli-websocket', { poid: this.currentPoId })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { amos, scripting, wfcli, nodecli }
|
@@ -2,9 +2,7 @@ const logAttributes = require('../../../util/logAttributes')
|
|||||||
const inputHandler = require('./inputHandler')
|
const inputHandler = require('./inputHandler')
|
||||||
const alarms = require('./commands/alarms')
|
const alarms = require('./commands/alarms')
|
||||||
const sync = require('./commands/sync')
|
const sync = require('./commands/sync')
|
||||||
const amos = require('./commands/amos')
|
const { amos, scripting, wfcli, nodecli } = require('../ShellTerminal/terminals')
|
||||||
const scripting = require('./commands/scripting')
|
|
||||||
const wfcli = require('./commands/wfcli')
|
|
||||||
const initialPrompt = require('./commands/initialPrompt')
|
const initialPrompt = require('./commands/initialPrompt')
|
||||||
const nextObjects = require('./commands/nextObjects')
|
const nextObjects = require('./commands/nextObjects')
|
||||||
const nextAttributes = require('./commands/nextAttributes')
|
const nextAttributes = require('./commands/nextAttributes')
|
||||||
@@ -35,7 +33,6 @@ class TopologyBrowser extends ENM {
|
|||||||
this.nodeTypesUrl = '/modelInfo/model/nodeTypes/'
|
this.nodeTypesUrl = '/modelInfo/model/nodeTypes/'
|
||||||
this.nodeSearchUrl = '/managedObjects/search/v2'
|
this.nodeSearchUrl = '/managedObjects/search/v2'
|
||||||
this.nodePoIdsUrl = '/managedObjects/getPosByPoIds'
|
this.nodePoIdsUrl = '/managedObjects/getPosByPoIds'
|
||||||
this.amosUrl = '/amos-service/amos/launch/info'
|
|
||||||
|
|
||||||
this.currentPoId = 0
|
this.currentPoId = 0
|
||||||
this.nextPoId = 1
|
this.nextPoId = 1
|
||||||
@@ -154,6 +151,10 @@ class TopologyBrowser extends ENM {
|
|||||||
await wfcli.call(this)
|
await wfcli.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async nodecli() {
|
||||||
|
await nodecli.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
async inputHandler() {
|
async inputHandler() {
|
||||||
await inputHandler.call(this)
|
await inputHandler.call(this)
|
||||||
}
|
}
|
||||||
|
@@ -1,29 +0,0 @@
|
|||||||
const { getActions, getNeType } = require('../../../../util/getActions')
|
|
||||||
|
|
||||||
|
|
||||||
async function amos() {
|
|
||||||
const axiosConfig = {
|
|
||||||
text: 'Launching AMOS...',
|
|
||||||
method: 'get',
|
|
||||||
url: this.amosUrl,
|
|
||||||
}
|
|
||||||
const amosCommand = { command: 'shell' }
|
|
||||||
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
|
|
||||||
if (meContextFind) {
|
|
||||||
const type = meContextFind[1]
|
|
||||||
const nodeName = meContextFind[2]
|
|
||||||
const neType = await getNeType.call(this)
|
|
||||||
const actions = await getActions.call(this, type, neType)
|
|
||||||
const hasAmos = actions.find(action => action.applicationId === 'advancedmoscripting')
|
|
||||||
if (hasAmos) {
|
|
||||||
axiosConfig['params'] = { poid: this.currentPoId }
|
|
||||||
amosCommand.command = 'amos'
|
|
||||||
amosCommand['poid'] = nodeName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const response = await this.httpClient.request(axiosConfig)
|
|
||||||
await this.webSocketSession(response.config.headers, '/terminal-websocket/command', amosCommand)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = amos
|
|
@@ -6,7 +6,7 @@ const tableChoices = require('../../../../util/tableChoices')
|
|||||||
const otherCommands = [
|
const otherCommands = [
|
||||||
'show', 'config', 'up', 'home',
|
'show', 'config', 'up', 'home',
|
||||||
'fdn', 'search', 'persistent', 'alarms',
|
'fdn', 'search', 'persistent', 'alarms',
|
||||||
'sync', 'amos', 'scripting', 'wfcli', 'exit'
|
'sync', 'amos', 'scripting', 'wfcli', 'nodecli', 'exit'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
async function scripting() {
|
|
||||||
const axiosConfig = {
|
|
||||||
text: 'Launching Scripting Shell...',
|
|
||||||
method: 'get',
|
|
||||||
url: this.amosUrl,
|
|
||||||
}
|
|
||||||
const response = await this.httpClient.request(axiosConfig)
|
|
||||||
await this.webSocketSession(response.config.headers, '/scripting-terminal-ws/command', { command: 'scripting' })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = scripting
|
|
@@ -1,31 +0,0 @@
|
|||||||
const { getActions, getNeType } = require('../../../../util/getActions')
|
|
||||||
|
|
||||||
|
|
||||||
async function wfcli() {
|
|
||||||
const axiosConfig = {
|
|
||||||
text: 'Launching WinFIOL CLI...',
|
|
||||||
method: 'get',
|
|
||||||
url: `/neconnection-service/v1/networkelement-data/${this.currentPoId}`,
|
|
||||||
}
|
|
||||||
const wfcliCommand = { command: 'wfcli' }
|
|
||||||
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
|
|
||||||
if (meContextFind) {
|
|
||||||
const type = meContextFind[1]
|
|
||||||
const neType = await getNeType.call(this)
|
|
||||||
const actions = await getActions.call(this, type, neType)
|
|
||||||
const hasWinfiol = actions.find(action => action.applicationId === 'winfiol')
|
|
||||||
if (hasWinfiol) {
|
|
||||||
wfcliCommand['poid'] = this.currentPoId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let response
|
|
||||||
try {
|
|
||||||
response = await this.httpClient.request(axiosConfig)
|
|
||||||
} catch (error) {
|
|
||||||
response = error.response
|
|
||||||
}
|
|
||||||
await this.webSocketSession(response.config.headers, '/winfiol-websocket/command', wfcliCommand)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = wfcli
|
|
@@ -12,6 +12,7 @@ const mainHelp = [
|
|||||||
'amos - launching Advanced MO Shell Scripting (amos)',
|
'amos - launching Advanced MO Shell Scripting (amos)',
|
||||||
'scripting - open shell terminal towards scripting VM',
|
'scripting - open shell terminal towards scripting VM',
|
||||||
'wfcli - launching WinFIOL CLI app',
|
'wfcli - launching WinFIOL CLI app',
|
||||||
|
'nodecli - launching node CLI app',
|
||||||
'exit - logout and exit application',
|
'exit - logout and exit application',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
|
|
||||||
|
@@ -80,6 +80,9 @@ async function handleCommand(tplg, command) {
|
|||||||
case '_wfcli':
|
case '_wfcli':
|
||||||
await tplg.wfcli()
|
await tplg.wfcli()
|
||||||
break
|
break
|
||||||
|
case '_nodecli':
|
||||||
|
await tplg.nodecli()
|
||||||
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
commandOther(tplg, command)
|
commandOther(tplg, command)
|
||||||
|
@@ -1,10 +1,16 @@
|
|||||||
const WebSocket = require('ws')
|
const WebSocket = require('ws')
|
||||||
const chalk = require('chalk')
|
const chalk = require('chalk')
|
||||||
|
const ora = require('ora')
|
||||||
|
|
||||||
|
|
||||||
|
let spinner = ora({
|
||||||
|
text: 'Starting connection...',
|
||||||
|
spinner: 'speaker',
|
||||||
|
})
|
||||||
|
|
||||||
function socketOnOpen(socket) {
|
function socketOnOpen(socket) {
|
||||||
console.log('📡 Connection started...')
|
|
||||||
socket.send(`{"type":"resize","cols":"${process.stdout.columns}","rows":"${process.stdout.rows}"}`)
|
socket.send(`{"type":"resize","cols":"${process.stdout.columns}","rows":"${process.stdout.rows}"}`)
|
||||||
|
spinner.succeed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -15,21 +21,26 @@ function socketOnMessage(event) {
|
|||||||
|
|
||||||
function socketOnClose(event, resolve, reject) {
|
function socketOnClose(event, resolve, reject) {
|
||||||
if (event.wasClean) {
|
if (event.wasClean) {
|
||||||
console.log(`\n${chalk.green('✔')} Connection closed: ${event.code} ${event.reason}.`)
|
console.log(`\n${chalk.green('✔')} Connection closed: ${chalk.green(event.code)} ${event.reason}.`)
|
||||||
resolve()
|
resolve()
|
||||||
} else {
|
} else {
|
||||||
reject(`Connection unexpectedly closed: ${event.code} ${event.reason}.`)
|
process.stdin.setRawMode(false)
|
||||||
|
process.stdin.pause()
|
||||||
|
reject(new Error(`Connection unexpectedly closed: ${event.code} ${event.reason}.`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function socketOnError(error, reject) {
|
function socketOnError(error, reject) {
|
||||||
|
process.stdin.setRawMode(false)
|
||||||
|
process.stdin.pause()
|
||||||
reject(error)
|
reject(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function webSocketSession(headers, socketUrl) {
|
async function webSocketSession(headers, socketUrl) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
spinner.start()
|
||||||
const socket = new WebSocket(socketUrl, {
|
const socket = new WebSocket(socketUrl, {
|
||||||
headers,
|
headers,
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
async function getNeType() {
|
|
||||||
const axiosConfig = {
|
|
||||||
text: 'Getting Network Element Type...',
|
|
||||||
method: 'get',
|
|
||||||
url: `${this.objectUrl}${this.currentPoId}`,
|
|
||||||
params: {
|
|
||||||
includeNonPersistent: false,
|
|
||||||
stringifyLong: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { data: { neType } } = await this.httpClient.request(axiosConfig)
|
|
||||||
return neType
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function getActions(type, neType) {
|
|
||||||
const axiosConfig = {
|
|
||||||
text: 'Matching Actions...',
|
|
||||||
method: 'post',
|
|
||||||
url: '/rest/v1/apps/action-matches',
|
|
||||||
data: {
|
|
||||||
application: 'topologybrowser',
|
|
||||||
multipleSelection: false,
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
dataType: 'ManagedObject',
|
|
||||||
properties: [
|
|
||||||
{ name: 'type', value: type },
|
|
||||||
{ name: 'type', value: type },
|
|
||||||
{ name: 'neType', value: neType },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { data: { actions } } = await this.httpClient.request(axiosConfig)
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = { getNeType, getActions }
|
|
Reference in New Issue
Block a user