From 9c8b03506a6f4b850f08e7d88aae597d3ba8ce0e Mon Sep 17 00:00:00 2001 From: Jannik Schönartz Date: Tue, 22 Jun 2021 18:50:06 +0000 Subject: [server/registration/idoit] Rework to new hw-collection method + restruce of the idoit external backend --- server/lib/external-backends/backendhelper.js | 12 +- .../external-backends/backends/idoit-backend.js | 1113 ++++++++++++-------- 2 files changed, 704 insertions(+), 421 deletions(-) (limited to 'server/lib') 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: * The client parameters are all optional. - * client: { - * title: , parentId: , - * network: { mac: , ip: } - * } + * 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: * The client parameters are all optional. * client: { - * id: , title: , parentId: , - * system: { model: , manufacturer: , serialnumber: }, - * cpu: { model: , manufacturer: , type: , frequency: , cores: }, - * ram: [{ title: , manufacturer: , type: , capacity: , unit: }, ...], - * drives: [{model: ,}, serial: , capacity: , unit: , type: , formfactor: , connection: ...] + * id: , + * title: , + * parentId: , + * system: { + * model: , + * manufacturer: , + * serialnumber: + * }, + * cpu: { + * model: , + * manufacturer: , + * type: , + * frequency: , + * cores: + * }, + * ram: [ + * { + * title: , + * manufacturer: , + * type: , + * capacity: , + * unit: + * }, + * ... + * ], + * drives: [ + * { + * model: , + * serial: , + * capacity: , + * unit: , + * type: , + * formfactor: , + * 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') -- cgit v1.2.3-55-g7522