Bluecat API

I sometimes have to write Perl scripts (yes I am that old!) that use the Bluecat API, but I find that the official documentation doesn’t provide enough info regarding the output of each API. Most APIs return a properties field that is actually a delimited string containing numerous values, but this changes depending upon which API is being used.

The properties are documented in Chapter 7 of the API guide, but there’s nothing like some real-world examples to see what actually comes back from the API. In addition to the properties field, there are also some set fields that are returned, such as “name”, “type” and “id”.

I sometimes use Data::Dumper to output the contents of each API, although more recently I’ve been using Postman, which is a great tool for poking them and seeing what comes back.

Authenticating

You have to authenticate and then use a token for each call. This token has a lifetime of only 5 minutes, so if you have a script that takes longer than this to run, you’ll need to make sure you re-authenticate. I have a couple of sub-routines that take care of this for me.

##############################################################################
sub testLogin {
	if (time() > ($session_start_time + $session_expire_timer)) {
		login();
	}
}

##############################################################################
sub login {
	$client->GET($bam_address."/Services/REST/v1/login?username=".$bam_api_user."&password=".$bam_api_password);
	my $strresponse = $client->responseContent();
	#Check authentication, exit if it errors
	if ($strresponse =~ m/Error/) {
		die "Authentication error, please try again","\n";
	}
	#Extract Authtoken from return string and use from now on (lasts 5 mins)
	my ($authtoken) = $strresponse =~ /-> ([^-]+) <-/;
	$client->addHeader('authorization', $authtoken);
	$session_start_time = time(); # Store timestamp so we can check expiry timer
	print STDERR "Authenticated at ".localtime()."\n";
}

I set a couple of global variables at the top of my script:

my $session_start_time;
my $session_expire_timer = 240; # Allows us to renew the REST token after x secs if getting too old (default token lifetime is only 5 mins)

Everytime I call an API, I precede it with testLogin(); – it checks the current time and re-authenticates if more than 4 minutes have passed.

Most of my scripts run interactively, so I use this to get the password:

$|++;
#Get Password
system "stty -echo";
print STDERR "Password: "; # Use STDERR so if user has redirected output to a log file, this still gets displayed on the terminal
chomp($pw = <STDIN>);
print STDERR "\n";
system "stty echo";
my $bam_api_password = uri_escape($pw); # "% encode" unsafe characters e.g, # ? & etc

These are the modules I use in every script:

use REST::Client;
use JSON::Parse 'parse_json';
use JSON::Create 'create_json';
use Data::Dumper;
use URI::Escape; # Required to encode the login password in case unsafe characters are used

 

Searching for networks (searchByObjectTypes)

There is an API called “getEntityByCIDR” but in order to use this you need to know the parentId of the parent object. If you don’t know the parentId, you’ll either have to walk the tree from the top down, or use one of the search APIs (I have heard a rumour you can just use “0” but I haven’t tried it yet). I chose the later and used “searchByObjectTypes” to get the network I was interested in.

Here is my REST call:

$client->GET("http://$bam_address/Services/REST/v1/searchByObjectTypes?keyword=$network_ip&types=IP4Network&start=0&count=10");

Output Example 1 (network has no name):

$VAR1 = [
 {
 'name' => undef,
 'type' => 'IP4Network',
 'id' => 684620,
 'properties' => 'CIDR=172.19.134.0/23|locationInherited=true|allowDuplicateHost=disable|inheritAllowDuplicateHost=true|pingBeforeAssign=disable|inheritPingBeforeAssign=true|gateway=172.19.134.1|inheritDefaultDomains=true|defaultView=69|inheritDefaultView=true|inheritDNSRestrictions=true|'
 }
 ];

Output Example 2 (network has a name):

$VAR1 = [
 {
 'name' => 'CEO\'s Office Data Switch 1 Vlan 511',
 'type' => 'IP4Network',
 'id' => 684628,
 'properties' => 'CIDR=172.19.137.0/26|locationInherited=true|allowDuplicateHost=disable|inheritAllowDuplicateHost=true|pingBeforeAssign=disable|inheritPingBeforeAssign=true|gateway=172.19.137.1|inheritDefaultDomains=true|defaultView=69|inheritDefaultView=true|inheritDNSRestrictions=true|'
 }
 ];

When processing the results, just do a check if “id” is defined, if it isn’t then the network wasn’t found.

Searching for DHCP ranges

You can use “searchByObjectTypes” to search for DHCP ranges provided you know one of the IP addresses in the range (e.g the start or end). If the IP address is outside the range then this won’t find it. It’s useful if you are just trying to see if a specific range is defined, if you don’t know what any of the IP addresses are then you’ll probably need to find a way to discover the range. I’ll cover that if/when I need to do it.

Here’s my call:

http://172.26.160.111/Services/REST/v1/searchByObjectTypes?keyword=68.2.33.1&types=DHCP4Range&start=0&count=1

Output example:

[
    {
        "id": 525752,
        "name": null,
        "type": "DHCP4Range",
        "properties": "start=68.2.33.1|end=68.2.33.254|"
    }
]

Getting IPAM objects (getEntities)

I needed to get a list of IPv4 IPAM addresses (not DNS host entries) associated to a particular network. If you know the network entityId (which you can get using the “getEntityByCIDR” API), you can use that to extract all the addresses allocated on that network via the “getEntities” API call. There are other types of objects you can retrieve, but for now I am just interested in object type “IP4Address”. Use the network entityId as the parentId in the API call as follows:

$client->GET("http://$bam_address/Services/REST/v1/getEntities?parentId=$network_id&type=IP4Address&start=0&count=9999");

Output Example:

$VAR1 = [
 {
 'name' => 'allstar.vpn-sdc-snat, Co. to Allstar Hide NAT',
 'type' => 'IP4Address',
 'id' => '11107850',
 'properties' => 'comments=77482 - 2017032802456 Requested by James T Kirk. Entered by J.Bloggs|locationInherited=true|address=10.244.18.35|state=STATIC|'
 },
 {
 'name' => 'Co. to Connect Hide NAT',
 'type' => 'IP4Address',
 'id' => '14603704',
 'properties' => 'locationInherited=true|address=10.244.18.15|state=STATIC|'
 }
 ];

Notice in the output that the first entry has a comment in the properties field whereas the second one doesn’t, so don’t always expect the sub-fields inside “properties” to be consistent. Also the “state” property can have different values for the default gateway and various types of DHCP allocations and reserved addresses, I’ll try and document these as I find them. Here’s an example of a network that just has a gateway defined:

$VAR1 = [
 {
 'name' => undef,
 'type' => 'IP4Address',
 'id' => '14592556',
 'properties' => 'locationInherited=true|address=10.42.32.1|state=GATEWAY|'
 }
 ];

getEntities can be used to retrieve all sorts of things, provided you know the entityId. Here is the output from a REST call for DHCP ranges on a network, note the properties helpfully tell you the start and end so you don’t have to do another call to get the actual entity. Here’s the REST call:

$client->GET("http://$bam_address/Services/REST/v1/getEntities?parentId=$network_id&type=DHCP4Range&start=0&count=9999");

Output example:

$VAR1 = [
 {
 'name' => undef,
 'type' => 'DHCP4Range',
 'id' => '15589868',
 'properties' => 'start=10.244.18.100|end=10.244.18.110|'
 }
 ];

Here is a list of all the entity types that can be retrieved:

Entity
Configuration
View
Zone
InternalRootZone
ZoneTemplate
EnumZone
EnumNumber
HostRecord
AliasRecord
MXRecord
TXTRecord
SRVRecord
GenericRecord
HINFORecord
NAPTRRecord
RecordWithLink
ExternalHostRecord
StartOfAuthority
IP4Block
IP4Network
IP6Block
IP6Network
IP6Address
IP4NetworkTemplate
DHCP4Range
DHCP6Range
IP4Address
MACPool
DenyMACPool
MACAddress
TagGroup
Tag
User
UserGroup
Server
NetworkServerInterface
PublishedServerInterface
NetworkInterface
VirtualInterface
LDAP
Kerberos
KerberosRealm
Radius
TFTPGroup
TFTPFolder
TFTPFile
TFTPDeploymentRole
DNSDeploymentRole
DHCPDeploymentRole
DNSOption
DHCPV4ClientOption
DHCPServiceOption
DHCPRawOption
DNSRawOption
DHCPV6ClientOption
DHCPV6ServiceOption
DHCPV6RawOption
VendorProfile
VendorOptionDef
VendorClientOption
CustomOptionDef
DHCPMatchClass
DHCPSubClass
Device
DeviceType
DeviceSubtype
DeploymentScheduler
IP4ReconciliationPolicy
DNSSECSigningPolicy
IP4IPGroup
ResponsePolicy
TSIGKey
RPZone
Location
InterfaceID

Getting linked DNS entries (getLinkedEntities)

If you have retrieved an IPAM object, one of the things you can do is grab any DNS records linked to that object. Bluecat stores linked DNS objects as object type “HostRecord”, note there are other types of DNS objects, such as “GenericRecord”, but these are not linked to IPAM objects – I’ll cover those another time.

For now, I have the IPAM object entityId stored in a variable and use that to retrieve any linked DNS records (note there can be more than one). Here is my REST call:

$client->GET("http://$bam_address/Services/REST/v1/getLinkedEntities?entityId=$host_id&type=HostRecord&start=0&count=999");

Output Example:

$VAR1 = [
 {
 'name' => 'ls9147-128019050064',
 'type' => 'HostRecord',
 'id' => 768717,
 'properties' => 'ttl=86400|absoluteName=ls9147-128019050064.uk.corp.org|addresses=128.20.50.64|reverseRecord=true|'
 },
 {
 'name' => 'ls9147-128019050064',
 'type' => 'HostRecord',
 'id' => 863894,
 'properties' => 'ttl=86400|absoluteName=ls9147-128019050064.corp|addresses=128.20.50.64|reverseRecord=true|'
 }
 ];

Notice that the name field is not fully qualified. For the FQDN you’ll need to extract that from the properties string.

Getting deployment roles (getDeploymentRoles)

If you want to get the deployment roles associated with a network, you can use the entityId for the network in question and call the “getDeploymentRoles” API, here is my REST code:

$client->GET("http://$bam_address/Services/REST/v1/getDeploymentRoles?entityId=$network_id");

Here’s the output, you’ll notice there are some additional fields that you’ll need to parse if you want to find out the name of the server, I’ll explain it below:

$VAR1 = [
 {
 'serverInterfaceId' => 20,
 'entityId' => 3829,
 'type' => 'SLAVE',
 'id' => 1722,
 'service' => 'DNS',
 'properties' => 'readOnly=false|view=69|inherited=true|'
 },
 {
 'serverInterfaceId' => 32,
 'entityId' => 3829,
 'type' => 'SLAVE',
 'id' => 1723,
 'service' => 'DNS',
 'properties' => 'readOnly=false|view=69|inherited=true|'
 },
 {
 'serverInterfaceId' => 1880,
 'entityId' => 3829,
 'type' => 'MASTER_HIDDEN',
 'id' => 2034,
 'service' => 'DNS',
 'properties' => 'readOnly=false|view=69|nsRecordTTL=0|inherited=true|'
 },
 {
 'serverInterfaceId' => 1913,
 'entityId' => 3829,
 'type' => 'SLAVE_STEALTH',
 'id' => 2038,
 'service' => 'DNS',
 'properties' => 'readOnly=false|view=69|inherited=true|'
 },
 {
 'serverInterfaceId' => 14,
 'entityId' => 3829,
 'type' => 'MASTER',
 'id' => 576,
 'service' => 'DHCP',
 'properties' => 'readOnly=false|secondaryServerInterfaceId=26|inherited=true|'
 }
 ];

You can see we get back an array of all the roles on this network, and they are a mixture of DNS and DHCP roles. Also you’ll see we have three different id’s returned as follows:

  • entityId
    • This is the Id of the network we are querying, notice they are all the same in this example because we have multiple roles for this network
  • id
    • This is the Id of the individual role, each one has a unique Id
  • serverInterfaceId
    • This is the Id of the server associated with the role, if you use this with the getEntityById API you should be able to extract the name of the server

DHCP Service Deployment Options

These are used to change the behaviour of DHCP (note, these are not client DHCP options), and a lot of them can be set on specific subnets. For this exercise I wish to disable “update-optimization” – first I want to see if the policy is already set, and if not then set it. So I first have to check each subnet using the getDHCPServiceDeploymentOption API.

$client->GET("http://$bam_address/Services/REST/v1/getDHCPServiceDeploymentOption?entityId=$entityId&name=update-optimization&serverId=0");

If the option is set, the output looks like this (I am using Postman now to test APIs):

{
 "id": 16567736,
 "type": "DHCPService",
 "name": "update-optimization",
 "value": "false",
 "properties": "inherited=false|"
}

If the option is not set, it will look like this:

{
 "id": 0,
 "type": null,
 "name": null,
 "value": null,
 "properties": null
}