summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/api/backends.js12
-rw-r--r--server/api/registration.js82
-rw-r--r--server/ipxe/registration.ipxe1
-rw-r--r--server/lib/external-backends/backendhelper.js28
-rw-r--r--server/lib/external-backends/backends/infoblox-backend.js118
-rw-r--r--server/lib/external-backends/index.js12
6 files changed, 223 insertions, 30 deletions
diff --git a/server/api/backends.js b/server/api/backends.js
index 838467a..e92999a 100644
--- a/server/api/backends.js
+++ b/server/api/backends.js
@@ -9,6 +9,16 @@ var noAuthRouter = decorateApp(express.Router())
// GET requests.
+// TODO DELETE
+noAuthRouter.getAsync('/:id/test', async (req, res) => {
+ const id = req.params.id
+ const backend = await db.backend.findOne({ where: { id: id } })
+ const externalBackends = new ExternalBackends()
+ const instance = externalBackends.getInstance(backend.type)
+ const result = await instance.test(backend.credentials)
+ res.send(result)
+})
+
/*
* @return: Returns a list of all backends saved in the db.
*/
@@ -31,7 +41,7 @@ router.getAsync('/:id', async (req, res) => {
const instance = externalBackends.getInstance(backend.type)
let credentialTypes = instance.getCredentials()
- // Get the ids of the 'password' fieldds
+ // Get the ids of the 'password' fields
let censorIds = []
credentialTypes.forEach(function f (element) {
if (element.type === 'switch') {
diff --git a/server/api/registration.js b/server/api/registration.js
index e3ca350..dc18bf1 100644
--- a/server/api/registration.js
+++ b/server/api/registration.js
@@ -115,9 +115,8 @@ noAuthRouter.postAsync('/clients', async (req, res) => {
let ipxe = req.body.ipxe
if (typeof ipxe === 'string') ipxe = true
-
- if (!client.type) client.type = 'CLIENT'
- if (!client.name) client.name = client.type + '_' + client.uuid
+ let automatic = req.body.automatic
+ if (typeof automatic === 'string' && automatic === 'true') automatic = true
// If the client already exists return the configloader ipxe script.
const clientDb = await db.client.findOne({ where: { uuid: client.uuid } })
@@ -126,6 +125,55 @@ noAuthRouter.postAsync('/clients', async (req, res) => {
else return res.send({ error: 'CLIENT_ALREADY_EXISTS', msg: 'A client with the provided UUID does already exist.' })
}
+ if (!client.type) client.type = 'CLIENT'
+
+ // DHCP network stuff:
+
+ // TODO: Multiip / backend problems
+ // * Multiple backends possible ? multiple dhcp's? if set ip differentiates which one should we save in the bas db?
+ // * Only servers have multiple ips? No multi leased ips possible?
+
+ // If there is no ip, we don't need DHCP checks.
+ // Only the first ip address is checked! client.networks[0]
+ if (client.networks.length >= 1) {
+ const network = client.networks[0]
+ // Get the dhcp backend. Only one dhcp backend can exist else -> conflict.
+ const dhcp = await backendHelper.getDhcp()
+ if (dhcp) {
+ if (automatic) {
+ // Set the name of the client if it's not set
+ if (!client.name) client.name = client.type + '_' + client.uuid
+ const setIp = await dhcp.instance.setIp(dhcp.backend.credentials, network.ip, network.mac, client.name, true)
+
+ // Check for errors.
+ if (!setIp.error) {
+ // Client ip set successfully
+ client.networks[0].ip = setIp.ip
+ } else {
+ log({ category: 'ERROR_DHCP', description: `[${dhcp.backend.id}] Error setting ip ${network.ip} for mac ${network.mac}. Error: ${setIp.msg}` })
+ }
+ } else if (network.dhcp) {
+ // If networks.dhcp is set the user already choose the ip and we have to set it now.
+ if (!client.name) return res.send(buildNameClientIpxeMenu(client))
+ const setIp = await dhcp.instance.setIp(dhcp.backend.credentials, network.dhcp, network.mac, client.name)
+ // Check for errors.
+ if (setIp.error) {
+ // Setting the client ip failed
+ log({ category: 'ERROR_DHCP', description: `[${dhcp.backend.id}] Error setting ip ${network.ip} for mac ${network.mac}. Error: ${setIp.msg}` })
+ } else {
+ // Client ip set successfully
+ client.networks[0].ip = network.dhcp
+ }
+ } else {
+ // If not check if the client has a leased ipv4 address.
+ const ipCheck = await dhcp.instance.checkIp(dhcp.backend.credentials, network.ip)
+ // Build ipxe and return
+ if (ipxe && ipCheck && !ipCheck.error) return res.send(buildSelectIpIpxeMenu(client, ipCheck))
+ else if (!ipxe && ipCheck && !ipCheck.error) return res.send({ client: client, ipList: ipCheck })
+ }
+ }
+ }
+
// Client does not exist.
if (!client.parents) client.parents = []
// TODO: Save all IPs? Maybe only primary ip?
@@ -356,6 +404,34 @@ function buildIpxeMenu (id, name, groups, parents) {
return script
}
+function buildSelectIpIpxeMenu (client, ipList) {
+ const basUrl = 'https://' + url
+ let script = '#!ipxe\r\n'
+ let menuscript = ''
+ script += ':start\r\n'
+ script += 'menu Select the ip for this client:\r\n'
+ for (let index in ipList) {
+ const ip = ipList[index]
+ client.networks[0].dhcp = ip
+ script += 'item ' + ip + ' ' + ip + '\r\n'
+ menuscript += ':' + ip + '\r\n' + 'params\r\nparam client ' + JSON.stringify(client) + '\r\n'
+ menuscript += 'chain --replace ' + basUrl + '/api/registration/clients##params\r\n\r\n'
+ }
+ script += `choose target && goto \${target}\r\n\r\n`
+ script += menuscript
+ return script
+}
+
+function buildNameClientIpxeMenu (client) {
+ const basUrl = 'https://' + url
+ let script = '#!ipxe\r\n'
+ script += '\r\necho Enter client name\r\nread clientname\r\nparams\r\n'
+ client.name = `\${clientname}`
+ script += 'param client ' + JSON.stringify(client) + '\r\n'
+ script += 'chain --replace ' + basUrl + '/api/registration/clients##params\r\n\r\n'
+ return script
+}
+
function toAscii (string) {
string = string.replace('ü', 'ue')
string = string.replace('ö', 'oe')
diff --git a/server/ipxe/registration.ipxe b/server/ipxe/registration.ipxe
index 505d72f..621921d 100644
--- a/server/ipxe/registration.ipxe
+++ b/server/ipxe/registration.ipxe
@@ -28,6 +28,7 @@ goto start
:automatic
params
param ipxe true
+param automatic true
param client { "type": "CLIENT", "uuid": "${uuid}", "purpose": "Pool PC", "networks": [{ "ip": "${net0/ip}", "mac": "${net0/mac}" }] }
chain https://bas.intra.uni-freiburg.de/api/registration/clients##params
diff --git a/server/lib/external-backends/backendhelper.js b/server/lib/external-backends/backendhelper.js
index d8d4b24..b7a8a9b 100644
--- a/server/lib/external-backends/backendhelper.js
+++ b/server/lib/external-backends/backendhelper.js
@@ -4,7 +4,7 @@ const ExternalBackends = require(path.join(__appdir, 'lib', 'external-backends')
const db = require(path.join(__appdir, 'lib', 'sequelize'))
const log = require(path.join(__appdir, 'lib', 'log'))
-module.exports = { addClient, updateClient, deleteClients, uploadFiles }
+module.exports = { addClient, getDhcp, updateClient, deleteClients, uploadFiles }
async function addClient (client) {
// Get all backends and call addClient for each instance.
@@ -123,3 +123,29 @@ async function uploadFiles (clientId, files) {
}
return results
}
+
+async function getDhcp () {
+ const backends = await db.backend.findAll()
+
+ let dhcp = false
+ for (let b in backends) {
+ const backend = backends[b]
+ const ba = new ExternalBackends()
+ const instance = ba.getInstance(backend.type)
+
+ const isDHCP = await instance.isDhcp(backend.credentials)
+ if (isDHCP) {
+ if (!dhcp) dhcp = { instance: instance, backend: backend }
+ else {
+ // Conflict occured!
+ const conflict = await db.conflict.create({ description: 'Multiple dhcp backends found' })
+
+ // Add both backends to the conflict.
+ conflict.createObject({ objectType: 'BACKEND', objectId: backend.id })
+ conflict.createObject({ objectType: 'BACKEND', objectId: dhcp.backend.id })
+ return false
+ }
+ }
+ }
+ return dhcp
+}
diff --git a/server/lib/external-backends/backends/infoblox-backend.js b/server/lib/external-backends/backends/infoblox-backend.js
index 2b2c6d1..27aa0b4 100644
--- a/server/lib/external-backends/backends/infoblox-backend.js
+++ b/server/lib/external-backends/backends/infoblox-backend.js
@@ -64,6 +64,64 @@ class InfobloxBackend extends ExternalBackends {
})
}
+ async checkIp (credentials, ipv4) {
+ const c = this.mapCredentials(credentials)
+
+ // Login
+ const ipam = new Infoblox({
+ ip: c.url,
+ apiVersion: c.version
+ })
+ const login = await ipam.login(c.username, c.password)
+ if (!login) return false
+
+ // Get the host and check the leased state
+ let host = JSON.parse(await ipam.getHost(ipv4))[0]
+ if (host.lease_state && host.lease_state === 'ACTIVE') {
+ // If leased return the next 20 free ips of the subnet.
+ const dhcpNetwork = JSON.parse(await ipam.getNetworkFromIp(ipv4))
+ const nextIps = await ipam.getNext(dhcpNetwork[0]._ref, 20)
+ return nextIps
+ }
+ return false
+ }
+
+ async setIp (credentials, ipv4, mac, name, setNextIp = false) {
+ const c = this.mapCredentials(credentials)
+
+ const ipam = new Infoblox({
+ ip: c.url,
+ apiVersion: c.version
+ })
+ const login = await ipam.login(c.username, c.password)
+ if (!login) return { error: 'LOGIN_FAILED' }
+
+ // If setNextIp is true, use the next available ip from the subnet
+ if (setNextIp) {
+ const network = JSON.parse(await ipam.getNetworkFromIp(ipv4))
+ ipv4 = 'func:nextavailableip:' + network[0].network
+ }
+ const domain = await ipam.getDomain()
+ const createHost = await ipam.create('record:host?_return_fields%2B=ipv4addrs&_return_as_object=1', {
+ 'name': name + '.' + domain,
+ 'ipv4addrs': [
+ {
+ 'ipv4addr': ipv4,
+ 'mac': mac
+ }
+ ]
+ })
+
+ // Return error if there is one
+ if (createHost.Error) {
+ return { error: 'ERROR_INFOBLOX', msg: createHost.text }
+ } else return { host: createHost.result.ipv4addrs[0].host, ip: createHost.result.ipv4addrs[0].ipv4addr }
+ }
+
+ isDhcp () {
+ return true
+ }
+
// ############################################################################
// ####################### helper/optional functions #########################
// Helper function, to map the array of credential objects into a single js object.
@@ -80,38 +138,48 @@ class InfobloxBackend extends ExternalBackends {
}
// #### TEST FUNCTIONS ###
- test (credentials) {
- var c = this.mapCredentials(credentials)
+ async test (credentials) {
+ const c = this.mapCredentials(credentials)
- var ipam = new Infoblox({
+ const ipam = new Infoblox({
ip: c.url,
apiVersion: c.version
})
- var promise = new Promise((resolve, reject) => {
- ipam.login(c.username, c.password).then(response => {
- if (response) {
- // ipam.create('record:host?_return_fields%2B=na13me,ipv4addrs&_return_as_object=1', {'name': 'wapiTest.lp.privat', 'ipv4addrs': [{'ipv4addr': '10.21.9.141', 'mac': 'aa:bb:cc:11:22:21'}]}).then(function (response) {
- // return response
- // })
- // Get the network from the ip.
- // ipam.getHost('10.21.9.78').then(function (response) {
- ipam.getNetworkFromIp('10.21.9.78').then(response => {
- // ipam.getIpsFromSubnet('10.21.9.0/24').then(function (response) {
- // ipam.list('record:host').then(function (response) {
- // ipam.getDomain().then(function (response) {
- response = JSON.parse(response)
- // ipam.getNext(response[0]._ref, 1).then(r => {
- resolve(response)
- // })
- // res.send(JSON.parse(response))
- })
- } else {
- return { success: false, error: 'TEST' }
+ const login = await ipam.login(c.username, c.password)
+ let result = {}
+ if (!login) return { error: 'LOGIN_FAILED' }
+ // Get network from ip
+ result['getNetworkFromIp'] = JSON.parse(await ipam.getNetworkFromIp('10.21.9.78'))
+ // Get host
+ // result["getHost.78"] = JSON.parse(await ipam.getHost('10.21.9.78'))
+ // result["getHost.219"] = JSON.parse(await ipam.getHost('10.21.9.219'))
+ // Get ips from subnet
+ // result["getIpsFromSubnet"] = JSON.parse(await ipam.getIpsFromSubnet('10.21.9.0/24'))
+ // List records
+ // result["list"] = JSON.parse(await ipam.list('record:host'))
+ // Get Domain
+ result['getDomain'] = await ipam.getDomain()
+ // Get next ip
+ // result["getNextIp.1"] = await ipam.getNext(result["getNetworkFromIp"][0]._ref, 1)
+ // Get next 20 ips
+ // result["getNextIp.20"] = await ipam.getNext(result["getNetworkFromIp"][0]._ref, 20)
+
+ // Create Host
+ // result['createHost'] = await ipam.create('record:host?_return_fields%2B=ipv4addrs&_return_as_object=1', {'name': 'wapiTest2.lp.privat', 'ipv4addrs': [{'ipv4addr': '10.21.9.218', 'mac': 'aa:bb:cc:11:22:21'}]})
+ /*
+ result['create'] = await ipam.create('record:host?_return_fields%2B=ipv4addrs&_return_as_object=1', {
+ 'name': 'test.lp.privat',
+ 'ipv4addrs': [
+ {
+ 'ipv4addr': 'func:nextavailableip:' + result['getNetworkFromIp'][0].network,
+ 'mac': 'aa:bb:cc:11:22:21'
}
- })
+ ]
})
- return promise
+ result['create']
+ */
+ return result
}
}
diff --git a/server/lib/external-backends/index.js b/server/lib/external-backends/index.js
index 7acaa85..8dcae5a 100644
--- a/server/lib/external-backends/index.js
+++ b/server/lib/external-backends/index.js
@@ -166,6 +166,18 @@ class ExternalBackends {
async getFile (credentials, externalId, filename) {
return { error: 'NOT_IMPLEMENTED_EXCEPTION', message: 'The provided backend does not have an getFile method' }
}
+ async checkIp (credentials, ipv4) {
+ return { error: 'NOT_IMPLEMENTED_EXCEPTION', message: 'The provided backend does not have a checkIp method' }
+ }
+ async setIp (credentials, ipv4, mac) {
+ return { error: 'NOT_IMPLEMENTED_EXCEPTION', message: 'The provided backend does not have a setIp method' }
+ }
+ /*
+ * Only one dhcp backend should be configures or it causes problems in the registration.
+ */
+ isDhcp () {
+ return false
+ }
}
module.exports = ExternalBackends