From f28f21b2f67f5e152e6e9d13814150a4adf316c7 Mon Sep 17 00:00:00 2001 From: Udo Walter Date: Wed, 27 Nov 2019 17:20:15 +0000 Subject: [configloader] bugfixes in the beta configloader: add hook sources correct handeling of multiple sources remove merging of multiple sources with the same config --- server/api/configloader.js | 18 ++++- server/lib/confighelper.js | 187 ++++++++++++++++++++++++++------------------- 2 files changed, 123 insertions(+), 82 deletions(-) (limited to 'server') diff --git a/server/api/configloader.js b/server/api/configloader.js index ba395d0..dc3a29d 100644 --- a/server/api/configloader.js +++ b/server/api/configloader.js @@ -7,10 +7,12 @@ var noAuthRouter = decorateApp(express.Router()) const config = require(path.join(__appdir, 'config', 'config')) const url = config.https.host + ':' + config.https.port const configHelper = require(path.join(__appdir, 'lib', 'confighelper')) +const log = require(path.join(__appdir, 'lib', 'log')) noAuthRouter.getAsync(['/test/group/:id', '/test/group/'], async (req, res) => { const list = req.query.list !== undefined && req.query.list !== 'false' - const config = await configHelper.getGroupConfig(req.params.id, list) + const group = await db.group.findOne({ where: { id: req.params.id } }) + const config = await configHelper.getGroupConfig(group, list) if (!config) return res.status(404).end() if (!list) { res.set('Content-Type', 'text/plain') @@ -22,8 +24,18 @@ noAuthRouter.getAsync(['/test/group/:id', '/test/group/'], async (req, res) => { noAuthRouter.getAsync(['/test/:uuid', '/test/'], async (req, res) => { const list = req.query.list !== undefined && req.query.list !== 'false' - const config = await configHelper.getConfig(req.params.uuid, list) + const client = await db.client.findOne({ where: { uuid: req.params.uuid } }) + const config = await configHelper.getConfig(client, list) if (!list) { + const logEntry = { + category: 'CLIENT_BOOT', + description: 'Client booted iPXE config [' + config.id + '] ' + config.name + '.\n' + + 'Client UUID: ' + req.params.uuid + '\n' + + 'Config ID: ' + config.id + '\n' + + 'Config Name: ' + config.name, + } + if (client) logEntry.clientId = client.id + log(logEntry) res.set('Content-Type', 'text/plain') res.send(config.script) } else { @@ -125,7 +137,7 @@ noAuthRouter.getAsync('/:uuid', async (req, res) => { } // No config found, use default config // await sendFilePromise(res, path.join(__appdir, 'ipxe', 'default.ipxe')) - res.send(await configHelper.getDefaultConfig(client)) + res.send(configHelper.getDefaultConfig(client)) }) // load config by given id diff --git a/server/lib/confighelper.js b/server/lib/confighelper.js index 5843725..6773bd0 100644 --- a/server/lib/confighelper.js +++ b/server/lib/confighelper.js @@ -3,11 +3,11 @@ const path = require('path') const fs = require('fs') const db = require(path.join(__appdir, 'lib', 'sequelize')) const eventHelper = require(path.join(__appdir, 'lib', 'eventhelper')) -const config = require(path.join(__appdir, 'config', 'config')) -const url = config.https.host + ':' + config.https.port +const serverConfig = require(path.join(__appdir, 'config', 'config')) +const url = serverConfig.https.host + ':' + serverConfig.https.port -async function getConfig (uuid, list) { - const client = await db.client.findOne({ where: { uuid: uuid } }) + +async function getConfig (client, list) { let configPath = [] // client not known, start registration @@ -25,7 +25,7 @@ async function getConfig (uuid, list) { if (client.registrationState !== null) { // client is in registration state, load scripts var hook = await db.registrationhook.findOne({ where: { id: client.registrationState } }) - let config = { id: 'HOOK', name: 'hook.name', hookId: hook.id, hookType: hook.type } + let config = { id: 'HOOK' + hook.id, name: hook.name, source: { type: hook.type + '_HOOK', id: hook.id, name: hook.name, } } if (!list) { if (hook.type === 'IPXE') { config.script = hook.script @@ -34,123 +34,139 @@ async function getConfig (uuid, list) { } return config } + if (hook.type === 'BASH') config.name = 'Minilinux' + else config.name = hook.name configPath.push(config) } // Check for event or direct configs - let importantList = [] - let eventList = [] - let configList = [] - await _checkParentConfigs(client.id, importantList, eventList, configList, !list) + const configs = await _checkParentConfigs(client.id, !list) - if (!list) { - if (importantList.length) return importantList[0] - else if (eventList.length) return eventList[0] - else if (configList.length) return configList[0] - } else { - configPath.push(...importantList, ...eventList, ...configList) + if (configs.length) { + if (!list) return configs[0] + else configPath.push(...configs) } // No config found, use default config let config = await _prepareConfig({ id: 'DEFAULT', source: { type: 'DEFAULT' } }) + if (config === null) config = { id: 'DEFAULT', name: 'Fallback Default', script: getDefaultConfig(client), source: { type: 'DEFAULT' } } + if (!list) return config configPath.push(config) return configPath } -async function getGroupConfig (groupId, list) { - const group = await db.group.findOne({ where: { id: groupId } }) + +async function getGroupConfig (group, list) { if (!group) return let configPath = [] // Check for event or direct configs - let importantList = [] - let eventList = [] - let configList = [] - await _checkParentConfigs([group.id], importantList, eventList, configList, !list) + const configs = await _checkParentConfigs(group.id, !list, false) if (!list) { - if (importantList.length) return importantList[0] - else if (eventList.length) return eventList[0] - else if (configList.length) return configList[0] + if (configs.length) return configs[0] } else { - configPath.push(...importantList, ...eventList, ...configList) + configPath.push(...configs) } // No config found, use default config let config = await _prepareConfig({ id: 'DEFAULT', source: { type: 'DEFAULT' } }) + if (config === null) config = { id: 'DEFAULT', name: 'Fallback Default', script: getDefaultConfig({}), source: { type: 'DEFAULT' } } + if (!list) return config configPath.push(config) return configPath } -async function _checkParentConfigs (ids, importantList, eventList, configList, breakOnImportant, blackList = []) { - let events = [] - let configs = [] - if (Array.isArray(ids)) { - events = await db.event.findAll({ where: { '$groups.id$': ids }, include: ['groups', 'config'] }) - let groups = await db.group.findAll({ where: { id: ids }, include: ['config'] }) - groups.forEach(group => { - if (group.configId) configs.push({ id: group.configId, source: { type: 'GROUP', id: group.id, name: group.name } }) - }) - } else { - events = await db.event.findAll({ where: { '$clients.id$': ids }, include: ['clients', 'config'] }) - let client = await db.client.findOne({ where: { id: ids }, include: ['config'] }) - if (client && client.configId) configs = [{ id: client.configId, source: { type: 'CLIENT', id: client.id, name: client.name } }] - } +async function _checkParentConfigs (id, breakOnImportant, isClient = true) { + const importantList = [] + const eventList = [] + const configList = [] + const blackList = [] + const seenGroupsList = isClient ? [] : [id] + let parents = isClient ? [] : [id] - let importants = [] - let nonImportants = [] + do { - events.forEach(event => { - let eventTimes = JSON.parse(event.times) - if (!eventHelper.isActive(eventTimes)) return + let events = [] + let configs = [] - if ((event.groups && event.groups.some(group => group.group_x_event.blacklist)) || - (event.clients && event.clients.length && event.clients[0].client_x_event.blacklist)) { - return blackList.push(event.id) + if (isClient) { + events = await db.event.findAll({ where: { '$clients.id$': id }, include: ['clients', 'config'] }) + let client = await db.client.findOne({ where: { id: id }, include: ['config'] }) + if (client && client.configId) configs = [{ id: client.configId, source: { type: 'CLIENT', id: client.id, name: client.name } }] + } else { + events = await db.event.findAll({ where: { '$groups.id$': parents }, include: ['groups', 'config'] }) + let groups = await db.group.findAll({ where: { id: parents }, include: ['config'] }) + groups.forEach(group => { + if (group.configId) configs.push({ id: group.configId, source: { type: 'GROUP', id: group.id, name: group.name } }) + }) } - if (blackList.includes(event.id) || !event.configId) return + let importants = [] + let nonImportants = [] + + events.forEach(event => { + let eventTimes = JSON.parse(event.times) + if (!eventHelper.isActive(eventTimes)) return - let config = { - id: event.configId, - source: { - type: event.important ? 'IMPORTANT_EVENT' : 'EVENT', - id: event.id, - name: event.name + if ((event.groups && event.groups.some(group => group.group_x_event.blacklist)) + || (event.clients && event.clients.length && event.clients[0].client_x_event.blacklist)) { + return blackList.push(event.id) } - } - if (event.groups) config.source.groups = event.groups.map(x => ({ id: x.id, name: x.name })) + if (blackList.includes(event.id) || !event.configId) return - if (event.important) importants.push(config) - else nonImportants.push(config) - }) + let config = { + id: event.configId, + source: { + type: event.important ? 'IMPORTANT_EVENT' : 'EVENT', + id: event.id, + name: event.name + } + } - if (importants.length > 1) importantList.push(await _createDynamicMenu(importants)) - else if (importants.length === 1) importantList.push(await _prepareConfig(importants[0])) + if (event.groups) config.source.groups = event.groups.map(x => ({ id: x.id, name: x.name })) - if (breakOnImportant && importantList.length) return + if (event.important) importants.push(config) + else nonImportants.push(config) + }) - if (nonImportants.length > 1) eventList.push(await _createDynamicMenu(nonImportants)) - else if (nonImportants.length === 1) eventList.push(await _prepareConfig(nonImportants[0])) + if (importants.length > 1) importantList.push(await _createDynamicMenu(importants)) + else if (importants.length === 1) importantList.push(await _prepareConfig(importants[0])) - if (configs.length > 1) configList.push(await _createDynamicMenu(configs)) - else if (configs.length === 1) configList.push(await _prepareConfig(configs[0])) + if (breakOnImportant && importantList.length) break - let parents = [] - if (Array.isArray(ids)) { - parents = await db.group.findAll({ where: { '$subgroups.id$': ids }, include: ['subgroups'] }) - } else { - parents = await db.group.findAll({ where: { '$clients.id$': ids }, include: ['clients'] }) - } - if (parents.length) await _checkParentConfigs(parents.map(x => x.id), importantList, eventList, configList, breakOnImportant, blackList) + if (nonImportants.length > 1) eventList.push(await _createDynamicMenu(nonImportants)) + else if (nonImportants.length === 1) eventList.push(await _prepareConfig(nonImportants[0])) + + if (configs.length > 1) configList.push(await _createDynamicMenu(configs)) + else if (configs.length === 1) configList.push(await _prepareConfig(configs[0])) + + + if (isClient) { + parents = (await db.group.findAll({ where: { '$clients.id$': id }, include: ['clients'] })).map(x => x.id) + isClient = false + } else { + parents = (await db.group.findAll({ where: { '$subgroups.id$': parents }, include: ['subgroups'] })).map(x => x.id) + } + + for (let i = parents.length - 1; i >= 0; i--) { + if (seenGroupsList.includes(parents[i])) parents.splice(i, 1) + } + + seenGroupsList.push(...parents) + + } while (parents.length) + + return [...importantList, ...eventList, ...configList] } + // create the config script from database async function _prepareConfig (configInfo, noScript) { let id = configInfo.id @@ -161,7 +177,7 @@ async function _prepareConfig (configInfo, noScript) { else where.id = id const config = await db.config.findOne({ where, include: ['entries'], order: [[db.config.associations.entries, db.config.associations.entries.through, 'sortValue', 'ASC']] }) - if (!config) return configInfo + if (!config) return null const result = { id: config.id, name: config.name, source: configInfo.source } @@ -201,19 +217,30 @@ async function _prepareConfig (configInfo, noScript) { return result } + // create dynamic menu to load the different given configs for a client async function _createDynamicMenu (configInfos, noScript) { const ids = [] const idSourceMap = {} - console.log(configInfos) + for (let i in configInfos) { + let id = configInfos[i].id + let source = configInfos[i].source + + if (!ids.includes(id)) ids.push(id) + + if (idSourceMap[id] === undefined) idSourceMap[id] = source + else if (Array.isArray(idSourceMap[id])) idSourceMap[id].push(source) + else idSourceMap[id] = [idSourceMap[id], source] + } - configInfos.forEach(configInfo => { - ids.push(configInfo.id) - idSourceMap[configInfo.id] = configInfo.source + if (ids.length === 1) return await _prepareConfig({ + id: ids[0], + source: idSourceMap[ids[0]] }) const configs = await db.config.findAll({ where: { id: ids } }) + const result = { merged: true, id: JSON.stringify(configs.map(x => x.id)), @@ -249,8 +276,9 @@ async function _createDynamicMenu (configInfos, noScript) { return result } + // Creates a default overview config, when there is no default config in the frontend. -async function getDefaultConfig (client) { +function getDefaultConfig (client) { let script = '#!ipxe\r\n\r\n' script += ':start\r\n' script += 'menu ' + 'Client is successfully registrated' + '\r\n' @@ -280,4 +308,5 @@ async function getDefaultConfig (client) { return script } + module.exports = { getConfig, getGroupConfig, getDefaultConfig } -- cgit v1.2.3-55-g7522