diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js index f4c3587bdc..2bc15216dc 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/bootSchema.js @@ -16,6 +16,7 @@ import { string, boolean } from 'yup' import { useGetHostsQuery } from 'client/features/OneApi/host' +import { useGetVmmConfigQuery } from 'client/features/OneApi/system' import { getKvmMachines } from 'client/models/Host' import { Field, arrayToOptions } from 'client/utils' import { @@ -24,7 +25,6 @@ import { CPU_ARCHITECTURES, SD_DISK_BUSES, FIRMWARE_TYPES, - KVM_FIRMWARE_TYPES, HYPERVISORS, } from 'client/constants' @@ -143,10 +143,14 @@ export const FIRMWARE = { .default(() => undefined), dependOf: ['HYPERVISOR', '$general.HYPERVISOR'], values: ([templateHyperv, hypervisor = templateHyperv] = []) => { - const types = - { - [kvm]: KVM_FIRMWARE_TYPES, - }[hypervisor] ?? FIRMWARE_TYPES + const configurableHypervisors = [kvm] + const { data: { OVMF_UEFIS = '' } = {} } = + configurableHypervisors?.includes(hypervisor) && + useGetVmmConfigQuery({ hypervisor }) + + const types = FIRMWARE_TYPES.concat( + OVMF_UEFIS?.replace(/"/g, '')?.split(' ') ?? [] + ) return arrayToOptions(types) }, diff --git a/src/fireedge/src/client/constants/vmTemplate.js b/src/fireedge/src/client/constants/vmTemplate.js index 4285167ca8..f4146042fb 100644 --- a/src/fireedge/src/client/constants/vmTemplate.js +++ b/src/fireedge/src/client/constants/vmTemplate.js @@ -96,11 +96,6 @@ export const COMMON_RESOLUTIONS = { export const FIRMWARE_TYPES = ['BIOS', 'EFI'] -export const KVM_FIRMWARE_TYPES = FIRMWARE_TYPES.concat([ - '/usr/share/OVMF/OVMF_CODE.fd', - '/usr/share/OVMF/OVMF_CODE.secboot.fd', -]) - export const PCI_TYPES = { MANUAL: 'pci_manual', AUTOMATIC: 'pci_automatic' } export const VCENTER_FIRMWARE_TYPES = FIRMWARE_TYPES.concat(['uefi']) diff --git a/src/fireedge/src/client/features/OneApi/system.js b/src/fireedge/src/client/features/OneApi/system.js index 10cd223fa5..ca963df3d2 100644 --- a/src/fireedge/src/client/features/OneApi/system.js +++ b/src/fireedge/src/client/features/OneApi/system.js @@ -14,6 +14,10 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import { Actions, Commands } from 'server/utils/constants/commands/system' +import { + Actions as VmmActions, + Commands as VmmCommands, +} from 'server/routes/api/system/routes' import { Actions as SunstoneActions, Commands as SunstoneCommands, @@ -117,6 +121,24 @@ const systemApi = oneApi.injectEndpoints({ providesTags: [{ type: SYSTEM, id: 'sunstone-avalaibles-views' }], keepUnusedDataFor: 600, }), + + getVmmConfig: builder.query({ + /** + * Returns the hypervisor VMM_EXEC config. + * + * @param {object} params - Request params + * @returns {object} The set config options + * @throws Fails when response isn't code 200 + */ + query: (params) => { + const name = VmmActions.VMM_CONFIG + const command = { name, ...VmmCommands[name] } + + return { params, command } + }, + providesTags: [{ type: SYSTEM, id: 'vmm_config' }], + keepUnusedDataFor: 600, + }), }), }) @@ -126,6 +148,8 @@ export const { useLazyGetOneVersionQuery, useGetOneConfigQuery, useLazyGetOneConfigQuery, + useGetVmmConfigQuery, + useLazyGetVmmConfigQuery, useGetSunstoneConfigQuery, useLazyGetSunstoneConfigQuery, useGetSunstoneViewsQuery, diff --git a/src/fireedge/src/server/routes/api/logo/functions.js b/src/fireedge/src/server/routes/api/logo/functions.js index 42d6262d02..beff937f00 100644 --- a/src/fireedge/src/server/routes/api/logo/functions.js +++ b/src/fireedge/src/server/routes/api/logo/functions.js @@ -18,7 +18,7 @@ const { getAllLogos, validateLogo, encodeLogo, -} = require('server/routes/api/logo/utils') +} = require('server/utils/logo') const { defaults, httpCodes } = require('server/utils/constants') const { httpResponse } = require('server/utils/server') diff --git a/src/fireedge/src/server/routes/api/logo/utils.js b/src/fireedge/src/server/routes/api/logo/utils.js deleted file mode 100644 index fca5b8741e..0000000000 --- a/src/fireedge/src/server/routes/api/logo/utils.js +++ /dev/null @@ -1,149 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, OpenNebula Project, OpenNebula Systems * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); you may * - * not use this file except in compliance with the License. You may obtain * - * a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - * ------------------------------------------------------------------------- */ -const { getSunstoneViewConfig } = require('server/utils/yml') -const { existsSync, readdirSync } = require('fs') -const path = require('path') -const { global } = require('window-or-global') -const Jimp = require('jimp') - -/** - * Retrieves the logo filename. - * - * @returns {string|null} The validated logo filename or null if the filename is invalid or not specified. - */ -const getLogo = () => { - const config = getSunstoneViewConfig() - const logo = config?.logo - - const validFilenameRegex = /^[a-zA-Z0-9-_]+\.(jpg|jpeg|png|)$/ - - if ( - logo && - typeof logo === 'string' && - logo.trim() !== '' && - validFilenameRegex.test(logo) - ) { - return { valid: true, filename: logo } - } - - return { valid: false, filename: null, ...(!logo ? { NOTSET: true } : {}) } -} - -/** - * Validates the specified logo file path. - * - * @param {string} logo - The logo file name to validate. - * @param {boolean} relativePaths - Return relative paths instead of absolute - * @returns {string|boolean} Full logo path or false if invalid. - */ -const validateLogo = (logo, relativePaths = false) => { - const imagesDirectory = global?.paths?.SUNSTONE_IMAGES - - if (!logo || !imagesDirectory) { - return { valid: false, path: null } - } - - const filePath = path.isAbsolute(logo) - ? logo - : path.join(imagesDirectory, path.normalize(logo)) - - if (!filePath?.startsWith(imagesDirectory)) { - return { valid: false, path: null } - } - - if (!existsSync(filePath)) { - return { valid: false, path: 'Not found' } - } - - if (relativePaths) { - const relativePath = path.relative(imagesDirectory, filePath) - - return { valid: true, path: `images/logos/${relativePath}` } - } - - return { valid: true, path: filePath } -} - -/** - * Encodes an image file at a specified path into a base64 string. - * - * @param {string} filePath - The full path to the image file. - * @returns {Promise} A promise that resolves to the base64 encoded image string. - */ -const encodeLogo = async (filePath) => { - try { - const image = await Jimp.read(filePath) - const data = await image.getBufferAsync(Jimp.MIME_PNG) - - return `data:image/png;base64,${data.toString('base64')}` - } catch (error) { - return null - } -} - -/** - * Resizes and encodes an image file to be used as a favicon. - * - * @param {string} filePath - The full path to the image file. - * @returns {Promise} A promise that resolves to the base64 encoded image string suitable for favicon use. - */ -const encodeFavicon = async (filePath) => { - try { - const image = await Jimp.read(filePath) - const resizedImage = await image.resize(32, 32) - const data = await resizedImage.getBufferAsync(Jimp.MIME_PNG) - - return `data:image/png;base64,${data.toString('base64')}` - } catch (error) { - return null - } -} - -/** - * Retrieves all logo files from the assets directory. - * - * @returns {object} A JSON object with filename as key and full path as value. - */ -const getAllLogos = () => { - const imagesDirectory = global?.paths?.SUNSTONE_IMAGES - if (!imagesDirectory || !existsSync(imagesDirectory)) { - return null - } - - const files = readdirSync(imagesDirectory) - const validFilenameRegex = /^[a-zA-Z0-9-_]+\.(jpg|jpeg|png|)$/ - - const logos = files.reduce((acc, file) => { - if (validFilenameRegex.test(file)) { - acc[file.replace(/\.(jpg|jpeg|png)$/, '')] = path.join( - imagesDirectory, - file - ) - } - - return acc - }, {}) - - return logos -} - -module.exports = { - getLogo, - getAllLogos, - validateLogo, - encodeLogo, - encodeFavicon, -} diff --git a/src/fireedge/src/server/routes/api/system/functions.js b/src/fireedge/src/server/routes/api/system/functions.js index 8ee4ffad77..50d684094b 100644 --- a/src/fireedge/src/server/routes/api/system/functions.js +++ b/src/fireedge/src/server/routes/api/system/functions.js @@ -22,10 +22,12 @@ const { Actions: ActionSystem, } = require('server/utils/constants/commands/system') const { createTokenServerAdmin } = require('server/routes/api/auth/utils') +const { getVmmConfig } = require('server/utils/vmm') const { defaultEmptyFunction, httpMethod } = defaults -const { ok, internalServerError, badRequest } = httpCodes +const { ok, internalServerError, badRequest, notFound } = httpCodes const { GET } = httpMethod +const { writeInLogger } = require('server/utils/logger') const ALLOWED_KEYS_ONED_CONF = [ 'DEFAULT_COST', @@ -112,6 +114,56 @@ const getConfig = ( }) } +/** + * + * @param {object} res - http response + * @param {Function} next - express stepper + * @param {object} params - params of http request + * @param {object} [params.hypervisor="kvm"] - fetch vmm_exec_[hypervisor].conf + * @returns {void} + */ +const getVmmConfigHandler = async ( + res = {}, + next = defaultEmptyFunction, + params = {} +) => { + try { + const { hypervisor } = params + const vmmConfig = (await getVmmConfig(hypervisor)) ?? {} + + if (!vmmConfig) { + res.locals.httpCode = httpResponse( + notFound, + 'No vmm_exec config found', + '' + ) + + return next() + } + + if (Object.keys(vmmConfig)?.length === 0) { + res.locals.httpCode = httpResponse( + notFound, + 'No valid vmm_exec config found', + '' + ) + } else { + res.locals.httpCode = httpResponse(ok, vmmConfig) + } + } catch (error) { + const httpError = httpResponse( + internalServerError, + 'Failed to load vmm_exec config', + '' + ) + writeInLogger(httpError) + res.locals.httpCode = httpError + } + + next() +} + module.exports = { getConfig, + getVmmConfigHandler, } diff --git a/src/fireedge/src/server/routes/api/system/index.js b/src/fireedge/src/server/routes/api/system/index.js index 6ec82f7ffa..8f2462ab5f 100644 --- a/src/fireedge/src/server/routes/api/system/index.js +++ b/src/fireedge/src/server/routes/api/system/index.js @@ -15,13 +15,20 @@ * ------------------------------------------------------------------------- */ const { Actions, Commands } = require('server/routes/api/system/routes') -const { getConfig } = require('server/routes/api/system/functions') +const { + getConfig, + getVmmConfigHandler, +} = require('server/routes/api/system/functions') -const { SYSTEM_CONFIG } = Actions +const { SYSTEM_CONFIG, VMM_CONFIG } = Actions module.exports = [ { ...Commands[SYSTEM_CONFIG], action: getConfig, }, + { + ...Commands[VMM_CONFIG], + action: getVmmConfigHandler, + }, ] diff --git a/src/fireedge/src/server/routes/api/system/routes.js b/src/fireedge/src/server/routes/api/system/routes.js index 0176cef44e..e1ba417b92 100644 --- a/src/fireedge/src/server/routes/api/system/routes.js +++ b/src/fireedge/src/server/routes/api/system/routes.js @@ -14,14 +14,19 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ -const { httpMethod } = require('server/utils/constants/defaults') +const { + from: { query }, + httpMethod, +} = require('../../../utils/constants/defaults') const basepath = '/system' const { GET } = httpMethod const SYSTEM_CONFIG = 'system.config' +const VMM_CONFIG = 'vmm.config' const Actions = { SYSTEM_CONFIG, + VMM_CONFIG, } module.exports = { @@ -32,5 +37,16 @@ module.exports = { httpMethod: GET, auth: true, }, + [VMM_CONFIG]: { + path: `${basepath}/vmmconfig`, + httpMethod: GET, + params: { + hypervisor: { + from: query, + default: 'kvm', + }, + }, + auth: true, + }, }, } diff --git a/src/fireedge/src/server/utils/logo.js b/src/fireedge/src/server/utils/logo.js index 04bc0e5cd1..bcd70a8cfe 100644 --- a/src/fireedge/src/server/utils/logo.js +++ b/src/fireedge/src/server/utils/logo.js @@ -13,11 +13,132 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -const { - getLogo, - validateLogo, - encodeFavicon, -} = require('server/routes/api/logo/utils') +const { getSunstoneViewConfig } = require('server/utils/yml') +const { existsSync, readdirSync } = require('fs') +const path = require('path') +const { global } = require('window-or-global') +const Jimp = require('jimp') + +/** + * Retrieves the logo filename. + * + * @returns {string|null} The validated logo filename or null if the filename is invalid or not specified. + */ +const getLogo = () => { + const config = getSunstoneViewConfig() + const logo = config?.logo + + const validFilenameRegex = /^[a-zA-Z0-9-_]+\.(jpg|jpeg|png|)$/ + + if ( + logo && + typeof logo === 'string' && + logo.trim() !== '' && + validFilenameRegex.test(logo) + ) { + return { valid: true, filename: logo } + } + + return { valid: false, filename: null, ...(!logo ? { NOTSET: true } : {}) } +} + +/** + * Validates the specified logo file path. + * + * @param {string} logo - The logo file name to validate. + * @param {boolean} relativePaths - Return relative paths instead of absolute + * @returns {string|boolean} Full logo path or false if invalid. + */ +const validateLogo = (logo, relativePaths = false) => { + const imagesDirectory = global?.paths?.SUNSTONE_IMAGES + + if (!logo || !imagesDirectory) { + return { valid: false, path: null } + } + + const filePath = path.isAbsolute(logo) + ? logo + : path.join(imagesDirectory, path.normalize(logo)) + + if (!filePath?.startsWith(imagesDirectory)) { + return { valid: false, path: null } + } + + if (!existsSync(filePath)) { + return { valid: false, path: 'Not found' } + } + + if (relativePaths) { + const relativePath = path.relative(imagesDirectory, filePath) + + return { valid: true, path: `images/logos/${relativePath}` } + } + + return { valid: true, path: filePath } +} + +/** + * Encodes an image file at a specified path into a base64 string. + * + * @param {string} filePath - The full path to the image file. + * @returns {Promise} A promise that resolves to the base64 encoded image string. + */ +const encodeLogo = async (filePath) => { + try { + const image = await Jimp.read(filePath) + const data = await image.getBufferAsync(Jimp.MIME_PNG) + + return `data:image/png;base64,${data.toString('base64')}` + } catch (error) { + return null + } +} + +/** + * Resizes and encodes an image file to be used as a favicon. + * + * @param {string} filePath - The full path to the image file. + * @returns {Promise} A promise that resolves to the base64 encoded image string suitable for favicon use. + */ +const encodeFavicon = async (filePath) => { + try { + const image = await Jimp.read(filePath) + const resizedImage = await image.resize(32, 32) + const data = await resizedImage.getBufferAsync(Jimp.MIME_PNG) + + return `data:image/png;base64,${data.toString('base64')}` + } catch (error) { + return null + } +} + +/** + * Retrieves all logo files from the assets directory. + * + * @returns {object} A JSON object with filename as key and full path as value. + */ +const getAllLogos = () => { + const imagesDirectory = global?.paths?.SUNSTONE_IMAGES + if (!imagesDirectory || !existsSync(imagesDirectory)) { + return null + } + + const files = readdirSync(imagesDirectory) + const validFilenameRegex = /^[a-zA-Z0-9-_]+\.(jpg|jpeg|png|)$/ + + const logos = files.reduce((acc, file) => { + if (validFilenameRegex.test(file)) { + acc[file.replace(/\.(jpg|jpeg|png)$/, '')] = path.join( + imagesDirectory, + file + ) + } + + return acc + }, {}) + + return logos +} /** * Retrieves, validates, and encodes a custom favicon image. @@ -54,5 +175,10 @@ const getEncodedFavicon = async () => { } module.exports = { + getLogo, + getAllLogos, + validateLogo, + encodeLogo, + encodeFavicon, getEncodedFavicon, } diff --git a/src/fireedge/src/server/utils/server.js b/src/fireedge/src/server/utils/server.js index 2a17af5f4e..c24c4bed81 100644 --- a/src/fireedge/src/server/utils/server.js +++ b/src/fireedge/src/server/utils/server.js @@ -616,6 +616,9 @@ const genPathResources = () => { if (!global.paths.SUNSTONE_VIEWS) { global.paths.SUNSTONE_VIEWS = `${ETC_LOCATION}/${defaultSunstonePath}/${defaultSunstoneViews}` } + if (!global.paths.VMM_EXEC_CONFIG) { + global.paths.VMM_EXEC_CONFIG = `${ETC_LOCATION}/vmm_exec` + } if (!global.paths.FIREEDGE_KEY_PATH) { global.paths.FIREEDGE_KEY_PATH = `${VAR_LOCATION}/.one/${defaultKeyFilename}` } diff --git a/src/fireedge/src/server/utils/vmm.js b/src/fireedge/src/server/utils/vmm.js new file mode 100644 index 0000000000..9867f51791 --- /dev/null +++ b/src/fireedge/src/server/utils/vmm.js @@ -0,0 +1,109 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +const fs = require('fs-extra') +const path = require('path') +const { global } = require('window-or-global') + +/** + * Parse custom configuration file format. + * + * @param {string} fileContent - Content of the configuration file. + * @returns {object} Parsed configuration object. + */ +const parseConfigFileContent = (fileContent) => { + const lines = fileContent + .split('\n') + .filter((line) => line && !line.startsWith('#')) + + const config = {} + let inBlock = false + let currentKey = '' + let blockContent = [] + let blockEndMarker = '' + + lines.forEach((line) => { + if (!line) return + + const trimLine = line?.trim() + + if (!inBlock) { + const [key, ...rest] = trimLine.split('=') + const value = rest.join('=').trim() + + if (key && /^[A-Za-z0-9_]+$/.test(key.trim())) { + currentKey = key.trim() + + if (value.startsWith('[') || value.startsWith('"')) { + blockEndMarker = value.startsWith('[') ? ']' : '"' + if (value.endsWith(blockEndMarker) && value.length > 1) { + config[currentKey] = value + } else { + inBlock = true + blockContent = [value] + } + } else { + config[currentKey] = value + } + } + } else { + blockContent.push(line) + if (line.endsWith(blockEndMarker)) { + config[currentKey] = blockContent.join('\n') + inBlock = false + currentKey = '' + blockContent = [] + } + } + }) + + if (inBlock && currentKey) { + config[currentKey] = blockContent?.join('\n') + } + + return config +} + +/** + * Get the configuration for a specific hypervisor. + * + * @param {string} hypervisor - The hypervisor type. + * @returns {Promise} Parsed configuration object. + */ +const getVmmConfig = async (hypervisor) => { + const vmmExecConfigDirectory = global?.paths?.VMM_EXEC_CONFIG + + const configFilePath = path.join( + vmmExecConfigDirectory, + `vmm_exec_${hypervisor}.conf` + ) + + if (!(await fs.pathExists(configFilePath))) { + throw new Error(`Configuration file not found: ${configFilePath}`) + } + + try { + const fileContent = await fs.readFile(configFilePath, 'utf-8') + const config = parseConfigFileContent(fileContent) + + return config + } catch (error) { + throw new Error(`Error parsing config file: ${configFilePath}`) + } +} + +module.exports = { + getVmmConfig, +} diff --git a/src/fireedge/src/server/utils/yml.js b/src/fireedge/src/server/utils/yml.js index 5b4721e8ed..99bdb3a62f 100644 --- a/src/fireedge/src/server/utils/yml.js +++ b/src/fireedge/src/server/utils/yml.js @@ -157,6 +157,7 @@ const getProvisionConfig = (options) => getConfiguration(defaultApps.provision.name, options) module.exports = { + readYAMLFile, getFireedgeConfig, getSunstoneConfig, getSunstoneViewConfig, diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js index a9988906ad..dc26c423cf 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js @@ -24,6 +24,7 @@ define(function(require) { var Locale = require("utils/locale"); var Tips = require("utils/tips"); var WizardFields = require("utils/wizard-fields"); + var TemplateUtils = require('utils/template-utils'); var FilesTable = require("tabs/files-tab/datatable"); var UniqueId = require("utils/unique-id"); var OpenNebulaHost = require("opennebula/host"); @@ -136,8 +137,6 @@ define(function(require) { var FIRMWARE_VALUES = { "BIOS": false, "EFI": false, - "/usr/share/OVMF/OVMF_CODE.fd": false, - "/usr/share/OVMF/OVMF_CODE.secboot.fd": true, "custom": true }; @@ -322,7 +321,18 @@ define(function(require) { }); that.initrdFilesTable.refreshResourceTableSelect(); - $("#firmwareType", context).change(function() { + var firmwareTypeSelect = $("#firmwareType", context); + + TemplateUtils.fetchOvmfValues().done(function(response) { + var ovmfUefis = response.ovmf_uefis; + ovmfUefis.forEach(function(uefi) { + firmwareTypeSelect.append(''); + }); + }).fail(function() { + console.error('Failed to load UEFI options'); + }); + + firmwareTypeSelect.change(function() { if (FIRMWARE_VALUES[$(this).val()]){ $("#firmwareSecure", context).show(); } diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs index 91d0b67546..43b3d18207 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs @@ -114,8 +114,6 @@ diff --git a/src/sunstone/public/app/utils/template-utils.js b/src/sunstone/public/app/utils/template-utils.js index abfbbfc495..414db27426 100644 --- a/src/sunstone/public/app/utils/template-utils.js +++ b/src/sunstone/public/app/utils/template-utils.js @@ -181,13 +181,21 @@ define(function(require) { return rtn; } + function _fetchOvmfValues() { + return $.ajax({ + url: '/ovmf_uefis', + method: 'GET' + }); + } + return { "stringToTemplate": _convert_string_to_template, "templateToString": _convert_template_to_string, "htmlDecode": _htmlDecode, "htmlEncode": _htmlEncode, "escapeDoubleQuotes": _escapeDoubleQuotes, - "removeHTMLTags": _removeHTMLTags + "removeHTMLTags": _removeHTMLTags, + "fetchOvmfValues": _fetchOvmfValues, }; }); diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 011214a8c9..a5331156a0 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -38,6 +38,7 @@ end VMS_LOCATION = VAR_LOCATION + "/vms" +VMM_EXEC_CONF = ETC_LOCATION + "/vmm_exec/vmm_exec_kvm.conf" SUNSTONE_AUTH = VAR_LOCATION + '/.one/sunstone_auth' SUNSTONE_LOG = LOG_LOCATION + '/sunstone.log' @@ -356,6 +357,23 @@ def valid_csrftoken? session[:csrftoken] && session[:csrftoken] == csrftoken end + def get_ovmf_uefis + ovmf_uefis = [] + + if File.exist?(VMM_EXEC_CONF) + File.foreach(VMM_EXEC_CONF) do |line| + if line =~ /^OVMF_UEFIS\s*=\s*"(.+)"$/ + ovmf_uefis = $1.split(" ") + break + end + end + else + logger.error("Configuration file not found: #{VMM_EXEC_CONF}") + end + + ovmf_uefis + end + def authorized? session[:ip] && session[:ip] == request.ip end @@ -862,6 +880,14 @@ def destroy_session [200, version.to_json] end +get '/ovmf_uefis' do + content_type 'application/json', :charset => 'utf-8' + ovmf_uefis = {} + ovmf_uefis["ovmf_uefis"] = get_ovmf_uefis + [200, ovmf_uefis.to_json] +end + + ############################################################################## # Login ##############################################################################