-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP towards #353, session handling commands
- Loading branch information
Göran Sander
committed
Mar 7, 2024
1 parent
7684c2b
commit e5ceae9
Showing
9 changed files
with
836 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ qvfs | |
qvfs_1 | ||
qvfs_2 | ||
qvfs_3 | ||
qvfs_tmp | ||
|
||
src/node_modules/* | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { deleteSessionsFromQSEoWIds } from '../util/session.js'; | ||
import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; | ||
import { catchLog } from '../util/log.js'; | ||
|
||
/** | ||
* Delete Qlik Sense proxy sessions | ||
* @param {object} options - Options object | ||
*/ | ||
const deleteSessions = async (options) => { | ||
try { | ||
// Set log level | ||
setLoggingLevel(options.logLevel); | ||
|
||
logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`); | ||
logger.verbose(`Ctrl-Q was started from ${execPath}`); | ||
|
||
logger.info('Delete Qlik Sense proxy sessions'); | ||
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`); | ||
|
||
logger.info(`Deleting sessions on proxy "${options.hostProxy}", virtual proxy "${options.sessionVirtualProxy}"`); | ||
const deleteResult = await deleteSessionsFromQSEoWIds(options); | ||
|
||
if (deleteResult === false) { | ||
logger.error('Error deleting proxy sessions.'); | ||
return false; | ||
} | ||
|
||
return true; | ||
} catch (err) { | ||
catchLog(`Error deleting proxy sessions from host "${options.hostProxy}", virtual proxy "${options.sessionVirtualProxy}"`, err); | ||
|
||
return false; | ||
} | ||
}; | ||
|
||
export default deleteSessions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
import { table } from 'table'; | ||
import { getSessionsFromQseow } from '../util/session.js'; | ||
import { logger, setLoggingLevel, isPkg, execPath } from '../../globals.js'; | ||
import { catchLog } from '../util/log.js'; | ||
|
||
const consoleTableConfig = { | ||
border: { | ||
topBody: `─`, | ||
topJoin: `┬`, | ||
topLeft: `┌`, | ||
topRight: `┐`, | ||
|
||
bottomBody: `─`, | ||
bottomJoin: `┴`, | ||
bottomLeft: `└`, | ||
bottomRight: `┘`, | ||
|
||
bodyLeft: `│`, | ||
bodyRight: `│`, | ||
bodyJoin: `│`, | ||
|
||
joinBody: `─`, | ||
joinLeft: `├`, | ||
joinRight: `┤`, | ||
joinJoin: `┼`, | ||
}, | ||
columns: { | ||
// 3: { width: 40 }, | ||
// 4: { width: 100 }, | ||
// 5: { width: 30 }, | ||
// 6: { width: 30 }, | ||
}, | ||
}; | ||
|
||
/** | ||
* | ||
* @param {*} options | ||
*/ | ||
const getSessions = async (options) => { | ||
try { | ||
// Set log level | ||
setLoggingLevel(options.logLevel); | ||
|
||
logger.verbose(`Ctrl-Q was started as a stand-alone binary: ${isPkg}`); | ||
logger.verbose(`Ctrl-Q was started from ${execPath}`); | ||
|
||
logger.info('Get Qlik Sense proxy sessions'); | ||
logger.debug(`Options: ${JSON.stringify(options, null, 2)}`); | ||
|
||
const sessionDataArray = await getSessionsFromQseow(options, null); | ||
|
||
if (sessionDataArray === false || sessionDataArray === undefined) { | ||
logger.error(`Error getting proxy sessions from from QSEoW`); | ||
return false; | ||
} | ||
|
||
// Build table or json, depending on output format | ||
if (options.outputFormat === 'table') { | ||
const sessionsTable = []; | ||
sessionsTable.push([ | ||
'Virtual proxy description', | ||
'Virtual proxy prefix', | ||
'Virtual proxy session cookie header', | ||
'Linked proxy service', | ||
'Load balancing nodes', | ||
'Session user directory', | ||
'Session user ID', | ||
'Session user name', | ||
'Session attributes', | ||
'Session ID', | ||
]); | ||
|
||
// Get total number of sessions | ||
// Sum the number of entries in each sessionDataArray.sessions array | ||
const totalSessions = sessionDataArray.reduce((acc, vp) => acc + vp.sessions.length, 0); | ||
|
||
// Get sessions per proxy host | ||
// First get all unique proxy hosts | ||
const uniqueProxyHosts = [...new Set(sessionDataArray.map((vp) => vp.hostProxy))]; | ||
const sessionsPerProxyHost = uniqueProxyHosts.map((host) => { | ||
const sessions = sessionDataArray.filter((vp) => vp.hostProxy === host).reduce((acc, vp) => acc + vp.sessions.length, 0); | ||
return { host, sessions }; | ||
}); | ||
|
||
// Get mapping between proxy host and proxy host name | ||
// Reduce to uniqur mappings | ||
const proxyHostNameMap = sessionDataArray.map((vp) => ({ hostProxy: vp.hostProxy, hostProxyName: vp.hostProxyName })); | ||
const uniqueProxyHostNameMap = proxyHostNameMap.filter( | ||
(vp, index, self) => index === self.findIndex((t) => t.hostProxy === vp.hostProxy) | ||
); | ||
|
||
// Build text for table header | ||
let headerText = `-- Sessions per virtual proxy and proxy services --\n\nTotal number of sessions: ${totalSessions}\n\n`; | ||
|
||
// Add sessions per proxy host | ||
headerText += 'Sessions per proxy service:\n'; | ||
sessionsPerProxyHost.forEach((p) => { | ||
// Get name of proxy host | ||
const proxyHostName = uniqueProxyHostNameMap.find((m) => m.hostProxy === p.host).hostProxyName; | ||
headerText += ` ${proxyHostName}: ${p.host}: ${p.sessions}\n`; | ||
}); | ||
|
||
consoleTableConfig.header = { | ||
alignment: 'left', | ||
content: headerText, | ||
}; | ||
|
||
// Expand all session data into a single array of objects to make later sorting and filtering easier | ||
const sessionsTabular = []; | ||
sessionDataArray.forEach((vp) => { | ||
vp.sessions.forEach((s) => { | ||
const proxyHostName = uniqueProxyHostNameMap.find((m) => m.hostProxy === vp.hostProxy).hostProxyName; | ||
const vpLoadBalancingNodes = vp.virtualproxy.loadBalancingServerNodes | ||
.map((node) => `${node.name}: ${node.hostName}`) | ||
.join('\n'); | ||
|
||
// session.Attributes is an array, where each element is an object with a single key-value pair | ||
const attributes = s.Attributes.map((a) => { | ||
// Convert object to string on the format "key: value" | ||
const attr = Object.keys(a).map((key) => `${key}: ${a[key]}`)[0]; | ||
|
||
return attr; | ||
}).join('\n'); | ||
|
||
sessionsTabular.push({ | ||
vpDescription: vp.virtualproxy.description, | ||
vpPrefix: vp.virtualproxy.prefix, | ||
vpSessionCookieHeaderName: vp.virtualproxy.sessionCookieHeaderName, | ||
proxyHost: vp.hostProxy, | ||
proxyName: proxyHostName, | ||
proxyFull: `${proxyHostName}:\n${vp.hostProxy}`, | ||
loadBalancingNodes: vpLoadBalancingNodes, | ||
userDir: s.UserDirectory, | ||
userId: s.UserId, | ||
userName: s.UserName === undefined || s.UserName === null ? '' : s.UserName, | ||
attributes, | ||
sessionId: s.SessionId, | ||
}); | ||
}); | ||
}); | ||
|
||
// Sort the sessionDataArray as specified in the options.sortBy option | ||
// Possible values are: 'prefix', 'proxyhost', 'proxyname' | ||
if (options.sortBy !== undefined && options.sortBy !== null && options.sortBy !== '') { | ||
if (options.sortBy === 'prefix') { | ||
sessionsTabular.sort((a, b) => a.vpPrefix.localeCompare(b.vpPrefix)); | ||
} else if (options.sortBy === 'proxyhost') { | ||
sessionsTabular.sort((a, b) => a.proxyHost.localeCompare(b.proxyHost)); | ||
} else if (options.sortBy === 'proxyname') { | ||
sessionsTabular.sort((a, b) => a.proxyName.localeCompare(b.proxyName)); | ||
} else if (options.sortBy === 'userdir') { | ||
sessionsTabular.sort((a, b) => a.userDir.localeCompare(b.userDir)); | ||
} else if (options.sortBy === 'userid') { | ||
sessionsTabular.sort((a, b) => a.userId.localeCompare(b.userId)); | ||
} else if (options.sortBy === 'username') { | ||
sessionsTabular.sort((a, b) => a.userName.localeCompare(b.userName)); | ||
} | ||
} else { | ||
logger.warn('--sort-by option is invalid. Use default sorting.'); | ||
} | ||
|
||
// Add to table that will be printed to console | ||
// eslint-disable-next-line no-restricted-syntax | ||
for (const s of sessionsTabular) { | ||
sessionsTable.push([ | ||
s.vpDescription, | ||
s.vpPrefix, | ||
s.vpSessionCookieHeaderName, | ||
s.proxyFull, | ||
s.loadBalancingNodes, | ||
s.userDir, | ||
s.userId, | ||
s.userName, | ||
s.attributes, | ||
s.sessionId, | ||
]); | ||
} | ||
|
||
// Print table to console | ||
logger.info(`\n${table(sessionsTable, consoleTableConfig)}`); | ||
} else { | ||
logger.error('Invalid --output-format option'); | ||
return false; | ||
} | ||
|
||
return sessionDataArray; | ||
} catch (err) { | ||
catchLog(`Error getting proxy sessions from host ${options.host}`, err); | ||
|
||
return false; | ||
} | ||
}; | ||
|
||
export default getSessions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.