summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJannik Schönartz2021-06-22 20:50:06 +0200
committerJannik Schönartz2021-06-22 20:50:06 +0200
commit9c8b03506a6f4b850f08e7d88aae597d3ba8ce0e (patch)
tree7e7d19f444e1c30ba13c5bbbae3674aaa1377f70
parent[server/pci] Fix bug where the downloaded data was accessed before it was dow... (diff)
downloadbas-9c8b03506a6f4b850f08e7d88aae597d3ba8ce0e.tar.gz
bas-9c8b03506a6f4b850f08e7d88aae597d3ba8ce0e.tar.xz
bas-9c8b03506a6f4b850f08e7d88aae597d3ba8ce0e.zip
[server/registration/idoit] Rework to new hw-collection method + restruce of the idoit external backend
-rw-r--r--server/api/registration.js298
-rw-r--r--server/lib/external-backends/backendhelper.js12
-rw-r--r--server/lib/external-backends/backends/idoit-backend.js1113
-rw-r--r--server/package-lock.json10
-rw-r--r--server/package.json1
5 files changed, 896 insertions, 538 deletions
diff --git a/server/api/registration.js b/server/api/registration.js
index 4897bb5..fd8b808 100644
--- a/server/api/registration.js
+++ b/server/api/registration.js
@@ -14,6 +14,7 @@ const log = require(path.join(__appdir, 'lib', 'log'))
const HttpResponse = require(path.join(__appdir, 'lib', 'httpresponse'))
// This is needed for parsing vendor/product codes
const pci = require(path.join(__appdir, 'lib', 'pci'))
+const EdidReader = require('edid-reader')
// Permission check middleware
router.all(['', '/hooks', '/:y', '/hooks/:x'], async (req, res, next) => {
@@ -183,134 +184,144 @@ noAuthRouter.post('/group', (req, res) => {
* Add method for adding a client or server.
*/
noAuthRouter.postAsync('/clients', async (req, res) => {
- let client = req.body.client
- if (typeof client === 'string') client = JSON.parse(client)
+ let client = {}
+
+ // Defines weather the answer is a ipxe script or json response
let ipxe = req.body.ipxe
if (typeof ipxe === 'string' && ipxe === 'true') ipxe = true
- let automatic = req.body.automatic
- if (typeof automatic === 'string' && automatic === 'true') automatic = true
- let confirmation = req.body.confirmation
- if (typeof confirmation === 'string' && confirmation === 'true') confirmation = true
-
- // If the client already exists return the configloader ipxe script.
- const clientDb = await db.client.findOne({ where: { uuid: client.uuid } })
- if (clientDb) {
- if (ipxe) return res.send(`#!ipxe\nchain https://` + url + `/api/configloader/\${uuid}`)
- 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]
let dhcp = false
- if (client.networks.length >= 1 && client.type === 'CLIENT') {
- const network = client.networks[0]
- // Get the dhcp backend. Only one dhcp backend can exist else -> conflict.
- dhcp = await backendHelper.getDhcp()
- let ipSelection = false
- let setIpError
- if (dhcp) {
- if (automatic) {
- // Set the name of the client if it's not set
- if (!client.name) {
- // Check the ip state in the dhcp
- const ipCheck = await dhcp.instance.checkIp(dhcp.backend.credentials, network.ip)
- // If it's not leased, set the hostname as clientname
- if (!ipCheck.error && !ipCheck.leased && ipCheck.name !== '') {
- if (ipCheck.name) client.name = ipCheck.name
- if (ipCheck.id) dhcp.ref = ipCheck.id
- } else {
- client.name = client.type + '_' + client.uuid
- const setIp = await dhcp.instance.setIp(dhcp.backend.credentials, network.ip, undefined, network.mac, undefined, true)
- // Check for errors.
- if (!setIp.error) {
- dhcp.ref = setIp.ref
- // Client ip set successfully
- client.networks[0].ip = setIp.ip
+ if (req.body.version && req.body.version >= 2) {
+ /* New hardware collection script */
+ client = await parseHardwareInformation(req.body)
+ } else {
+ client = req.body.client
+ if (typeof client === 'string') client = JSON.parse(client)
+ let automatic = req.body.automatic
+ if (typeof automatic === 'string' && automatic === 'true') automatic = true
+ let confirmation = req.body.confirmation
+ if (typeof confirmation === 'string' && confirmation === 'true') confirmation = true
+
+ // If the client already exists return the configloader ipxe script.
+ const clientDb = await db.client.findOne({ where: { uuid: client.uuid } })
+ if (clientDb) {
+ if (ipxe) return res.send(`#!ipxe\nchain https://` + url + `/api/configloader/\${uuid}`)
+ else return res.send({ error: 'CLIENT_ALREADY_EXISTS', msg: 'A client with the provided UUID does already exist.' })
+ }
+
+ // 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 && client.type === 'CLIENT') {
+ const network = client.networks[0]
+ // Get the dhcp backend. Only one dhcp backend can exist else -> conflict.
+ dhcp = await backendHelper.getDhcp()
+ let ipSelection = false
+ let setIpError
+ if (dhcp) {
+ if (automatic) {
+ // Set the name of the client if it's not set
+ if (!client.name) {
+ // Check the ip state in the dhcp
+ const ipCheck = await dhcp.instance.checkIp(dhcp.backend.credentials, network.ip)
+
+ // If it's not leased, set the hostname as clientname
+ if (!ipCheck.error && !ipCheck.leased && ipCheck.name !== '') {
+ if (ipCheck.name) client.name = ipCheck.name
+ if (ipCheck.id) dhcp.ref = ipCheck.id
} else {
- log({
- category: 'ERROR_DHCP',
- description: `[${dhcp.backend.id}] Error setting ip ${network.ip} for mac ${network.mac}\nError: ${setIp.msg}`
- })
+ client.name = client.type + '_' + client.uuid
+ const setIp = await dhcp.instance.setIp(dhcp.backend.credentials, network.ip, undefined, network.mac, undefined, true)
+ // Check for errors.
+ if (!setIp.error) {
+ dhcp.ref = setIp.ref
+ // 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}\nError: ${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 (!network.dhcp.domain) {
- // Check if there are multiple domains.
- const domainList = await dhcp.instance.checkDomain(dhcp.backend.credentials)
- if (domainList.length > 1) return res.send(buildSelectDomainIpxeMenu(client, domainList))
- else network.dhcp.domain = domainList[0]
- }
- if (!client.name) return res.send(buildNameClientIpxeMenu(client))
- if (confirmation) return res.send(buildOverviewIpxeMenu(client))
-
- const setIp = await dhcp.instance.setIp(dhcp.backend.credentials, network.dhcp.ip, network.dhcp.domain, network.mac, client.name)
- dhcp.ref = setIp.id
- // Check for errors.
- if (setIp.error) {
- log({
- category: 'ERROR_DHCP',
- description: `[${dhcp.backend.id}] Error setting ip ${network.ip} for mac ${network.mac}\nError: ${setIp.msg}`
- })
-
- // Setting the client ip failed
- ipSelection = true
- delete client.networks[0].dhcp
- delete client.name
- setIpError = 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 (!network.dhcp.domain) {
+ // Check if there are multiple domains.
+ const domainList = await dhcp.instance.checkDomain(dhcp.backend.credentials)
+ if (domainList.length > 1) return res.send(buildSelectDomainIpxeMenu(client, domainList))
+ else network.dhcp.domain = domainList[0]
+ }
+ if (!client.name) return res.send(buildNameClientIpxeMenu(client))
+ if (confirmation) return res.send(buildOverviewIpxeMenu(client))
+
+ const setIp = await dhcp.instance.setIp(dhcp.backend.credentials, network.dhcp.ip, network.dhcp.domain, network.mac, client.name)
+ dhcp.ref = setIp.id
+ // Check for errors.
+ if (setIp.error) {
+ log({
+ category: 'ERROR_DHCP',
+ description: `[${dhcp.backend.id}] Error setting ip ${network.ip} for mac ${network.mac}\nError: ${setIp.msg}`
+ })
+
+ // Setting the client ip failed
+ ipSelection = true
+ delete client.networks[0].dhcp
+ delete client.name
+ setIpError = setIp.msg
+ } else {
+ // Client ip set successfully
+ client.networks[0].ip = network.dhcp.ip
+ client.networks[0].hostname = client.name
+ client.networks[0].domain = setIp.domain
+ }
} else {
- // Client ip set successfully
- client.networks[0].ip = network.dhcp.ip
- client.networks[0].hostname = client.name
- client.networks[0].domain = setIp.domain
+ ipSelection = true
}
- } else {
- ipSelection = true
- }
- if (ipSelection) {
- // If not: check if the client has a leased ipv4 address.
- const ipCheck = await dhcp.instance.checkIp(dhcp.backend.credentials, network.ip)
+ if (ipSelection) {
+ // 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.leased && !ipCheck.error) return res.send(buildSelectIpIpxeMenu(client, ipCheck.nextIps, setIpError))
- else if (!ipxe && ipCheck.leased && !ipCheck.error) return res.send({ client: client, ipList: ipCheck.nextIps })
+ // Build ipxe and return
+ if (ipxe && ipCheck.leased && !ipCheck.error) return res.send(buildSelectIpIpxeMenu(client, ipCheck.nextIps, setIpError))
+ else if (!ipxe && ipCheck.leased && !ipCheck.error) return res.send({ client: client, ipList: ipCheck.nextIps })
- // Set the hostname as clientname if it exists and is not a leased ip.
- if (!ipCheck.leased && ipCheck.name) {
- if (ipCheck.name) client.name = ipCheck.name
- if (ipCheck.id) dhcp.ref = ipCheck.id
- } else {
- // Leased ip but no hostname? --> Maybe not waited long enough after DHCP deletion
- let date = new Date()
- const tenMin = 1000 * 60 * 10
- const fiveMin = 1000 * 60 * 5
- // Round up to the next 10-min mark for the error msg
- // === Add 5 min to the time and round to the nearest one
- const rounded = new Date(Math.round((date.getTime() + fiveMin) / tenMin) * tenMin)
-
- return res.send(buildNameClientIpxeMenu(client, ['Client has a fixed IP but NO hostname was found.', 'Infoblox might not be ready after client deletion.', `Wait until ${rounded.toTimeString()} or enter a name and continue anyways ...`]))
+ // Set the hostname as clientname if it exists and is not a leased ip.
+ if (!ipCheck.leased && ipCheck.name) {
+ if (ipCheck.name) client.name = ipCheck.name
+ if (ipCheck.id) dhcp.ref = ipCheck.id
+ } else {
+ // Leased ip but no hostname? --> Maybe not waited long enough after DHCP deletion
+ let date = new Date()
+ const tenMin = 1000 * 60 * 10
+ const fiveMin = 1000 * 60 * 5
+ // Round up to the next 10-min mark for the error msg
+ // === Add 5 min to the time and round to the nearest one
+ const rounded = new Date(Math.round((date.getTime() + fiveMin) / tenMin) * tenMin)
+
+ return res.send(buildNameClientIpxeMenu(client, ['Client has a fixed IP but NO hostname was found.', 'Infoblox might not be ready after client deletion.', `Wait until ${rounded.toTimeString()} or enter a name and continue anyways ...`]))
+ }
+ }
+ } else { // End of DHCP Stuff
+ if (automatic) {
+ client.name = client.type + '_' + client.uuid
}
- }
- } else { // End of DHCP Stuff
- if (automatic) {
- client.name = client.type + '_' + client.uuid
}
}
}
// Client does not exist.
if (!client.parents) client.parents = []
+ if (!client.name) client.name = client.type + '_' + client.uuid
+ if (!client.type) client.type = 'CLIENT'
+
// TODO: Save all IPs? Maybe only primary ip?
const createClient = { name: client.name, description: client.type, ip: client.networks[0].ip, mac: client.networks[0].mac, uuid: client.uuid }
if (client.type === 'CLIENT') createClient.registrationState = await getNextHookScript(client.parents)
@@ -495,7 +506,7 @@ function getRecursiveParents (groupIds) {
async function parseHardwareInformation (data) {
let client = {
'parents': [], // TODO:
- 'type': '', // SERVER OR CLIENT
+ 'type': data.type ? data.type : 'CLIENT', // SERVER OR CLIENT
'uuid': '',
'networks': [], // { 'mac': '', 'ip': '' }
'system': {
@@ -509,8 +520,11 @@ async function parseHardwareInformation (data) {
'isEcc': false
},
'drives': [],
- 'gpus': []
+ 'gpus': [],
+ 'monitors': [],
+ 'contacts': []
}
+ if (data.name) client.name = data.name
// TODO: Rack and Bay stuff
@@ -641,9 +655,9 @@ async function parseHardwareInformation (data) {
let network = {
'name': ip.ifname,
'mac': ip.address,
- 'ip': '',
- 'ipv6': '',
- 'hostname': ''
+ 'ip': undefined,
+ 'ipv6': undefined,
+ 'hostname': undefined
}
for (let addr of ip['addr_info']) {
@@ -671,6 +685,25 @@ async function parseHardwareInformation (data) {
}
/* edid */
+ for (let rawEdid of data.edid) {
+ const edid = EdidReader.parse(rawEdid.edid)
+ client.monitors.push({
+ model: edid.modelName,
+ vendor: edid.vendor,
+ serialnumber: edid.serialNumber,
+ modes: edid.standardDisplayModes,
+ port: rawEdid.path.split('/')[4].substring(6),
+ resolution: {
+ width: edid.dtds[0].horActivePixels,
+ height: edid.dtds[0].vertActivePixels
+ },
+ dimensions: {
+ width: edid.displaySize[0],
+ height: edid.displaySize[1],
+ inch: Math.round(Math.sqrt(Math.pow(edid.displaySize[0], 2) + Math.pow(edid.displaySize[1], 2)) / 25.4)
+ }
+ })
+ }
/* lshw */
if (data.lshw && data.lshw.length > 0) {
@@ -681,15 +714,46 @@ async function parseHardwareInformation (data) {
client.gpus.push({
'manufacturer': gpu.vendor,
'model': gpu.product
- /*,
- 'memory': ,
- 'unit':
+ /*
+ 'memory': undefined,
+ 'unit': undefined,
*/
})
}
}
}
+ /* Contacts */
+ if (data.contacts && data.contacts.length > 0) {
+ for (let contact of data.contacts) {
+ client.contacts.push({
+ 'firstname': contact.firstname,
+ 'lastname': contact.lastname
+ })
+ }
+ }
+
+ /* Location */
+ if (data.location) {
+ // Server might get an name of the rack instead of an id
+ if (typeof data.location.parent === 'string' && isNaN(data.location.parent)) {
+ // Parent is not a number, so get the BAS ID for the object
+ const parent = await db.group.findOne({ where: { name: data.location.parent } })
+ // findOne only returns the first object with the matching name, so if the name isn't unique id should be used
+ client.parents.push(parent.id)
+ }
+
+ // Add bay and slot if given
+ if (data.location.bay || data.location.slot) {
+ client.location = {
+ ...(data.location.slot && {
+ slot: data.location.slot,
+ bay: data.location.bay ? data.location.bay : 0
+ })
+ }
+ }
+ }
+
return client
}
diff --git a/server/lib/external-backends/backendhelper.js b/server/lib/external-backends/backendhelper.js
index 0421cd7..e63f37f 100644
--- a/server/lib/external-backends/backendhelper.js
+++ b/server/lib/external-backends/backendhelper.js
@@ -31,7 +31,10 @@ async function addClient (client) {
for (let element of elements) {
conflict.createObject({ objectType: 'GROUP', objectId: element.id })
}
- } else if (elements.length === 1) tmpClient['parentId'] = elements[0].backend_x_group.externalId
+ }
+
+ // Nevertheless use the first one as "primary" parent
+ if (elements.length > 0) tmpClient['parentId'] = elements[0].backend_x_group.externalId
}
let addClient = await instance.addClient(backend.credentials, tmpClient)
@@ -85,9 +88,12 @@ async function updateClient (client) {
for (let element of elements) {
conflict.createObject({ objectType: 'GROUP', objectId: element.id })
}
- } else if (elements.length === 1) tmpClient['parentId'] = elements[0].backend_x_group.externalId
- else if (elements.length === 0) tmpClient['parentId'] = null
+ }
+
+ // Use first parent as parent
+ if (elements.length > 1) tmpClient['parentId'] = elements[0].backend_x_group.externalId
}
+
try {
let updateClient = await instance.updateClient(backend.credentials, tmpClient)
updateClient.backendId = backend.id
diff --git a/server/lib/external-backends/backends/idoit-backend.js b/server/lib/external-backends/backends/idoit-backend.js
index 1d3ac6f..0a8b164 100644
--- a/server/lib/external-backends/backends/idoit-backend.js
+++ b/server/lib/external-backends/backends/idoit-backend.js
@@ -166,10 +166,7 @@ class IdoitBackend extends ExternalBackends {
*
* credentials: <BACKEND_CREDENTIALS>
* The client parameters are all optional.
- * client: {
- * title: <CLIENT_TITLE>, parentId: <PARENT_ID>,
- * network: { mac: <MAC_ADDRESS>, ip: <IP_ADDRESS> }
- * }
+ * If given, the values will get updated after the client is createdö
*/
async addClient (credentials, client) {
const c = this.mapCredentials(credentials)
@@ -183,200 +180,14 @@ class IdoitBackend extends ExternalBackends {
}
client.parentId = parseInt(client.parentId)
+ // Hardcoded id from the Freiburg idoit, should be set in the dynamic backend settings
if (client.type === 'CLIENT') {
params['type'] = 10
- if (client.parentId) params.categories.C__CATG__LOCATION = { 'data': { 'parent': client.parentId } }
} else if (client.type === 'SERVER') {
params['type'] = 5
-
- if (client.location && client.location.bay === null) params.categories.C__CATG__LOCATION = { 'data': { 'parent': client.parentId, 'option': client.location.assembly, 'insertion': client.location.insertion, 'pos': client.location.slot } }
- if (client.formfactor) params.categories.C__CATG__FORMFACTOR = { 'data': { 'formfactor': client.formfactor.formfactor, 'rackunits': client.formfactor.rackunits } }
-
- // Rack segmentation
- if (client.location.bay !== undefined && client.location.bay !== null) {
- // Get all assigned objects of the rack (parentid) to check for existing rack segments at slot position.
- const rackobjectsBody = this.getBody('cmdb.category.read', { 'apikey': c.apikey, 'object': client.parentId, 'objID': client.parentId, 'category': 'C__CATG__OBJECT' }, 'get_rack_objects')
- const rackobjects = await this.axiosRequest(c.url, [rackobjectsBody], headers)
-
- // Get the name of the rack
- console.log('')
- console.log('Get Rack Name:')
- const rackBody = this.getBody('cmdb.category.read', { 'apikey': c.apikey, 'object': client.parentId, 'objID': client.parentId, 'category': 'C__CATG__GLOBAL' }, 'get_rack')
- const rack = await this.axiosRequest(c.url, [rackBody], headers)
-
- if (!rack[0].result) return { error: 'IDOIT_ERROR', message: rack[0].error.message }
-
- const rackName = rack[0].result[0].title
-
- let objectPositionBodies = []
-
- // For each segmentation object in the rack get the slot number
- console.log('')
- console.log('Get Slot IDs Request:')
- for (let obj in rackobjects[0].result) {
- const object = rackobjects[0].result[obj]
- if (object.assigned_object.type !== 'C__OBJTYPE__RACK_SEGMENT') continue
- objectPositionBodies.push(this.getBody('cmdb.category.read', { 'apikey': c.apikey, 'object': object.objID, 'objID': object.objID, 'category': 'C__CATG__LOCATION' }, 'get_rack_object_position_' + object.objID))
- }
- let objectPositions = await this.axiosRequest(c.url, objectPositionBodies, headers)
- if (objectPositions.length >= 1) objectPositions = objectPositions.filter(x => parseInt(x.result[0].pos.title) === parseInt(client.location.slot))
-
- // There should only be one segment object for the rack slot if so set it as parent
- var chassisId
- if (objectPositions.length === 1) chassisId = parseInt(objectPositions[0].result[0].objID)
- else {
- // Create a new rack segment
- const createSegmentParamObject = {
- 'apikey': c.apikey,
- 'type': 92,
- 'title': rackName + ' Slot ' + client.location.slot,
- 'categories': {
- 'C__CATS__CHASSIS': {
- 'data': {
- 'front_x': 2,
- 'front_y': 1,
- 'rear_x': 0,
- 'rear_y': 0
- }
- },
- 'C__CATS__CHASSIS_SLOT': [
- {
- 'title': 'Bay 1',
- 'insertion': 'front',
- 'from_x': 0,
- 'to_x': 0,
- 'from_y': 0,
- 'to_y': 0
- },
- {
- 'title': 'Bay 2',
- 'insertion': 'front',
- 'from_x': 1,
- 'to_x': 1,
- 'from_y': 0,
- 'to_y': 0
- }
- ],
- 'C__CATG__LOCATION': {
- 'data': {
- 'parent': client.parentId,
- 'option': client.location.option,
- 'insertion': client.location.insertion,
- 'pos': client.location.slot
- }
- }
- }
- }
-
- console.log('')
- console.log('Create Segment Request:')
- const createSegmentParam = this.getBody('cmdb.object.create', createSegmentParamObject, 'create_segment')
- const createSegment = await this.axiosRequest(c.url, [createSegmentParam], headers)
- chassisId = createSegment[0].result.id
-
- /*
- // Set the new rack units height. (Needs an extra request, why? I DONT KNOW... idoit...)
- const setSegmentSizeParams = this.getBody('cmdb.category.save', { 'apikey': c.apikey,
- 'object': chassisId,
- 'objID': chassisId,
- 'category': 'C__CATG__FORMFACTOR',
- 'data': { 'rackunits': client.formfactor.rackunits } }, 'set_segment_size')
- await this.axiosRequest(c.url, [setSegmentSizeParams], headers)
- */
- }
- }
- }
-
- // Add categories to the object
- if (client.uuid) params.categories.C__CATG__MODEL = { 'data': { 'productid': client.uuid } }
- if (client.networks) {
- params.categories.C__CATG__IP = []
- for (let index in client.networks) {
- const network = client.networks[index]
- let networkparams = {}
- if (network.ip) networkparams.ipv4_address = network.ip
- if (network.hostname) networkparams.hostname = network.hostname
- if (network.domain) networkparams.domain = network.domain
- if (network.net) networkparams.net = network.net
- if (network.primary) networkparams.primary = network.primary ? 1 : 0
- params.categories.C__CATG__IP.push(networkparams)
- }
- }
-
- // Add contact assignment to the object.
- if (client.contacts) {
- // Get the persons ids.
- let readPersonBodies = []
- for (let index in client.contacts) {
- readPersonBodies.push(this.getBody('cmdb.objects.read', {
- 'apikey': c.apikey,
- 'filter': {
- 'type': 'C__OBJTYPE__PERSON',
- 'first_name': client.contacts[index].first_name,
- 'last_name': client.contacts[index].last_name
- }
- }, 'read_persons_' + index))
- }
- console.log('')
- console.log('Read Person Request:')
- const requestReadPersons = await this.axiosRequest(c.url, readPersonBodies, headers)
- if (requestReadPersons.error) return requestReadPersons
- const error = requestReadPersons.filter(x => x.error)
-
- if (error.length === 0) {
- const personIds = requestReadPersons.map(x => {
- if (x.result.length === 1) return x.result[0].id
- }).filter(Boolean)
- params.categories.C__CATG__CONTACT = []
- for (let index in personIds) {
- params.categories.C__CATG__CONTACT.push({
- 'contact': personIds[index]
- })
- }
- } else console.log(error)
- }
-
- // Add operating system information.
- if (client.runtime && client.runtime.operating_system) {
- // Get the operating system ids.
- console.log('')
- console.log('Get OS Request:')
- const getOSParam = {
- 'apikey': c.apikey,
- 'filter': {
- 'type': 35, // 35 = Operating System
- 'title': client.runtime.operating_system.name
- }
- }
- const getOSBody = this.getBody('cmdb.objects.read', getOSParam, 'get_os')
- const requestGetOS = await this.axiosRequest(c.url, [getOSBody], headers)
-
- // Extra request for getting the id of the version number
- console.log('')
- console.log('Get OS-Version Request:')
- const getOSVersionParam = {
- 'apikey': c.apikey,
- 'objID': requestGetOS[0].result[0].id,
- 'catgID': 'C__CATG__VERSION'
- }
- const getOSVersionBody = this.getBody('cmdb.category.read', getOSVersionParam, 'get_os_version')
- const requestGetOSVersion = await this.axiosRequest(c.url, [getOSVersionBody], headers)
- const osVersion = requestGetOSVersion[0].result.filter(x => x.title === client.runtime.operating_system.version)
-
- // Add the result of the OS request (ids) to the create request.
- if (requestGetOS[0].result) {
- params.categories.C__CATG__OPERATING_SYSTEM = {
- 'data': {
- 'application': requestGetOS[0].result[0].id,
- 'assigned_version': osVersion[0].id
- }
- }
- }
}
// Send the create request.
- console.log('')
- console.log('Create Client Request:')
const body = this.getBody('cmdb.object.create', params, 'client_create')
const requestCreate = await this.axiosRequest(c.url, [body], headers)
@@ -384,82 +195,21 @@ class IdoitBackend extends ExternalBackends {
if (requestCreate.error) return { error: requestCreate.errno, message: 'Connection was refused.' }
else if (requestCreate[0].error) return { error: 'IDOIT_ERROR', message: requestCreate[0].error.message }
- // Add mac address: Network port is a subcategory of network so it need an extra request.
- let macRequests = []
- const hostnameIds = requestCreate[0].result.categories.C__CATG__IP
-
- if (client.networks) {
- let macBodies = []
- for (let index in client.networks) {
- const network = client.networks[index]
- // For the ip adresses
- // network.id = requestCreate[0].result.categories.C__CATG__IP[index]
- let addresses = []
- // Push the ids as string
- if (hostnameIds.length > index) addresses.push('' + hostnameIds[index])
-
- let paramsMac = {
- 'object': requestCreate[0].result.id,
- 'objID': requestCreate[0].result.id,
- 'category': 'C__CATG__NETWORK_PORT',
- 'data': {
- 'mac': network.mac,
- 'addresses': addresses
- },
- 'apikey': c.apikey
- }
-
- if (network.device) {
- if (network.device.speed) {
- paramsMac.data.speed = parseFloat(network.device.speed)
- // MB/s GB/s ... etc not supported?! Only Mbit/s Gbit/s ...
- paramsMac.data.speed_type = null
- }
-
- if (network.device.name) paramsMac.data.title = network.device.name
- if (network.device.type) paramsMac.data.port_type = network.device.type
- }
-
- macBodies.push(this.getBody('cmdb.category.save', paramsMac, 'add_mac_address_' + index))
- }
-
- console.log('')
- console.log('Add MAC Request:')
- const response = await this.axiosRequest(c.url, macBodies, headers)
- macRequests.push(response)
- }
-
- // If chassis id is set, assign the object to the chassis bay
- if (chassisId) {
- // Read bay ids
- const paramsSlots = {
- 'object': chassisId,
- 'objID': chassisId,
- 'category': 'C__CATS__CHASSIS_SLOT',
- 'apikey': c.apikey
- }
-
- console.log('')
- console.log('Read Rack Slots:')
- const readSlotsParam = this.getBody('cmdb.category.read', paramsSlots, 'read_slots')
- const readSlots = await this.axiosRequest(c.url, [readSlotsParam], headers)
- const bays = readSlots[0].result
-
- console.log('')
- console.log('Assign to Rack Slot:')
- const assignToSlotBody = this.getBody('cmdb.category.save', { 'apikey': c.apikey,
- 'objID': chassisId,
- 'object': chassisId,
- 'category': 'C__CATS__CHASSIS_DEVICES',
- 'data': { 'assigned_device': requestCreate[0].result.id, 'assigned_slots': [bays[client.location.bay].id] } }, 'assign_to_slot')
- await this.axiosRequest(c.url, [assignToSlotBody], headers)
- }
+ // Add id to the client and call the update method for all the other hardware information
+ client.id = requestCreate[0].result.id
+ const update = await this.updateClient(credentials, client)
// Purpose for Clients:
// 1 = Production | 5 = PVS
// 2 = Test | 7 = Pool PC
// 3 = Quality Assurance | 8 = Mitarbeiter Arbeitsplatz
- return { succes: true, id: requestCreate[0].result.id, type: params.type, message: requestCreate[0].result.message, macRequests: macRequests }
+ return {
+ succes: true,
+ id: requestCreate[0].result.id,
+ type: params.type,
+ message: requestCreate[0].result.message,
+ responses: [requestCreate, ...update.response]
+ }
}
/*
@@ -468,11 +218,43 @@ class IdoitBackend extends ExternalBackends {
* credentials: <BACKEND_CREDENTIALS>
* The client parameters are all optional.
* client: {
- * id: <CLIENT_ID>, title: <CLIENT_TITLE>, parentId: <PARENT_ID>,
- * system: { model: <SYSTEM_MODEL>, manufacturer: <SYSTEM_MANUFACTURER>, serialnumber: <SYSTEM_SERIALNUMBER> },
- * cpu: { model: <CPU_MODEL>, manufacturer: <CPU_MANUFACTURER>, type: <CPU_TYPE>, frequency: <CPU_FREQUENCY>, cores: <CPU_CORES> },
- * ram: [{ title: <RAM_TITLE>, manufacturer: <RAM_MANUFACTURER>, type: <RAM_TYPE>, capacity: <RAM_CAPACITY>, unit: <RAM_UNIT> }, ...],
- * drives: [{model: <DRIVE_MODEL>,}, serial: <DRIVE_SERIAL>, capacity: <DRIVE_CAPACITY>, unit: <DRIVE_UNIT>, type: <DRIVE_TYPE>, formfactor: <DRIVE_FORMFACTOR>, connection: <DRIVE_CONNECTION> ...]
+ * id: <CLIENT_ID>,
+ * title: <CLIENT_TITLE>,
+ * parentId: <PARENT_ID>,
+ * system: {
+ * model: <SYSTEM_MODEL>,
+ * manufacturer: <SYSTEM_MANUFACTURER>,
+ * serialnumber: <SYSTEM_SERIALNUMBER>
+ * },
+ * cpu: {
+ * model: <CPU_MODEL>,
+ * manufacturer: <CPU_MANUFACTURER>,
+ * type: <CPU_TYPE>,
+ * frequency: <CPU_FREQUENCY>,
+ * cores: <CPU_CORES>
+ * },
+ * ram: [
+ * {
+ * title: <RAM_TITLE>,
+ * manufacturer: <RAM_MANUFACTURER>,
+ * type: <RAM_TYPE>,
+ * capacity: <RAM_CAPACITY>,
+ * unit: <RAM_UNIT>
+ * },
+ * ...
+ * ],
+ * drives: [
+ * {
+ * model: <DRIVE_MODEL>,
+ * serial: <DRIVE_SERIAL>,
+ * capacity: <DRIVE_CAPACITY>,
+ * unit: <DRIVE_UNIT>,
+ * type: <DRIVE_TYPE>,
+ * formfactor: <DRIVE_FORMFACTOR>,
+ * connection: <DRIVE_CONNECTION>
+ * },
+ * ...
+ * ]
* }
*/
async updateClient (credentials, client) {
@@ -485,190 +267,418 @@ class IdoitBackend extends ExternalBackends {
client.parentId = parseInt(client.parentId)
let bodies = []
- // workaround for the fucking idoit shit. -.-
- let requestResults = []
-
- // Update title of the object
- if (client.name) bodies.push(this.getBody('cmdb.object.update', { 'id': client.id, 'title': client.name, 'apikey': c.apikey }, 'update_title'))
- // 'object' should be 'objID' but there is a fucking bug in the idoit api. Soo let's add both parameters because else it will break when they fix it.
- // Update the productid to the uuid.
- if (client.uuid) {
+ // Helper function for creating categorie update requests
+ let createCategorieRequest = (categorie, method, additionalParams, idCounter = 0) => {
+ // 'object' should be 'objID' but there is a bug in the idoit api.
+ // Soo let's add both parameters because else it will break when they fix it. -.-i
let params = {
'object': client.id,
'objID': client.id,
- 'category': 'C__CATG__MODEL',
- 'data': {
- 'productid': client.uuid
- },
- 'apikey': c.apikey
+ 'category': categorie,
+ 'apikey': c.apikey,
+ ...additionalParams
}
- bodies.push(this.getBody('cmdb.category.save', params, 'update_uuid'))
+ return this.getBody(`cmdb.category.${method}`, params, `${method.toUpperCase()}_${categorie}_${idCounter}`)
}
- if (client.parentId) {
- // Update the object. Location
- let paramsLocation = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__LOCATION',
- 'data': {
- 'parent': client.parentId
- },
+ let createUpdateCategorieRequest = (categorie, data, additionalParams, idCounter) => {
+ return createCategorieRequest(categorie, 'save', { data: data, ...additionalParams }, idCounter)
+ }
+
+ // Update title of the object
+ if (client.name) {
+ const params = {
+ 'id': client.id,
+ 'title': client.name,
'apikey': c.apikey
}
- bodies.push(this.getBody('cmdb.category.save', paramsLocation, 'update_parent'))
+ bodies.push(this.getBody('cmdb.object.update', params, 'update_title'))
}
- // Update the object. Model data.
- if (client.system) {
- let params = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__MODEL',
- 'data': {
- 'manufacturer': client.system.manufacturer,
- 'title': client.system.model,
- 'serial': client.system.serialnumber
- },
- 'apikey': c.apikey
+ // Update the productid to the uuid and model/system data.
+ if (client.uuid || client.system) {
+ const modelData = {
+ ...(client.uuid && {
+ 'productid': client.uuid
+ }),
+ ...(client.system && {
+ ...(client.system.manufacturer && {
+ 'manufacturer': client.system.manufacturer
+ }),
+ ...(client.system.model && {
+ 'title': client.system.model
+ }),
+ ...(client.system.serialnumber && {
+ 'serial': client.system.serialnumber
+ })
+ })
}
- bodies.push(this.getBody('cmdb.category.save', params, 'update_model'))
+
+ bodies.push(createUpdateCategorieRequest('C__CATG__MODEL', modelData))
}
- // Update networks & Check if there is at least one network which will get updated.
- if (client.networks && client.networks.filter(network => Object.keys(network).length >= 4).length !== 0) {
- let parmReadNetwork = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__IP',
- 'apikey': c.apikey
+ // For all objects, where a client can have mutiple of, we need to check if we need to update a similar entrie or if it's a new one
+ // Get all data, where the clients wants to update
+ const getObjectsBodies = []
+
+ if (client.networks && client.networks.length > 0) {
+ // Get all ips and macs to check if an update is needed
+ getObjectsBodies.push(createCategorieRequest('C__CATG__IP', 'read'))
+ getObjectsBodies.push(createCategorieRequest('C__CATG__NETWORK_PORT', 'read'))
+ }
+ if (client.cpus && client.cpus.length > 0) getObjectsBodies.push(createCategorieRequest('C__CATG__CPU', 'read'))
+ if (client.gpus && client.gpus.length > 0) getObjectsBodies.push(createCategorieRequest('C__CATG__GRAPHIC', 'read'))
+ if (client.ram && client.ram.modules && client.ram.modules.length > 0) getObjectsBodies.push(createCategorieRequest('C__CATG__MEMORY', 'read'))
+ if (client.drives && client.drives.length > 0) getObjectsBodies.push(createCategorieRequest('C__CATG__STORAGE_DEVICE', 'read'))
+ if (client.monitors && client.monitors.length > 0) getObjectsBodies.push(createCategorieRequest('C__CATG__CUSTOM_FIELDS_MONITOR', 'read'))
+ if (!client.parentId && client.location && client.location.bay) getObjectsBodies.push(createCategorieRequest('C__CATG__LOCATION', 'read'))
+ if (client.contacts && client.contacts.length > 0) {
+ // Get the already attached objects
+ getObjectsBodies.push(createCategorieRequest('C__CATG__CONTACT', 'read'))
+
+ // Get the whole persons list
+ getObjectsBodies.push(
+ this.getBody(
+ 'cmdb.objects.read',
+ {
+ 'apikey': c.apikey,
+ 'filter': {
+ 'type': 'C__OBJTYPE__PERSON'
+ }
+ },
+ 'READ_C__OBJTYPE__PERSON'
+ )
+ )
+ }
+
+ const boundObjects = await this.axiosRequest(c.url, getObjectsBodies, headers)
+
+ // Add contact assignment to the object.
+ if (client.contacts) {
+ // Get the persons ids.
+ const persons = boundObjects.filter(response => response.id.startsWith('READ_C__OBJTYPE__PERSON'))[0].result
+ let counter = 0
+
+ // Helper method for replaceing ä -> ae, ü -> ue, ö -> oe
+ const replaceUmlauts = input => {
+ return input.toLowerCase().replace(/\u00e4/g, 'ae').replace(/\u00fc/g, 'ue').replace(/\u00f6/g, 'oe')
}
- const ips = await this.axiosRequest(c.url, [this.getBody('cmdb.category.read', parmReadNetwork, 'read_ips')], headers)
- parmReadNetwork.category = 'C__CATG__NETWORK_PORT'
- const macs = await this.axiosRequest(c.url, [this.getBody('cmdb.category.read', parmReadNetwork, 'read_macs')], headers)
-
- for (let index in client.networks) {
- const network = client.networks[index]
- if (network.ip && network.hostname && network.domain) {
- // Update ip addresses
- let paramsIp = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__IP',
- 'data': {
+
+ for (let contact of client.contacts) {
+ const firstname = replaceUmlauts(contact.firstname)
+ const lastname = replaceUmlauts(contact.lastname)
+
+ const contactPerson = persons.filter(person => {
+ if (!replaceUmlauts(person.title).includes(firstname)) return false
+ else if (!replaceUmlauts(person.title).includes(lastname)) return false
+ return true
+ })
+
+ let contactId
+ if (contactPerson.length > 0) contactId = contactPerson[0].id
+
+ // Check if the contact is already assigned
+ const bContacts = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__CONTACT'))
+ let bContactExists = false
+ if (bContacts.length > 0) {
+ bContactExists = bContacts[0].result.filter(c => parseInt(c.contact.id) === contactId).length > 0
+ }
+
+ if (contactId && !bContactExists) {
+ const contactData = {
+ 'contact': contactId,
+ 'primary': counter === 1 ? 1 : 0
+ }
+ bodies.push(createUpdateCategorieRequest('C__CATG__CONTACT', contactData, {}, counter))
+ counter++
+ }
+ }
+ }
+
+ // Update networks & Check if there is at least one network which will get updated.
+ if (client.networks && client.networks.length > 0) {
+ // Returns an array with the existing objects
+ const ips = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__IP'))[0].result
+ .map(ip => {
+ return {
+ id: ip.id,
+ ip: ip.ipv4_address.ref_title,
+ hostname: ip.hostname,
+ domain: ip.domain
+ }
+ })
+
+ const macs = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__NETWORK_PORT'))[0].result
+ .map(mac => {
+ return {
+ id: mac.id,
+ name: mac.title,
+ mac: mac.mac,
+ ip: mac.addresses.length > 0 ? mac.addresses[0] : undefined
+ }
+ })
+
+ let counter = 0
+ for (let network of client.networks) {
+ let updateIp = true
+ const networkObject = {
+ ip: network.ip,
+ hostname: network.hostname,
+ domain: network.domain
+ }
+
+ // Check if there is an existing object with a similar entrie
+ const similarObjects = []
+ for (let ipObj of ips) {
+ const simplifiedIpObject = {
+ ip: ipObj.ip,
+ hostname: ipObj.hostname ? ipObj.hostname : undefined,
+ domain: ipObj.domain ? ipObj.domain : undefined
+ }
+ // Check weather there is already an item which has the same attributes
+ if (JSON.stringify(networkObject) === JSON.stringify(simplifiedIpObject)) {
+ updateIp = false
+ break
+ }
+ if (network.ip && network.ip === ipObj.ip) similarObjects.push(ipObj)
+ else if (network.hostname && network.hostname !== '' && network.hostname === ipObj.hostname) similarObjects.push(ipObj)
+ // else if (network.ipv6 === ip.ipv6_address.ref_title) similarObjects.push(ipObj)
+ // else if (network.domain && network.domain === ipObj.domain) similarObjects.push(ipObj)
+ }
+
+ let ipEntryIds = []
+ if (updateIp) {
+ const ipData = {
+ ...(network.ip && {
'ipv4_address': network.ip,
- 'hostname': network.hostname,
- 'domain': network.domain,
- 'net': network.net
- },
- 'apikey': c.apikey
+ 'primary': network.primary ? 1 : 0
+ }),
+ ...(network.hostname && {
+ 'hostname': network.hostname
+ }),
+ ...(network.domain && {
+ 'domain': network.domain
+ })
+ // TODO: ipv6 needs a different host address type. Not possible to set both?
+ // ...(network.ipv6 && {
+ // 'ipv6_address': network.ipv6
+ // }),
+ }
+
+ let addParams = {}
+
+ // For now update the first one if there are more
+ if (similarObjects.length > 0) {
+ addParams.entry = parseInt(similarObjects[0].id)
+ }
+
+ if (Object.keys(ipData).length !== 0) {
+ // If we update an existing entry, we don't need the entry id, else we need to create the object to get the entry id for the mac object.
+ const ipBody = createUpdateCategorieRequest('C__CATG__IP', ipData, addParams, counter)
+ if (addParams.entry) {
+ bodies.push(ipBody)
+ ipEntryIds.push(addParams.entry)
+ } else {
+ const createIpResponse = await this.axiosRequest(c.url, [ipBody], headers)
+ ipEntryIds.push(createIpResponse[0].result.entry)
+ }
}
- if (ips[0].result.length > index) paramsIp.entry = parseInt(ips[0].result[index].id)
- bodies.push(this.getBody('cmdb.category.save', paramsIp, 'update_ip'))
}
- // Update mac addresses
- if (network.mac) {
- let paramsMac = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__NETWORK_PORT',
- 'data': {
- 'mac': network.mac
- },
- 'apikey': c.apikey
+ // Check for similar mac objects
+ let updateMac = true
+ const similarMacs = []
+ for (let mac of macs) {
+ const simplifiedMacObject = {
+ name: mac.name,
+ mac: mac.mac
}
- if (macs[0].result.length > index) paramsMac.entry = parseInt(macs[0].result[index].id)
- bodies.push(this.getBody('cmdb.category.save', paramsMac, 'update_mac'))
+
+ if (JSON.stringify({ name: network.name, mac: network.mac }) === JSON.stringify(simplifiedMacObject)) {
+ updateMac = false
+ break
+ }
+ if (network.name && network.name === mac.title) similarMacs.push(mac)
+ // Can't go with mac because two diffrent interface could have the same mac
+ // else if (network.mac && network.mac === mac.mac) return true
+ }
+
+ let addParams = {}
+ if (similarMacs.length > 0) {
+ addParams.entry = parseInt(similarMacs[0].id)
}
+
+ if (updateMac) {
+ // Update Mac-Address
+ const macData = {
+ 'title': network.name,
+ 'mac': network.mac,
+ 'addresses': ipEntryIds
+ }
+
+ bodies.push(createUpdateCategorieRequest('C__CATG__NETWORK_PORT', macData, addParams, counter))
+ }
+ counter++
}
}
// Update the object. CPU data.
- // TODO: Delete cpu if exists?
if (client.cpus) {
- let counter = 1
+ const boundedCpus = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__CPU'))[0].result
+ .map(cpu => {
+ return {
+ id: cpu.id,
+ title: cpu.title,
+ manufacturer: cpu.manufacturer.title,
+ type: cpu.type.title,
+ frequency: cpu.frequency.title,
+ frequency_unit: parseInt(cpu.frequency_unit.id),
+ cores: parseInt(cpu.cores)
+ }
+ })
+
+ let counter = 0
for (let cpu of client.cpus) {
if (cpu.unit === 'MHz') cpu.unit = 2
else if (cpu.unit === 'GHz') cpu.unit = 3
- let params = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__CPU',
- 'data': {
- 'manufacturer': cpu.manufacturer,
- 'title': cpu.model,
- 'type': cpu.type,
- 'frequency': parseFloat(cpu.frequency),
- 'frequency_unit': cpu.unit,
- 'cores': parseInt(cpu.cores)
- },
- 'apikey': c.apikey
+ const cpuData = {
+ 'title': cpu.model,
+ 'manufacturer': cpu.manufacturer,
+ 'type': cpu.type,
+ 'frequency': parseFloat(cpu.frequency),
+ 'frequency_unit': cpu.unit,
+ 'cores': parseInt(cpu.cores)
}
- counter++
- bodies.push(this.getBody('cmdb.category.save', params, 'create_cpu_' + counter))
+ let updateCpu = true
+ for (let bCpu of boundedCpus) {
+ let simplifiedCpuObject = { ...bCpu }
+ delete simplifiedCpuObject.id
+
+ if (JSON.stringify(cpuData) === JSON.stringify(simplifiedCpuObject)) {
+ updateCpu = false
+ break
+ }
+ }
+
+ if (updateCpu) {
+ bodies.push(createUpdateCategorieRequest('C__CATG__CPU', cpuData, undefined, counter))
+ counter++
+ }
}
}
// GPUS
if (client.gpus) {
- let counter = 1
+ const boundedGpus = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__GRAPHIC'))[0].result
+ .map(gpu => {
+ return {
+ id: gpu.id,
+ title: gpu.title,
+ manufacturer: gpu.manufacturer.title,
+ ...(gpu.memory && { 'memory': gpu.memory }),
+ ...(gpu.unit && { 'unit': gpu.unit })
+ }
+ })
+
+ let counter = 0
for (let gpu of client.gpus) {
- let params = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__GRAPHIC',
- 'data': {
- 'manufacturer': gpu.manufacturer,
- 'title': gpu.model,
- 'memory': gpu.memory,
- 'unit': gpu.unit
- },
- 'apikey': c.apikey
+ const gpuData = {
+ 'title': gpu.model,
+ 'manufacturer': gpu.manufacturer,
+ ...(gpu.memory && { 'memory': gpu.memory }),
+ ...(gpu.unit && { 'unit': gpu.unit })
}
- counter++
- bodies.push(this.getBody('cmdb.category.save', params, 'create_gpu_' + counter))
+ let updateGpu = true
+ for (let bGpu of boundedGpus) {
+ let simplifiedGpuObject = { ...bGpu }
+ delete simplifiedGpuObject.id
+
+ if (JSON.stringify(gpuData) === JSON.stringify(simplifiedGpuObject)) {
+ updateGpu = false
+ break
+ }
+ }
+
+ if (updateGpu) {
+ bodies.push(createUpdateCategorieRequest('C__CATG__GRAPHIC', gpuData, undefined, counter))
+ counter++
+ }
}
}
// Update the object. Ram data.
if (client.ram) {
- let counter = 1
+ const boundedRams = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__MEMORY'))[0].result
+ .map(bRam => {
+ return {
+ id: bRam.id,
+ title: bRam.title.title,
+ manufacturer: bRam.manufacturer.title,
+ capacity: bRam.capacity.title,
+ unit: bRam.unit.id,
+ type: bRam.type.title,
+ description: bRam.description
+ }
+ })
+
+ let counter = 0
for (let module of client.ram.modules) {
// Add KB and TB
- if (module.unit === 'MB') module.unit = 2
- else if (module.unit === 'GB') module.unit = 3
+ if (module.unit === 'MB') module.unit = '2'
+ else if (module.unit === 'GB') module.unit = '3'
+
+ let ramData = {
+ 'title': module.model,
+ 'manufacturer': module.manufacturer,
+ 'capacity': parseFloat(module.capacity),
+ 'unit': module.unit,
+ 'type': module.type,
+ 'description': JSON.stringify({
+ serialnumber: module.serialnumber,
+ formfactor: module.formfactor,
+ speed: module.speed
+ })
+ }
- let params = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__MEMORY',
- 'data': {
- 'title': module.model,
- 'manufacturer': module.manufacturer,
- 'type': module.type,
- 'capacity': parseFloat(module.capacity),
- 'unit': module.unit,
- 'description': JSON.stringify({
- serialnumber: module.serialnumber,
- formfactor: module.formfactor,
- speed: module.speed
- })
- },
- 'apikey': c.apikey
+ let updateRam = true
+ for (let bRam of boundedRams) {
+ let simplifiedRamObject = { ...bRam }
+ delete simplifiedRamObject.id
+
+ if (JSON.stringify(ramData) === JSON.stringify(simplifiedRamObject)) {
+ updateRam = false
+ break
+ }
+ }
+
+ if (updateRam) {
+ bodies.push(createUpdateCategorieRequest('C__CATG__MEMORY', ramData, undefined, counter))
+ counter++
}
- counter++
- bodies.push(this.getBody('cmdb.category.save', params, 'create_memory_' + counter))
}
}
// Update the object. Drive data.
if (client.drives) {
- let counter = 1
+ const boundedDrives = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__STORAGE_DEVICE'))[0].result
+ .map(bDrive => {
+ return {
+ id: bDrive.id,
+ title: bDrive.title,
+ type: bDrive.type.title,
+ ...(bDrive.firmware && { firmware: bDrive.firmware }),
+ ...(bDrive.capacity && { capacity: bDrive.capacity.title }),
+ ...(bDrive.unit && { unit: parseInt(bDrive.unit.id) }),
+ ...(bDrive.serial && { serial: bDrive.serial }),
+ ...(bDrive.connected && { connected: bDrive.connected.title })
+ }
+ })
+
+ let counter = 0
for (let index in client.drives) {
const drive = client.drives[index]
// UNIT
@@ -683,33 +693,183 @@ class IdoitBackend extends ExternalBackends {
else if (drive.type === '7200 rpm') drive.type = 'Hard disk'
else if (drive.type === '') drive.type = 'Hard disk'
- let params = {
- 'object': client.id,
- 'objID': client.id,
- 'category': 'C__CATG__STORAGE_DEVICE',
+ let driveData = {
+ 'title': drive.model,
+ 'type': drive.type,
+ ...(drive.firmware && { 'firmware': drive.firmware }),
+ ...(drive.capacity && { 'capacity': parseFloat(drive.capacity) }),
+ ...(drive.unit && { 'unit': drive.unit }),
+ ...(drive.serial && { 'serial': drive.serial }),
+ ...(drive.connection && { 'connected': drive.connection })
+ }
+
+ let updateDrive = true
+ for (let bDrive of boundedDrives) {
+ let simplifiedDriveObject = { ...bDrive }
+ delete simplifiedDriveObject.id
+
+ if (JSON.stringify(driveData) === JSON.stringify(simplifiedDriveObject)) {
+ updateDrive = false
+ break
+ }
+ }
+
+ if (updateDrive) {
+ bodies.push(createUpdateCategorieRequest('C__CATG__STORAGE_DEVICE', driveData, undefined, counter))
+ counter++
+ }
+ }
+ }
+
+ // Add Monitors
+ if (client.monitors) {
+ // Monitor is a custom object, therefore the fieldnames can be looked up in the idoit interface / api
+ const boundedMonitors = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__CUSTOM_FIELDS_MONITOR'))[0].result
+ .map(bMonitor => {
+ return {
+ id: bMonitor.id,
+ f_text_c_1618324877787: bMonitor.f_text_c_1618324877787,
+ f_text_c_1618324891521: bMonitor.f_text_c_1618324891521,
+ f_text_c_1618328160730: bMonitor.f_text_c_1618328160730,
+ f_text_c_1618328167672: bMonitor.f_text_c_1618328167672,
+ f_text_c_1620145359196: bMonitor.f_text_c_1620145359196,
+ f_text_c_1622557969975: bMonitor.f_text_c_1622557969975
+ }
+ })
+
+ let counter = 0
+ for (let monitor of client.monitors) {
+ const monitorData = {
+ 'f_text_c_1618324877787': monitor.model, // Model
+ 'f_text_c_1618324891521': monitor.vendor, // Manufacturer
+ 'f_text_c_1618328160730': `${monitor.resolution.width}x${monitor.resolution.height}`, // Resolution
+ 'f_text_c_1618328167672': monitor.serialnumber, // Serial Number
+ 'f_text_c_1620145359196': `${monitor.dimensions.inch}"`, // Display Size (Inch)
+ 'f_text_c_1622557969975': monitor.port
+ }
+
+ let updateMonitor = true
+ for (let bMonitor of boundedMonitors) {
+ let simplifiedMonitorObject = { ...bMonitor }
+ delete simplifiedMonitorObject.id
+
+ if (JSON.stringify(monitorData) === JSON.stringify(simplifiedMonitorObject)) {
+ updateMonitor = false
+ break
+ }
+ }
+
+ if (updateMonitor) {
+ bodies.push(createUpdateCategorieRequest('C__CATG__CUSTOM_FIELDS_MONITOR', monitorData, undefined, counter))
+ counter++
+ }
+ }
+ }
+
+ /* OPERTATING SYSTEM REWORK TODO
+ // Add operating system information.
+ if (client.runtime && client.runtime.operating_system) {
+ // Get the operating system ids.
+ const getOSParam = {
+ 'apikey': c.apikey,
+ 'filter': {
+ 'type': 35, // 35 = Operating System
+ 'title': client.runtime.operating_system.name
+ }
+ }
+ const getOSBody = this.getBody('cmdb.objects.read', getOSParam, 'get_os')
+ const requestGetOS = await this.axiosRequest(c.url, [getOSBody], headers)
+
+ // Extra request for getting the id of the version number
+ const getOSVersionParam = {
+ 'apikey': c.apikey,
+ 'objID': requestGetOS[0].result[0].id,
+ 'catgID': 'C__CATG__VERSION'
+ }
+ const getOSVersionBody = this.getBody('cmdb.category.read', getOSVersionParam, 'get_os_version')
+ const requestGetOSVersion = await this.axiosRequest(c.url, [getOSVersionBody], headers)
+ const osVersion = requestGetOSVersion[0].result.filter(x => x.title === client.runtime.operating_system.version)
+
+ // Add the result of the OS request (ids) to the create request.
+ if (requestGetOS[0].result) {
+ params.categories.C__CATG__OPERATING_SYSTEM = {
'data': {
- 'category_id': counter,
- 'title': drive.model,
- 'type': drive.type,
- 'firmware': drive.firmware,
- // 'manufacturer': ,
- // 'model': ,
- 'capacity': parseFloat(drive.capacity),
- 'unit': drive.unit,
- 'serial': drive.serial,
- 'connected': drive.connection
- },
- 'apikey': c.apikey
+ 'application': requestGetOS[0].result[0].id,
+ 'assigned_version': osVersion[0].id
+ }
}
+ }
+ }
+ */
- bodies.push(this.getBody('cmdb.category.save', params, 'create_drive_' + counter))
- counter++
+ // Update the object location.
+ if (client.parentId && (!client.location || (client.location && !client.location.slot))) {
+ // Either client or no slot was set
+ const locationData = {
+ 'parent': client.parentId
+ }
+
+ bodies.push(createUpdateCategorieRequest('C__CATG__LOCATION', locationData))
+ } else {
+ // Create segments and prepare bay
+
+ // Rack segmentation
+ if (!!client.location && !!client.location.slot) {
+ // Get the parentId if none was given
+ if (!client.parentId) {
+ let boundedLocation = boundObjects.filter(response => response.id.startsWith('READ_C__CATG__LOCATION'))
+ if (boundedLocation.length > 0) boundedLocation = boundedLocation[0].result
+ if (boundedLocation.length > 0) client.parentId = parseInt(boundedLocation[0].location_path)
+ }
+
+ // Call method for getting the chassis id, can be either an existing one or a new created one.
+ const chassisId = await this.getChassisId(c, client, headers)
+
+ // If chassis id is set, assign the object to the chassis bay
+ if (chassisId) {
+ // Read bay ids
+ const paramsSlots = {
+ 'object': parseInt(chassisId),
+ 'objID': parseInt(chassisId),
+ 'category': 'C__CATS__CHASSIS_SLOT',
+ 'apikey': c.apikey
+ }
+
+ const readSlotsParam = this.getBody('cmdb.category.read', paramsSlots, 'read_slots')
+ const readSlots = await this.axiosRequest(c.url, [readSlotsParam], headers)
+ const bays = readSlots[0].result
+
+ const assignToSlotData = {
+ 'assigned_device': client.id,
+ 'assigned_slots': [parseInt(bays[client.location.bay].id)]
+ }
+
+ bodies.push(createUpdateCategorieRequest('C__CATS__CHASSIS_DEVICES', assignToSlotData, {
+ 'object': parseInt(chassisId),
+ 'objID': parseInt(chassisId)
+ }))
+ }
}
}
+ // Only server have formfactor categories.
+ if (client.formfactor) {
+ const formfactorData = {
+ ...(client.formfactor.formfactor && {
+ 'formfactor': client.formfactor.formfactor
+ }),
+ ...(client.formfactor.rackunits && {
+ 'rackunits': client.formfactor.rackunits
+ })
+ }
+
+ bodies.push(createUpdateCategorieRequest('C__CATG__FORMFACTOR', formfactorData))
+ }
+
+ let requestUpdate = []
+ // Send the batch request to idoit
if (bodies.length > 0) {
- const requestUpdate = await this.axiosRequest(c.url, bodies, headers)
- requestResults.push(requestUpdate)
+ requestUpdate = await this.axiosRequest(c.url, bodies, headers)
if (requestUpdate.error) return requestUpdate
}
@@ -724,12 +884,129 @@ class IdoitBackend extends ExternalBackends {
success: true,
id: client.id,
type: type,
- response: requestResults
+ response: requestUpdate
}
return result
}
+ /*
+ * Rack -> Rack obeject
+ * Slot -> Rack Segment object (contains multiple bays)
+ * Bay -> Slot objects inside the Rack Segment object
+ *
+ */
+ async getChassisId (credentials, client, headers) {
+ // Check if parentId is still missing. If so skip bay assignment.
+ if (!client.parentId) return undefined
+
+ // Get all assigned objects of the rack (parentid) to check for existing rack segments at slot position.
+ const rackobjectsBody = this.getBody(
+ 'cmdb.category.read',
+ {
+ 'apikey': credentials.apikey,
+ 'object': client.parentId,
+ 'objID': client.parentId,
+ 'category': 'C__CATG__OBJECT'
+ },
+ 'get_rack_objects'
+ )
+
+ // Get the name of the rack
+ const rackBody = this.getBody(
+ 'cmdb.category.read',
+ {
+ 'apikey': credentials.apikey,
+ 'object': client.parentId,
+ 'objID': client.parentId,
+ 'category': 'C__CATG__GLOBAL'
+ },
+ 'get_rack'
+ )
+
+ const rackRequest = await this.axiosRequest(credentials.url, [rackobjectsBody, rackBody], headers)
+ const rackObjects = rackRequest[0]
+ const rack = rackRequest[1]
+
+ if (!rackObjects.result || !rack.result) return undefined
+
+ const rackName = rack.result[0].title
+ let rackSegmentId
+
+ // For each segmentation object in the rack get the slot number
+ for (let object of rackObjects.result) {
+ if (object.assigned_object.type !== 'C__OBJTYPE__RACK_SEGMENT') continue
+
+ if (object.assigned_object.title === `${rackName} Slot ${client.location.slot}`) {
+ // This is the slot where it should get added
+ rackSegmentId = object.assigned_object.id
+ break
+ }
+ }
+
+ // There should only be one segment object for the rack slot if so set it as parent
+ let chassisId = rackSegmentId
+ if (!chassisId) {
+ // Create a new rack segment
+ const createSegmentParamObject = {
+ 'apikey': credentials.apikey,
+ 'type': 92,
+ 'title': rackName + ' Slot ' + client.location.slot,
+ 'categories': {
+ 'C__CATS__CHASSIS': {
+ 'data': {
+ 'front_x': 2,
+ 'front_y': 1,
+ 'rear_x': 0,
+ 'rear_y': 0
+ }
+ },
+ 'C__CATS__CHASSIS_SLOT': [
+ {
+ 'title': 'Bay 1',
+ 'insertion': 'front',
+ 'from_x': 0,
+ 'to_x': 0,
+ 'from_y': 0,
+ 'to_y': 0
+ },
+ {
+ 'title': 'Bay 2',
+ 'insertion': 'front',
+ 'from_x': 1,
+ 'to_x': 1,
+ 'from_y': 0,
+ 'to_y': 0
+ }
+ ],
+ 'C__CATG__LOCATION': {
+ 'data': {
+ 'parent': client.parentId,
+ 'option': client.location.option ? client.location.option : 'Horizontal',
+ 'insertion': client.location.insertion ? client.location.insertion : 'Front and backside',
+ 'pos': client.location.slot
+ }
+ }
+ }
+ }
+
+ const createSegmentParam = this.getBody('cmdb.object.create', createSegmentParamObject, 'create_segment')
+ const createSegment = await this.axiosRequest(credentials.url, [createSegmentParam], headers)
+ chassisId = createSegment[0].result.id
+
+ /*
+ // Set the new rack units height. (Needs an extra request, why? I DONT KNOW... idoit...)
+ const setSegmentSizeParams = this.getBody('cmdb.category.save', { 'apikey': c.apikey,
+ 'object': chassisId,
+ 'objID': chassisId,
+ 'category': 'C__CATG__FORMFACTOR',
+ 'data': { 'rackunits': client.formfactor.rackunits } }, 'set_segment_size')
+ await this.axiosRequest(c.url, [setSegmentSizeParams], headers)
+ */
+ }
+ return chassisId
+ }
+
async getFileList (credentials, externalId) {
const c = this.mapCredentials(credentials)
const body = this.getBody('cmdb.category.read', { 'objID': externalId, 'category': 'C__CATG__FILE', apikey: c.apikey }, 'filelist')
diff --git a/server/package-lock.json b/server/package-lock.json
index 4754263..a159fef 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -877,6 +877,16 @@
"safe-buffer": "^5.0.1"
}
},
+ "edid-reader": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/edid-reader/-/edid-reader-0.3.8.tgz",
+ "integrity": "sha512-lzY7R8go0EXQXW3AtVU4mFB9ITKAtxRZDOC9j6zsu8gXj/iBikbRf+nApPzdduNdTeBocLDIVEoVND+Wa5EDFg==",
+ "requires": {
+ "bluebird": "^3.3.4",
+ "glob": "^7.0.3",
+ "lodash": "^4.6.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
diff --git a/server/package.json b/server/package.json
index 3d794ed..be0dbc3 100644
--- a/server/package.json
+++ b/server/package.json
@@ -13,6 +13,7 @@
"compression": "^1.7.3",
"cookie-parser": "^1.4.4",
"debug": "^3.2.6",
+ "edid-reader": "^0.3.8",
"express": "^4.16.4",
"express-fileupload": "^1.1.6",
"http-errors": "^1.7.2",