summaryrefslogblamecommitdiffstats
path: root/server/lib/external-backends/backends/infoblox-backend.js
blob: ca7a7f97764391abdf523b2d0c1bd25059942223 (plain) (tree)


























                                                                                               
                                       





                                            
 
                                               
                         


                                                                 

                                                                                  




                                                        



                                                                
          
   























                                                                                                                  













                                                          








                                                          
                                        


                                                      

                                                            
                                                                       







                                                                                             
                                               
     
 
                                                   

                                                                           

   
                                                                         










                                                                      

                                                                     
                                                           
                   
                      

                                                                                         
     

                                                              























                                                                          



                                                              

                                                          
                                                                                                                                                        
     





               














                                                          

                                                           
















                                                                             


























                                                          


















                                                                                                                                                            



                 















                                                                                     

                                              
 
                               



                           





                                                                                      
                                                                       
                                                                            
                                                                       
                                                                        
 
                          

                                                                                                                     
 

                                                                  
 

                                                
 
                  
                                                                                     
 
                      



                                                                                          


                                                                                                                                                                                                                       
 




                                                                                                         

                                                                                         
                                    
         
       
      
      





                                                                                                                                                  





                                                                                                                                        
 
                 



                                
/* global __appdir */
const path = require('path')
const ExternalBackends = require(path.join(__appdir, 'lib', 'external-backends'))
const Infoblox = require('infoblox')

class InfobloxBackend extends ExternalBackends {
  // ############################################################################
  // ######################## needed functions  #################################

  /*
   * Returns the credential structure / fields, defined in the backends.
   */
  getCredentials () {
    return [
      { type: 'text', id: 1, name: 'API url', icon: 'link' },
      { type: 'text', id: 2, name: 'API version', icon: 'info' },
      { type: 'text', id: 3, name: 'Username', icon: 'person_outline' },
      { type: 'password', id: 4, name: 'Password', icon: 'vpn_key', show: false }
    ]
  }

  /*
   * Checks the connection of a given backend.
   * infoblox: If we can login the connection to the server and also the login is successfull.
   *
   * return: { success: <boolean>, status: '<STATUS_CODE_IF_ERROR>', error: '<ERROR_MESSAGE>' }
   */
  async checkConnection (credentials) {
    var c = this.mapCredentials(credentials)

    var ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })

    // Add custom timeout to the infoblox login
    return Promise.race([
      new Promise(async (resolve, reject) => {
        try {
          const result = await ipam.login(c.username, c.password)
          if (!result) resolve({ error: 'LOGIN_FAILED', message: 'Login failed' })
          else resolve(result)
        } catch (e) {
          resolve({ error: 'LOGIN_FAILED', message: e })
        }
      }),
      new Promise((resolve, reject) => {
        let wait = setTimeout(() => {
          clearTimeout(wait)
          resolve({ error: 'LOGIN_FAILED', message: 'Timeout' })
        }, 15000)
      })])
  }

  /*
   * Gets the client via IP, because only fixed pcs can be found via mac. But we also want so support leased pc's?
   *
   */
  async getClient (credentials, client) {
    var c = this.mapCredentials(credentials)

    var ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })

    return ipam.login(c.username, c.password).then(response => {
      if (response) {
        return ipam.getHost('10.21.9.228').then(response => {
          return JSON.parse(response)
        })
      } else {
        return { success: false, error: 'Login failed' }
      }
    })
  }

  async checkDomain (credentials) {
    const c = this.mapCredentials(credentials)

    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })
    const login = await ipam.login(c.username, c.password)
    if (!login) return { error: 'LOGIN_FAILED' }

    const domainList = await ipam.getDomain()
    return domainList
  }

  async checkIp (credentials, ipv4) {
    const c = this.mapCredentials(credentials)

    // Login
    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })
    const login = await ipam.login(c.username, c.password)
    if (!login) return { leased: false }

    // Get the host and check the leased state
    let host = JSON.parse(await ipam.getHost(ipv4))[0]
    if (!host) return { error: 'HOST_NOT_FOUND' }
    if (host.lease_state && host.lease_state === 'ACTIVE') {
      const dhcpNetwork = JSON.parse(await ipam.getNetworkFromIp(ipv4))

      // If leased return the next 20 free ips of the subnet. API method is limited to 20 ...
      // const nextIps = await ipam.getNext(dhcpNetwork[0]._ref, 20)

      // Instead of only getting the next 20 ips, GET THEM ALL
      let nextIps = JSON.parse(await ipam.getIpsFromSubnet(dhcpNetwork[0].network))
      nextIps = nextIps.filter(x => x.status === 'UNUSED').map(y => y.ip_address)

      return { leased: true, nextIps: nextIps }
    }

    let response = { leased: false, id: host._ref }
    if (host.names.length >= 1) response.name = host.names[0].split('.')[0]
    return response
  }

  async setIp (credentials, ipv4, domain, mac, name, setNextIp = false) {
    const c = this.mapCredentials(credentials)

    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })
    const login = await ipam.login(c.username, c.password)
    if (!login) return { error: 'LOGIN_FAILED' }

    // If setNextIp is true, use the next available ip from the subnet
    if (setNextIp) {
      try {
        const network = JSON.parse(await ipam.getNetworkFromIp(ipv4))
        ipv4 = 'func:nextavailableip:' + network[0].network
      } catch (e) {
        console.log(e)
        return { error: 'ERROR_INFOBLOX', msg: 'No network found. Missing permissions?' }
      }
    }
    // If the domain is not set, take the first available one.
    if (!domain) domain = (await ipam.getDomain())[0]

    // Set fixed ip if the name is not set (Automatic registration)
    let path = ''
    let data = {}

    if (!name) {
      path = 'fixedaddress?_return_fields%2B=ipv4addr&_return_as_object=1'
      data = {
        'ipv4addr': ipv4,
        'mac': mac
      }
    } else {
      path = 'record:host?_return_fields%2B=ipv4addrs&_return_as_object=1'
      data = {
        'name': name + '.' + domain,
        'ipv4addrs': [
          {
            'ipv4addr': ipv4,
            'mac': mac
          }
        ]
      }
    }
    const createHost = await ipam.create(path, data)

    // Return error if there is one
    if (createHost.Error) {
      return { error: 'ERROR_INFOBLOX', msg: createHost.text }
    } else {
      if (!name) return { ip: createHost.result.ipv4addr }
      else return { host: createHost.result.ipv4addrs[0].host, ip: createHost.result.ipv4addrs[0].ipv4addr, id: createHost.result._ref, domain: domain }
    }
  }

  isDhcp () {
    return true
  }

  async updateClient (credentials, client) {
    const c = this.mapCredentials(credentials)

    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })
    const login = await ipam.login(c.username, c.password)
    if (!login) return { error: 'LOGIN_FAILED' }

    let data = {}
    let result = []

    for (let index in client.networks) {
      const network = client.networks[index]
      if (network.hostname && network.domain) {
        data.name = network.hostname + '.' + network.domain
      }

      let ipv4addr = {}
      if (network.ip) ipv4addr.ipv4addr = network.ip
      if (network.mac) ipv4addr.mac = network.mac
      if (network.ip || network.mac) {
        data.ipv4addrs = [ipv4addr]
      }

      const oldId = client.id
      const newId = await ipam.update(oldId, data)
      // If the id changed (domain / name / ip) the mapping has to be updated
      if (oldId !== newId) result.push({ oldId: oldId, newId: newId })
    }
    return { mappings: result }
  }

  async deleteObjects (credentials, objectIds) {
    const c = this.mapCredentials(credentials)

    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })
    const login = await ipam.login(c.username, c.password)
    if (!login) return { error: 'LOGIN_FAILED' }

    for (let index in objectIds) {
      const ref = objectIds[index]
      await ipam.delete(ref)
    }
  }

  async getObjects (credentials) {
    const c = this.mapCredentials(credentials)

    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })
    const login = await ipam.login(c.username, c.password)
    if (!login) return { error: 'LOGIN_FAILED' }

    let result = []

    const domains = await ipam.getDomain()
    for (let index in domains) {
      const domain = domains[index]

      // Max result of infoblox is 1000, so we need to use the paging system
      const response = JSON.parse(await ipam.list('record:host?zone=' + domain + '&_paging=1&_return_as_object=1&_max_results=1000'))
      let records = response.result
      let nextPageId = response.next_page_id
      while (nextPageId) {
        let nextpage = JSON.parse(await ipam.list('record:host?zone=' + domain + '&_paging=1&_return_as_object=1&_max_results=1000&_page_id=' + nextPageId))
        records = records.concat(nextpage.result)
        nextPageId = nextpage.next_page_id
      }

      for (let i in records) {
        const record = records[i]
        result.push({ id: record._ref, mac: record.ipv4addrs[0].mac, ip: record.ipv4addrs[0].ipv4addr, domain: domain })
      }
    }
    return result
  }

  // ############################################################################
  // ####################### helper/optional functions  #########################
  // Helper function, to map the array of credential objects into a single js object.
  mapCredentials (credentials) {
    const c = JSON.parse(credentials)
    var mapped = {
      url: c.find(x => x.id === 1).value,
      version: c.find(x => x.id === 2).value,
      username: c.find(x => x.id === 3).value,
      password: c.find(x => x.id === 4).value
    }

    return mapped
  }

  // #### TEST FUNCTIONS ###
  async test (credentials) {
    const c = this.mapCredentials(credentials)

    const ipam = new Infoblox({
      ip: c.url,
      apiVersion: c.version
    })

    const login = await ipam.login(c.username, c.password)
    let result = {}
    if (!login) return { error: 'LOGIN_FAILED' }
    // Get network from ip
    result['getNetworkFromIp'] = JSON.parse(await ipam.getNetworkFromIp('10.21.9.78'))
    // Get host
    result['getHost.78'] = JSON.parse(await ipam.getHost('10.21.9.78'))
    // result["getHost.219"] = JSON.parse(await ipam.getHost('10.21.9.219'))
    result['getHost.12'] = JSON.parse(await ipam.getHost('10.21.9.12'))
    result['getHost.43'] = JSON.parse(await ipam.getHost('10.21.11.43'))

    // Get ips from subnet
    result['getIpsFromSubnet'] = JSON.parse(await ipam.getIpsFromSubnet('10.21.9.0/24'))
    result['getIpsFromSubnet'] = result['getIpsFromSubnet'].filter(x => x.status === 'UNUSED').map(y => y.ip_address)

    // List records
    // result["list"] = JSON.parse(await ipam.list('record:host'))

    // Get Domain
    result['getDomain'] = await ipam.getDomain()

    // Get next ip
    result['getNextIp.1'] = await ipam.getNext(result['getNetworkFromIp'][0]._ref, 1)

    // Get next 20 ips
    result['getNextIp.20'] = await ipam.getNext(result['getNetworkFromIp'][0]._ref, 20)

    // Get all unsued ips
    // result["getUnusedIps"] = await ipam.getNext(result["getNetworkFromIp"][0]._ref, 20)

    // Create Host
    // result['createHost'] = await ipam.create('record:host?_return_fields%2B=ipv4addrs&_return_as_object=1', {'name': 'wapiTest2.lp.privat', 'ipv4addrs': [{'ipv4addr': '10.21.9.218', 'mac': 'aa:bb:cc:11:22:21'}]})

    /*
    result['create'] = await ipam.create('record:host?_return_fields%2B=ipv4addrs&_return_as_object=1', {
      'name': 'test.lp.privat',
      'ipv4addrs': [
        {
          //'ipv4addr': 'func:nextavailableip:' +  result['getNetworkFromIp'][0].network,
          'ipv4addr': '10.21.9.206',
          'mac': 'aa:bb:cc:11:22:21'
        }
      ]
    })
    */

    // Delete stuff
    // result["getHost.173"] = JSON.parse(await ipam.getHost('10.21.9.173'))
    // result["deleteHost.173"] = JSON.parse(await ipam.delete(result["getHost.173"][0]._ref))
    // result["deleteHost.206"] = JSON.parse(await ipam.delete('record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnByaXZhdC5scC50ZXN0:test.lp.privat/default'))

    // result['allrecords'] = JSON.parse(await ipam.list('record:host?zone=lp.privat'))
    /*
    result['updateDomain'] = await ipam.update('record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmRlLnVuaS1mcmVpYnVyZy5hZHMucHVibGljLnplZnQ5MDQz', {
      'name': 'zeft9043.public.ads.uni-freiburg.de'
    })
    */

    return result
  }
}

module.exports = InfobloxBackend