summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/api/ipxe.js63
-rw-r--r--server/ipxe/embedded_bios.ipxe15
-rw-r--r--server/ipxe/embedded_efi.ipxe (renamed from server/ipxe/main.ipxe)5
-rw-r--r--server/lib/shell.js144
-rw-r--r--server/lib/socketio.js1
-rw-r--r--webapp/src/components/IpxeBuilderModule.vue63
-rw-r--r--webapp/src/components/IpxeBuilderModuleConfig.vue224
-rw-r--r--webapp/src/config/dashboard.js4
-rw-r--r--webapp/src/config/i18n.js6
9 files changed, 485 insertions, 40 deletions
diff --git a/server/api/ipxe.js b/server/api/ipxe.js
index f50d9d3..549eb6f 100644
--- a/server/api/ipxe.js
+++ b/server/api/ipxe.js
@@ -7,12 +7,71 @@ var router = express.Router()
var noAuthRouter = express.Router()
// GET requests.
+router.get('/build', (req, res) => {
+ shell.buildIpxe(req, res)
+})
+
+router.get('/:version/script', (req, res) => {
+ res.setHeader('content-type', 'text/plain')
+ res.sendFile(path.join(__appdir, 'ipxe', 'embedded_' + req.params.version + '.ipxe'))
+})
+
+router.get('/:version/certificate', (req, res) => {
+ res.setHeader('content-type', 'text/plain')
+ res.sendFile(path.join(__appdir, 'bin', 'fullchain.pem'))
+})
+
+router.get('/:version/general', (req, res) => {
+ res.setHeader('content-type', 'text/plain')
+ res.sendFile(path.join(__appdir, 'ipxe', 'general_' + req.params.version + '.h'))
+})
+
+router.get('/:version/console', (req, res) => {
+ res.setHeader('content-type', 'text/plain')
+ res.sendFile(path.join(__appdir, 'ipxe', 'console_' + req.params.version + '.h'))
+})
+
+router.get('/:version/log', async (req, res) => {
+ res.setHeader('content-type', 'text/plain')
+ const filepath = path.join(__appdir, 'ipxe', 'log_' + req.params.version + '.txt')
+ res.sendFile(filepath, err => {
+ if (err) console.log('Could not send file ' + filepath)
+ })
+})
+
+router.put('/:version/:filename', (req, res) => {
+ var filepath = null
+ // Set the file path
+ if (req.params.filename === 'script') {
+ filepath = path.join(__appdir, 'ipxe', 'embedded_' + req.params.version + '.ipxe')
+ } else if (req.params.filename === 'console' || req.params.filename === 'general') {
+ filepath = path.join(__appdir, 'ipxe', req.params.filename + '_' + req.params.version + '.h')
+ } else if (req.params.filename === 'certificate') {
+ filepath = path.join(__appdir, 'bin', 'fullchain.pem')
+ } else {
+ res.status(500).send({ status: 'UNKNOWN_FILENAME', error: 'The provided filename is unknown' })
+ return
+ }
+
+ // Write File
+ fs.writeFile(filepath, req.body.data, err => {
+ if (err) res.status(500).send({ status: 'WRITE_FILE_ERROR', error: err })
+ else res.status(200).send({ status: 'SUCCESS' })
+ })
+})
/*
* @return: Rebuild the ipxe.
*/
-router.get('/build', (req, res) => {
- shell.buildIpxe(req, res)
+router.get('/:version/build', async (req, res) => {
+ if (req.params.version === 'efi' || req.params.version === 'bios') await shell.buildIpxe(req, res)
+ res.status(200).send({ status: 'success' })
+})
+
+router.get('/:version/clean', (req, res) => {
+ if (req.params.version === 'efi') shell.clean('efi')
+ else if (req.params.version === 'bios') shell.clean('bios')
+ res.send()
})
module.exports.router = router
diff --git a/server/ipxe/embedded_bios.ipxe b/server/ipxe/embedded_bios.ipxe
new file mode 100644
index 0000000..698c683
--- /dev/null
+++ b/server/ipxe/embedded_bios.ipxe
@@ -0,0 +1,15 @@
+#!ipxe
+
+################
+# BIOS Version #
+################
+
+ifopen
+
+# Wallpaper
+set img tftp://10.8.102.124/ipxeWallpaper3_scale.png || shell
+console --picture ${img} --x 800 --y 600 || shell
+
+:loop
+chain https://bas.intra.uni-freiburg.de/api/configloader/${uuid}
+goto loop \ No newline at end of file
diff --git a/server/ipxe/main.ipxe b/server/ipxe/embedded_efi.ipxe
index 3bb8e86..70bfc17 100644
--- a/server/ipxe/main.ipxe
+++ b/server/ipxe/embedded_efi.ipxe
@@ -1,4 +1,9 @@
#!ipxe
+
+################
+# EFI Version #
+################
+
ifopen
# Wallpaper
diff --git a/server/lib/shell.js b/server/lib/shell.js
index 2b1ea0b..09a3ae2 100644
--- a/server/lib/shell.js
+++ b/server/lib/shell.js
@@ -1,47 +1,121 @@
+// Those packages needs to be installed to build ipxe:
+// sudo apt install liblzma-dev
+// sudo apt install mkisofs
+
/* global __appdir */
var path = require('path')
var shell = require('shelljs')
var ipxeGIT = 'git://git.ipxe.org/ipxe.git'
+var io = require(path.join(__appdir, 'lib', 'socketio'))
+const fs = require('fs')
module.exports = {
- buildIpxe: function (req, res) {
- if (!shell.which('git')) {
- return res.status(500).send({ status: 'GIT_MISSING', error_message: 'Please install git on the server.' })
- }
+ buildIpxe: async function (req, res) {
+ const ipxeVersion = req.params.version
+
+ // Cloning git.
+ sendToLog(ipxeVersion, 'Cloning git ...\n', 'primary')
+ await cloneIpxe(ipxeVersion)
- var gitclone = 'git clone ' + ipxeGIT
- shell.cd(path.join(__appdir, 'ipxe'))
- shell.exec(gitclone, function (code, stdout, stderr) {
- shell.cd(path.join(__appdir, 'ipxe', 'ipxe', 'src'))
-
- // Remove the general config and paste in the own one
- shell.rm(path.join(__appdir, 'ipxe', 'ipxe', 'src', 'config', 'general.h'))
- // shell.cp(path.join(__appdir, 'ipxe', 'general.h'), path.join(__appdir, 'ipxe', 'ipxe', 'src', 'config'))
- shell.cp(path.join(__appdir, 'ipxe', 'general_efi.h'), path.join(__appdir, 'ipxe', 'ipxe', 'src', 'config', 'general.h'))
- shell.rm(path.join(__appdir, 'ipxe', 'ipxe', 'src', 'config', 'console.h'))
- // shell.cp(path.join(__appdir, 'ipxe', 'console.h'), path.join(__appdir, 'ipxe', 'ipxe', 'src', 'config'))
- shell.cp(path.join(__appdir, 'ipxe', 'console_efi.h'), path.join(__appdir, 'ipxe', 'ipxe', 'src', 'config', 'console.h'))
- // PCBIOS Variant
- // var make = 'make EMBED=' + path.join(__appdir, 'ipxe', 'main.ipxe') + ' TRUST=' + path.join(__appdir, 'bin', 'fullchain.pem')// + ' bin/undionly.kpxe'
-
- // EFI Variant
- var make = 'make bin-x86_64-efi/snponly.efi EMBED=' + path.join(__appdir, 'ipxe', 'main.ipxe') + ' TRUST=' + path.join(__appdir, 'bin', 'fullchain.pem')// + ' bin/undionly.kpxe'
-
- // USB
- // var make = 'make EMBED=' + path.join(__appdir, 'ipxe', 'reboot.ipxe') + ' TRUST=' + path.join(__appdir, 'bin', 'fullchain.pem') + ' bin/ipxe.usb'
- shell.env.DEBUG = ''
- shell.exec(make, function (code, stdout, stderr) {
- // shell.rm(path.join(__appdir, 'tftp', 'ipxe.0'))
- shell.rm(path.join(__appdir, 'ipxe', 'ipxe.0'))
- // shell.cp('bin/undionly.kpxe', path.join(__appdir, 'tftp'))
- shell.cp('bin/undionly.kpxe', path.join(__appdir, 'ipxe'))
- shell.mv(path.join(__appdir, 'ipxe', 'undionly.kpxe'), path.join(__appdir, 'ipxe', 'ipxe.0'))
- // shell.rm('-rf', 'ipxe');
- return res.status(200).send({ status: 'success' })
+ // Copying configs.
+ sendToLog(ipxeVersion, 'Copying configs ...\n', 'primary')
+ copyConfigs(ipxeVersion)
+
+ // Make ipxe.
+ sendToLog(ipxeVersion, 'Make iPXE ...\n', 'primary')
+ shell.cd(path.join(__appdir, 'ipxe', 'ipxe_' + ipxeVersion, 'src'))
+
+ // Different make command for efi / bios are needed.
+ var makeCmd = ''
+ if (ipxeVersion === 'efi') makeCmd = 'make bin-x86_64-efi/snponly.efi EMBED=' + path.join(__appdir, 'ipxe', 'embedded_' + ipxeVersion + '.ipxe') + ' TRUST=' + path.join(__appdir, 'bin', 'fullchain.pem')// + ' bin/undionly.kpxe'
+ else if (ipxeVersion === 'bios') makeCmd = 'make EMBED=' + path.join(__appdir, 'ipxe', 'embedded_' + ipxeVersion + '.ipxe') + ' TRUST=' + path.join(__appdir, 'bin', 'fullchain.pem')// + ' bin/undionly.kpxe'
+ await new Promise((resolve, reject) => {
+ var make = shell.exec(makeCmd, { async: true }, () => {
+ resolve()
+ })
+
+ // Send the output to the frontend log.
+ make.stdout.on('data', data => {
+ sendToLog(ipxeVersion, data, 'normal')
+ })
+ make.stderr.on('data', data => {
+ sendToLog(ipxeVersion, data, 'error')
})
})
+
+ // Copy and rename the ipxe file.
+ shell.cp('bin/undionly.kpxe', path.join(__appdir, 'ipxe'))
+ shell.mv(path.join(__appdir, 'ipxe', 'undionly.kpxe'), path.join(__appdir, 'ipxe', 'ipxe.' + ipxeVersion))
+ },
+
+ clean: async function (ipxeVersion) {
+ shell.cd(path.join(__appdir, 'ipxe'))
+ shell.rm('-rf', 'ipxe_' + ipxeVersion)
+ shell.rm(path.join(__appdir, 'ipxe', 'ipxe.' + ipxeVersion))
+ shell.rm(path.join(__appdir, 'ipxe', 'log_' + ipxeVersion + '.txt'))
+ sendToLog(ipxeVersion, 'iPXE files successfully cleaned', 'success', false)
}
}
-// sudo apt-get install liblzma-dev
-// sudo apt-get install mkisofs
+async function cloneIpxe(ipxeVersion) {
+ // Check if git is installed on the server.
+ if (!shell.which('git')) {
+ return { status: 'GIT_MISSING', error_message: 'Please install git on the server.' }
+ }
+
+ shell.cd(path.join(__appdir, 'ipxe'))
+ return new Promise((resolve, reject) => {
+ var clone = shell.exec('git clone ' + ipxeGIT + ' ipxe_' + ipxeVersion, { async: true }, () => {
+ resolve()
+ })
+ clone.stdout.on('data', data => {
+ sendToLog(ipxeVersion, data, 'normal')
+ })
+ clone.stderr.on('data', data => {
+ sendToLog(ipxeVersion, data, 'error')
+ })
+ })
+}
+
+function copyConfigs(ipxeVersion) {
+ // Remove the default configs and paste in the customized ones.
+ shell.rm(path.join(__appdir, 'ipxe', 'ipxe_' + ipxeVersion, 'src', 'config', 'general.h'))
+ shell.rm(path.join(__appdir, 'ipxe', 'ipxe_' + ipxeVersion, 'src', 'config', 'console.h'))
+ shell.cp(path.join(__appdir, 'ipxe', 'general_' + ipxeVersion + '.h'), path.join(__appdir, 'ipxe', 'ipxe_' + ipxeVersion, 'src', 'config', 'general.h'))
+ shell.cp(path.join(__appdir, 'ipxe', 'console_' + ipxeVersion + '.h'), path.join(__appdir, 'ipxe', 'ipxe_' + ipxeVersion, 'src', 'config', 'console.h'))
+ return
+}
+
+function sendToLog(ipxeVersion, msg, status, writeLog = true) {
+ var date = new Date();
+ var dateString = '[' + date.getFullYear() + '-'
+
+ var month = parseInt(date.getMonth()) + 1
+ if (month < 10) dateString += '0' + month + '-'
+ else dateString += month + '-'
+
+ if (parseInt(date.getDate()) < 10) dateString += '0' + date.getDate() + ' '
+ else dateString += date.getDate() + ' '
+
+ if (parseInt(date.getHours()) < 10) dateString += '0' + date.getHours() + ':'
+ else dateString += date.getHours() + ':'
+
+ if (parseInt(date.getMinutes()) < 10) dateString += '0' + date.getMinutes() + ':'
+ else dateString += date.getMinutes() + ':'
+
+ if (parseInt(date.getSeconds()) < 10) dateString += '0' + date.getSeconds()
+ else dateString += date.getSeconds()
+ dateString += ']'
+
+ var logEntry = { date: dateString, status: status, msg: msg }
+
+ var socket = ipxeVersion + ' log'
+ io.in('broadcast ipxeBuild').emit(socket, logEntry)
+ if (writeLog) writeLog(ipxeVersion, logEntry)
+}
+
+function writeLog(ipxeVersion, logEntry) {
+ fs.writeFile(path.join(__appdir, 'ipxe', 'log_' + ipxeVersion + '.txt'), logEntry.date + '\t' + logEntry.status + '\t' + logEntry.msg, { flag: 'a+' }, (err) => {
+ if (err) throw err
+ })
+}
diff --git a/server/lib/socketio.js b/server/lib/socketio.js
index 07c2f22..5cbedb7 100644
--- a/server/lib/socketio.js
+++ b/server/lib/socketio.js
@@ -27,6 +27,7 @@ io.on('connection', socket => {
// Join broadcast rooms (TODO: check if user has permission to join those rooms)
socket.join('broadcast newClient')
+ socket.join('broadcast ipxeBuild')
// Notification broadcasts
socket.on('notifications clearAll', () => socket.to(userId).emit('notifications clearAll'))
diff --git a/webapp/src/components/IpxeBuilderModule.vue b/webapp/src/components/IpxeBuilderModule.vue
new file mode 100644
index 0000000..9cb9bea
--- /dev/null
+++ b/webapp/src/components/IpxeBuilderModule.vue
@@ -0,0 +1,63 @@
+<i18n>
+{
+ "en": {
+ "efi": "EFI",
+ "bios": "BIOS"
+ },
+ "de": {
+ "efi": "EFI",
+ "bios": "BIOS"
+ }
+}
+</i18n>
+
+<template>
+ <v-container fill-height>
+ <v-layout>
+ <v-flex class="tabs-wrapper" xl10 offset-xl1 lg12>
+ <v-card>
+ <v-tabs v-model="tabs" centered :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor">
+ <v-tab>{{ $t('efi') }}</v-tab>
+ <v-tab>{{ $t('bios') }}</v-tab>
+ </v-tabs>
+ </v-card>
+
+ <v-tabs-items v-model="tabs" style="padding-bottom: 20px">
+ <v-tab-item>
+ <ipxe-builder-module-config :ipxeVersion="'efi'"></ipxe-builder-module-config>
+ </v-tab-item>
+ <v-tab-item>
+ <ipxe-builder-module-config :ipxeVersion="'bios'"></ipxe-builder-module-config>
+ </v-tab-item>
+
+ </v-tabs-items>
+ </v-flex>
+ </v-layout>
+ </v-container>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import IpxeBuilderModuleConfig from '@/components/IpxeBuilderModuleConfig'
+
+export default {
+ name: 'IpxeBuilder',
+ components: {
+ IpxeBuilderModuleConfig
+ },
+ data () {
+ return {
+ tabs: 0
+ }
+ },
+ computed: {
+ ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor'])
+ },
+ watch: {
+ },
+ methods: {
+ },
+ created () {
+ }
+}
+</script>
diff --git a/webapp/src/components/IpxeBuilderModuleConfig.vue b/webapp/src/components/IpxeBuilderModuleConfig.vue
new file mode 100644
index 0000000..e4f619a
--- /dev/null
+++ b/webapp/src/components/IpxeBuilderModuleConfig.vue
@@ -0,0 +1,224 @@
+<i18n>
+{
+ "en": {
+ "efi": "EFI",
+ "bios": "BIOS",
+ "general": "general.h",
+ "console": "console.h",
+ "script": "Embedded script (EMBED=)",
+ "trust": "Embedded certificate (TRUST=)",
+ "ipxe": "iPXE",
+ "buildIpxe": "Build iPXE",
+ "scriptSaved": "Embedded script saved successfully",
+ "certificateSaved": "Certificate saved successfully",
+ "generalSaved": "general.h saved successfully",
+ "consoleSaved": "console.h saved successfully",
+ "buildingIpxe": "Building iPXE ...",
+ "cleanIpxe": "Clean iPXE",
+ "cleaningIpxe": "Cleaning iPXE ..."
+ },
+ "de": {
+ "efi": "EFI",
+ "bios": "BIOS",
+ "general": "general.h",
+ "console": "console.h",
+ "script": "Eingebettetes Skript (EMBED=)",
+ "trust": "Eingebettetes Zertifikat (TRUST=)",
+ "ipxe": "iPXE",
+ "buildIpxe": "iPXE bauen",
+ "scriptSaved": "Eingebettetes Skript wurde erfolgreich gespeichert",
+ "certificateSaved": "Zertifikat wurde erfolgreich gespeichert",
+ "generalSaved": "general.h wurde erfolgreich gespeichert",
+ "consoleSaved": "console.h wurde erfolgreich gespeichert",
+ "buildingIpxe": "iPXE wird gebaut ...",
+ "cleanIpxe": "iPXE aufräumen",
+ "cleaningIpxe": "iPXE wird aufgeräumt .."
+ }
+}
+</i18n>
+
+<template>
+ <div>
+
+ <v-subheader>{{ $t('ipxe') }}</v-subheader>
+ <v-card style="padding-left: 24px;padding-right: 24px; padding-top: 12px; padding-bottom: 12px; height: 50vh; overflow: auto;" ref="log">
+ <template v-for="(entry, index) in log">
+ <pre :class="entry.status + '--text'" style="margin-bottom: 0px" :key="index"><span>{{ entry.date }} {{ entry.msg }}</span></pre>
+ </template>
+ </v-card>
+ <div class="text-xs-right">
+ <v-btn flat color="error" @click="cleanIpxe"><v-icon left>delete</v-icon>{{ $t('cleanIpxe') }}</v-btn>
+ <v-btn flat color="primary" @click="buildIpxe"><v-icon left>gavel</v-icon>{{ $t('buildIpxe') }}</v-btn>
+ </div>
+
+ <v-subheader></v-subheader>
+ <v-expansion-panel v-model="scriptExpanded">
+ <v-expansion-panel-content>
+ <div slot="header">{{ $t('script') }}</div>
+ <v-card>
+ <codemirror class="script-editor" ref="script" v-model="script"></codemirror>
+ </v-card>
+ <!--<v-divider></v-divider>-->
+ <div class="text-xs-right">
+ <v-btn flat color="error" @click="undo('script')"><v-icon left>undo</v-icon>{{ $t('undo') }}</v-btn>
+ <v-btn flat color="success" @click="redo('script')"><v-icon left>redo</v-icon>{{ $t('redo') }}</v-btn>
+ <v-btn color="primary" @click="save('script')"><v-icon left>save</v-icon>{{ $t('save') }}</v-btn>
+ </div>
+ </v-expansion-panel-content>
+ </v-expansion-panel>
+
+ <v-subheader></v-subheader>
+ <v-expansion-panel v-model="certificateExpanded">
+ <v-expansion-panel-content>
+ <div slot="header">{{ $t('trust') }}</div>
+ <v-card>
+ <codemirror class="script-editor" ref="certificate" v-model="certificate"></codemirror>
+ </v-card>
+ <!--<v-divider></v-divider>-->
+ <div class="text-xs-right">
+ <v-btn flat color="error" @click="undo('certificate')"><v-icon left>undo</v-icon>{{ $t('undo') }}</v-btn>
+ <v-btn flat color="success" @click="redo('certificate')"><v-icon left>redo</v-icon>{{ $t('redo') }}</v-btn>
+ <v-btn color="primary" @click="save('certificate')"><v-icon left>save</v-icon>{{ $t('save') }}</v-btn>
+ </div>
+ </v-expansion-panel-content>
+ </v-expansion-panel>
+
+ <v-subheader></v-subheader>
+ <v-expansion-panel v-model="generalExpanded">
+ <v-expansion-panel-content>
+ <div slot="header">{{ $t('general') }}</div>
+ <v-card>
+ <codemirror class="script-editor" ref="general" v-model="general"></codemirror>
+ </v-card>
+ <!--<v-divider></v-divider>-->
+ <div class="text-xs-right">
+ <v-btn flat color="error" @click="undo('general')"><v-icon left>undo</v-icon>{{ $t('undo') }}</v-btn>
+ <v-btn flat color="success" @click="redo('general')"><v-icon left>redo</v-icon>{{ $t('redo') }}</v-btn>
+ <v-btn color="primary" @click="save('general')"><v-icon left>save</v-icon>{{ $t('save') }}</v-btn>
+ </div>
+ </v-expansion-panel-content>
+ </v-expansion-panel>
+
+ <v-subheader></v-subheader>
+ <v-expansion-panel v-model="consoleExpanded">
+ <v-expansion-panel-content>
+ <div slot="header">{{ $t('console') }}</div>
+ <v-card>
+ <codemirror class="script-editor" ref="console" v-model="console"></codemirror>
+ </v-card>
+ <!--<v-divider></v-divider>-->
+ <div class="text-xs-right">
+ <v-btn flat color="error" @click="undo('console')"><v-icon left>undo</v-icon>{{ $t('undo') }}</v-btn>
+ <v-btn flat color="success" @click="redo('console')"><v-icon left>redo</v-icon>{{ $t('redo') }}</v-btn>
+ <v-btn color="primary" @click="save('console')"><v-icon left>save</v-icon>{{ $t('save') }}</v-btn>
+ </div>
+ </v-expansion-panel-content>
+ </v-expansion-panel>
+
+ </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import axios from 'axios'
+
+export default {
+ name: 'IpxeBuilder',
+ props: ['ipxeVersion'],
+ components: {
+ },
+ data () {
+ return {
+ tabs: 0,
+ script: '',
+ certificate: '',
+ general: '',
+ console: '',
+ scriptExpanded: null,
+ certificateExpanded: null,
+ generalExpanded: null,
+ consoleExpanded: null,
+ log: []
+ }
+ },
+ computed: {
+ ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor'])
+ },
+ watch: {
+ scriptExpanded: function () {
+ if (this.scriptExpanded === 0) this.$refs.script.refresh()
+ },
+ certificateExpanded: function () {
+ if (this.certificateExpanded === 0) this.$refs.certificate.refresh()
+ },
+ generalExpanded: function () {
+ if (this.generalExpanded === 0) this.$refs.general.refresh()
+ },
+ consoleExpanded: function () {
+ if (this.consoleExpanded === 0) this.$refs.console.refresh()
+ }
+ },
+ methods: {
+ save (apiPath) {
+ axios.put('/api/ipxe/' + this.ipxeVersion + '/' + apiPath, { data: this[apiPath] }).then(result => {
+ const saveMsg = apiPath + 'Saved'
+ if (result.data.status === 'SUCCESS') this.$snackbar({ color: 'success', text: this.$tc(saveMsg) })
+ else this.$snackbar({ color: 'error', text: result.data.error })
+ })
+ },
+ undo (element) {
+ this.$refs[element].codemirror.undo()
+ },
+ redo (element) {
+ this.$refs[element].codemirror.redo()
+ },
+ buildIpxe () {
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/build').then(result => {
+ this.$snackbar({ color: 'primary', text: this.$tc('buildingIpxe') })
+ })
+ },
+ cleanIpxe () {
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/clean').then(result => {
+ this.$snackbar({ color: 'primary', text: this.$tc('cleaningIpxe') })
+ })
+ }
+ },
+ created () {
+ // Load the data.
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/script').then(result => {
+ this.script = result.data
+ })
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/certificate').then(result => {
+ this.certificate = result.data
+ })
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/general').then(result => {
+ this.general = result.data
+ })
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/console').then(result => {
+ this.console = result.data
+ })
+
+ axios.get('/api/ipxe/' + this.ipxeVersion + '/log').then(result => {
+ var lines = result.data.split('\n')
+ for (var line in lines) {
+ if (lines[line] === '') continue
+ var attr = lines[line].split('\t')
+ this.log.push({ date: attr[0], status: attr[1], msg: attr[2] })
+ }
+ })
+
+ // Socket io event listeners
+ this.$socket.on(this.ipxeVersion + ' log', entry => {
+ this.log.push({ msg: entry.msg, status: entry.status, date: entry.date })
+ })
+ },
+ updated () {
+ this.$refs.log.$el.scrollTop = this.$refs.log.$el.scrollHeight
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/config/dashboard.js b/webapp/src/config/dashboard.js
index a092cdc..ac8a7b1 100644
--- a/webapp/src/config/dashboard.js
+++ b/webapp/src/config/dashboard.js
@@ -3,11 +3,13 @@ import ConfiguratorModule from '@/components/ConfiguratorModule'
import RegistrationModule from '@/components/RegistrationModule'
import BackendModule from '@/components/BackendModule'
import PermissionModule from '@/components/PermissionModule'
+import IpxeBuilder from '@/components/IpxeBuilderModule'
export default [
{ path: 'groups', component: GroupModule, icon: 'category' },
{ path: 'configurator', component: ConfiguratorModule, icon: 'list' },
{ path: 'registration', component: RegistrationModule, icon: 'assignment' },
{ path: 'backends', component: BackendModule, icon: 'cloud' },
- { path: 'permissions', component: PermissionModule, icon: 'lock_open' }
+ { path: 'permissions', component: PermissionModule, icon: 'lock_open' },
+ { path: 'ipxe', component: IpxeBuilder, icon: 'merge_type' }
]
diff --git a/webapp/src/config/i18n.js b/webapp/src/config/i18n.js
index 818f57c..3f71729 100644
--- a/webapp/src/config/i18n.js
+++ b/webapp/src/config/i18n.js
@@ -27,7 +27,8 @@ export default {
'ConfiguratorModule': 'iPXE Configurator',
'RegistrationModule': 'Client Registration',
'BackendModule': 'External Backends',
- 'PermissionModule': 'Permission Manager'
+ 'PermissionModule': 'Permission Manager',
+ 'IpxeBuilder': 'iPXE Builder'
}
},
'de': {
@@ -58,7 +59,8 @@ export default {
'ConfiguratorModule': 'iPXE Konfigurator',
'RegistrationModule': 'Client Registrierung',
'BackendModule': 'Externe Backends',
- 'PermissionModule': 'Rechteverwaltung'
+ 'PermissionModule': 'Rechteverwaltung',
+ 'IpxeBuilder': 'iPXE Builder'
}
}
}