class Proxy::DHCP::BlueCat::BlueCatAPI

This Class handles all commuincation to the bluecat address manager

Attributes

token[RW]

contains the bluecat api token

config_id[R]

id of your Bluecat configuration

config_name[R]

Name of your Bluecat configuration

host[R]

fqdn or ip of your bluecat address manager

parent_block[R]

id of the parent_block that holds the subnets that you want to use

password[R]

credentials of your api user

scheme[R]

connection mode to the address manager. http or https

server_id[R]

id of the server that holds your dhcp

username[R]

credentials of your api user

verify[R]

validate ssl connection. true or false

view_name[R]

name of your dns view

Public Class Methods

new(scheme, verify, host, parent_block, view_name, config_name, config_id, server_id, username, password) click to toggle source
# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 48
def initialize(scheme, verify, host, parent_block, view_name, config_name, config_id, server_id, username, password)
  @scheme = scheme
  @verify = verify
  @host = host
  @parent_block = parent_block
  @view_name = view_name
  @config_name = config_name
  @config_id = config_id
  @server_id = server_id
  @username = username
  @password = password
end

Public Instance Methods

add_host(options) click to toggle source

public wrapper function to add the dhcp reservation and dns records

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 221
def add_host(options)
  # add the ip and hostname and mac as static
  rest_post("addDeviceInstance",
            "configName=#{@config_name} \
            &ipAddressMode=PASS_VALUE \
            &ipEntity=#{options["ip"]} \
            &viewName=#{@view_name} \
            &zoneName=#{options["hostname"].split(".", 2).last} \
            &deviceName=#{options["hostname"]} \
            &recordName=#{options["hostname"]} \
            &macAddressMode=PASS_VALUE \
            &macEntity=#{options["mac"]} \
            &options=AllowDuplicateHosts=true%7C")

  address_id = get_addressid_by_ip(options["ip"])

  # update the state of the ip from static to dhcp reserved
  rest_put("changeStateIP4Address",
           "addressId=#{address_id} \
            &targetState=MAKE_DHCP_RESERVED \
            &macAddress=#{options["mac"]}")

  unless options["nextServer"].nil? || options["filename"].nil?
    rest_post("addDHCPClientDeploymentOption",
              "entityId=#{address_id}&name=tftp-server-name&value=#{options["nextServer"]}")
    rest_post("addDHCPClientDeploymentOption",
              "entityId=#{address_id}&name=boot-file-name&value=#{options["filename"]}")
  end

  # deploy the config
  rest_post("deployServerConfig", "serverId=#{@server_id}&properties=services=DHCP")
  # lets wait a little bit for the complete dhcp deploy
  sleep 3
  rest_post("deployServerConfig", "serverId=#{@server_id}&properties=services=DNS")
  nil
end
find_mysubnet(subnet_address) click to toggle source

public fetches a subnet by its network address

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 310
def find_mysubnet(subnet_address)
  net = IPAddress.parse(get_network_by_ip(subnet_address))
  ::Proxy::DHCP::Subnet.new(net.address, net.netmask)
end
get_addressid_by_ip(ip) click to toggle source

helper function to get the object id of a ip by an ip address

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 176
def get_addressid_by_ip(ip)
  json = rest_get("getIP4Address", "containerId=#{@config_id}&address=#{ip}")
  result = JSON.parse(json)
  return nil if result.empty?

  result["id"].to_s
end
get_network_by_ip(ip) click to toggle source

helper function to get the whole subnet informarions by an ip address

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 196
def get_network_by_ip(ip)
  logger.debug("BAM get_network_by_ip #{ip}")
  querystring = "containerId=#{@config_id}&type=IP4Network&address=#{ip}"
  json = rest_get("getIPRangedByIP", querystring)
  result = JSON.parse(json)
  properties = parse_properties(result["properties"])
  properties["CIDR"].to_s
end
get_networkid_by_ip(ip) click to toggle source

helper function to get the object id of a subnet by an ip address

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 185
def get_networkid_by_ip(ip)
  logger.debug("BAM get_networkid_by_ip #{ip}")
  querystring = "containerId=#{@config_id}&type=IP4Network&address=#{ip}"
  json = rest_get("getIPRangedByIP", querystring)
  result = JSON.parse(json)
  return nil if result.empty?

  result["id"].to_s
end
host_by_mac_and_subnet(subnet_of_host, mac) click to toggle source

public fetches all dhcp reservations by a mac

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 394
def host_by_mac_and_subnet(subnet_of_host, mac)
  logger.debug("subnet_of_host #{subnet_of_host}")

  net = get_network_by_ip(subnet_of_host)

  logger.debug("net #{net}")

  subnet = IPAddress.parse(net)

  logger.debug("subnet #{subnet} ")

  json = rest_get("getMACAddress", "configurationId=#{@config_id}&macAddress=#{mac}")
  result = JSON.parse(json)
  macid = result["id"].to_s
  return if macid == "0"

  json2 = rest_get("getLinkedEntities", "entityId=#{macid}&type=IP4Address&start=0&count=9999")
  result2 = JSON.parse(json2)
  return nil if result2.empty?

  logger.debug("das #{result2}")

  result2.each do |record|
    properties = parse_properties(record["properties"])
    ip = IPAddress(properties["address"])

    logger.debug("hier #{subnet.to_string} #{ip.to_string}")

    next unless subnet.include?(ip)

    logger.debug("hier #{record}")

    host = hosts_by_ip(properties["address"])
    return nil if host.nil?

    logger.debug("da #{host}")
    return host[0]
  end

  nil
end
hosts(network_address) click to toggle source

public fetches all dhcp reservations in a subnet

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 317
def hosts(network_address)
  netid = get_networkid_by_ip(network_address)
  net = IPAddress.parse(get_network_by_ip(network_address))
  subnet = ::Proxy::DHCP::Subnet.new(net.address, net.netmask)

  json = rest_get("getNetworkLinkedProperties", "networkId=#{netid}")
  results = JSON.parse(json)

  hosts = results.map do |result|
    properties = parse_properties(result["properties"])

    ## Static Addresses and Gateway are not needed here
    ## But lets keep the logic to identify them
    # if properties.length() >= 4
    #  if properties["state"] == "Gateway" or properties["state"] == "Static"
    #    address = properties[0].split("=").last()
    #    macAddress = "00:00:00:00:00:00"
    #    hosttag = properties[3].split("=").last().split(":")
    #    name = hosttag[1] + "." + hosttag[3]
    #    opts = {:hostname => name}
    #    ::Proxy::DHCP::Reservation.new(name, address, macAddress, subnet, opts)
    #  end
    # end
    next unless properties.length >= 5
    next unless properties["state"] == "DHCP Reserved"

    hosttag = properties["host"].split(":")
    name = "#{hosttag[1]}.#{hosttag[3]}"
    opts = { hostname: name }
    ::Proxy::DHCP::Reservation.new(name, properties["address"], properties["macAddress"].tr("-", ":"), subnet, opts)
  end
  hosts.compact
end
hosts_by_ip(ip) click to toggle source

public fetches a host by its ip

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 353
def hosts_by_ip(ip)
  hosts = []
  net = IPAddress.parse(get_network_by_ip(ip))
  subnet = ::Proxy::DHCP::Subnet.new(net.address, net.netmask)
  ipid = get_addressid_by_ip(ip)
  return nil if ipid.to_s == "0"

  json = rest_get("getLinkedEntities", "entityId=#{ipid}&type=HostRecord&start=0&count=2")
  results = JSON.parse(json)

  if results.empty? || (results == "Link request is not supported")
    # no host record on ip, fetch mac only
    json2 = rest_get("getIP4Address", "containerId=#{@config_id}&address=#{ip}")
    result2 = JSON.parse(json2)
    properties2 = parse_properties(result2["properties"])
    unless properties2["macAddress"].nil?
      mac_address = properties2["macAddress"].tr("-", ":")
      hosts.push(Proxy::DHCP::Reservation.new("", ip, mac_address, subnet, {}))
    end
  else
    # host record on ip, return more infos
    results.each do |result|
      properties = parse_properties(result["properties"])
      opts = { hostname: properties["absoluteName"] }

      next unless properties["reverseRecord"].to_s == "true".to_s

      json2 = rest_get("getEntityById", "id=#{ipid}")
      result2 = JSON.parse(json2)
      properties2 = parse_properties(result2["properties"])
      unless properties2["macAddress"].nil?
        mac_address = properties2["macAddress"].tr("-", ":")
        hosts.push(Proxy::DHCP::Reservation.new(properties["absoluteName"], ip, mac_address, subnet, opts))
      end
    end
  end
  hosts.compact
end
next_ip(netadress, start_ip, _end_ip) click to toggle source

public fetches the next free address in a subnet +end_ip not implemented+

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 276
def next_ip(netadress, start_ip, _end_ip)
  networkid = get_networkid_by_ip(netadress)

  start_ip = IPAddress.parse(netadress).first if start_ip.to_s.empty?

  properties = "offset=#{start_ip}%7CexcludeDHCPRange=false"
  result = rest_get("getNextIP4Address", "parentId=#{networkid}&properties=#{properties}")
  return if result.empty?

  result.tr('"', "")
end
parse_properties(properties) click to toggle source

helper function to parse the properties scheme of bluecat into a hash

properies: a string that contains properties for the object in attribute=value format, with each separated by a | (pipe) character. For example, a host record object may have a properties field such as ttl=123|comments=my comment|.

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 210
def parse_properties(properties)
  properties = properties.split("|")
  h = {}
  properties.each do |property|
    h[property.split("=").first.to_s] = property.split("=").last.to_s
  end
  h
end
remove_host(ip) click to toggle source

public wrapper function to remove a ip record and depending dns records

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 260
def remove_host(ip)
  ipid = get_addressid_by_ip(ip)
  json = rest_get("getLinkedEntities", "entityId=#{ipid}&type=HostRecord&start=0&count=2")
  results = JSON.parse(json)

  results.map do |result|
    rest_delete("delete", "objectId=#{result["id"]}")
  end
  rest_delete("delete", "objectId=#{ipid}")

  rest_post("deployServerConfig", "serverId=#{@server_id}&properties=services=DHCP,DNS")
end
rest_delete(endpoint, querystring) click to toggle source

wrapper function to for rest delete requests

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 155
def rest_delete(endpoint, querystring)
  logger.debug("BAM DELETE #{endpoint}?#{querystring}")
  response = HTTParty.delete(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                             headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                        "Content-Type" => "application/json" },
                             verify: @verify)

  # Session propably expired, refresh it and do the request again
  if response.code == 401
    rest_login
    response = HTTParty.delete(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                               headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                          "Content-Type" => "application/json" },
                               verify: @verify)
  end
  return response.body if response.code == 200

  logger.error("BAM DELETE Failed. HTTP#{response.code} #{response.body}")
end
rest_get(endpoint, querystring) click to toggle source

wrapper function to for rest get requests

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 91
def rest_get(endpoint, querystring)
  rest_login if self.class.token.nil?

  logger.debug("BAM GET #{endpoint}?#{querystring}")

  response = HTTParty.get(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                          headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                     "Content-Type" => "application/json" },
                          verify: @verify)
  # Session propably expired, refresh it and do the request again
  if response.code == 401
    rest_login
    response = HTTParty.get(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                            headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                       "Content-Type" => "application/json" },
                            verify: @verify)
  end

  return response.body if response.code == 200

  logger.error("BAM GET Failed. HTTP#{response.code} #{response.body}")
end
rest_login() click to toggle source

login to bam, parse the session token

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 62
def rest_login
  logger.debug("BAM Login #{@scheme} #{@host} ")
  response = HTTParty.get(format("%s://%s/Services/REST/v1/login?username=%s&password=%s",
                                 @scheme,
                                 @host,
                                 @username,
                                 @password),
                          headers: { "Content-Type" => "text/plain" },
                          verify => @verify)
  logger.error("BAM Login Failed. HTTP#{response.code} #{response.body}") if response.code != 200
  body = response.body.to_s
  token = body.match(/BAMAuthToken:\s+(\S+)/).captures

  logger.debug("BAM Login Body #{response.body}")
  logger.debug("BAM Login Token #{token[0]}")
  self.class.token = token[0].to_s
end
rest_logout() click to toggle source

logout from bam

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 81
def rest_logout
  logger.debug("BAM Logout ")
  response = HTTParty.get(format("%s://%s/Services/REST/v1/logout", @scheme, @host),
                          headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                     "Content-Type" => "application/json" },
                          verify: @verify)
  logger.error("BAM Logout Failed. HTTP#{response.code} #{response.body}") if response.code != 200
end
rest_post(endpoint, querystring) click to toggle source

wrapper function to for rest post requests

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 115
def rest_post(endpoint, querystring)
  logger.debug("BAM POST #{endpoint}?#{querystring}")
  response = HTTParty.post(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                           headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                      "Content-Type" => "application/json" },
                           verify: @verify)
  # Session propably expired, refresh it and do the request again
  if response.code == 401
    rest_login
    response = HTTParty.post(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                             headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                        "Content-Type" => "application/json" },
                             verify: @verify)
  end
  return response.body if response.code == 200

  logger.error("BAM POST Failed. HTTP#{response.code} #{response.body}")
end
rest_put(endpoint, querystring) click to toggle source

wrapper function to for rest put requests

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 135
def rest_put(endpoint, querystring)
  logger.debug("BAM PUT #{endpoint}?#{querystring}")
  response = HTTParty.put(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                          headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                     "Content-Type" => "application/json" },
                          verify: @verify)
  # Session propably expired, refresh it and do the request again
  if response.code == 401
    rest_login
    response = HTTParty.put(format("%s://%s/Services/REST/v1/%s?%s", @scheme, @host, endpoint, querystring),
                            headers: { "Authorization" => "BAMAuthToken: #{self.class.token}",
                                       "Content-Type" => "application/json" },
                            verify: @verify)
  end
  return response.body if response.code == 200

  logger.error("BAM PUT Failed. HTTP#{response.code} #{response.body}")
end
subnets() click to toggle source

public fetches all subnets under the parent_block

# File lib/smart_proxy_dhcp_bluecat/bluecat_api.rb, line 290
def subnets
  json = rest_get("getEntities", "parentId=#{@parent_block}&type=IP4Network&start=0&count=10000")
  results = JSON.parse(json)
  subnets = results.map do |result|
    properties = parse_properties(result["properties"])
    net = IPAddress.parse(properties["CIDR"])
    opts = { routers: [properties["gateway"]] }

    if properties["gateway"].nil?
      logger.error("subnet issue: #{properties["CIDR"]} skipped, due missing gateway in bluecat")
      next
    end

    ::Proxy::DHCP::Subnet.new(net.address, net.netmask, opts)
  end
  subnets.compact
end