A CMDB for IT infrastructures

Abstract

Can we use a CMDB for the whole IT management? In this post we'll see what a CMDB should be, and we'll see how to import existing network devices into a CMDB using NAPALM. Finally we'll see how to operate on network devices from a CMDB and how pushing configurations. This is a compendium to my speech in Milano for AutomateIT² event.

Suppose an IT manager is asking for the total number of active VMs to his staff:

  • Alice: consults an Excel file and reports 1984 running systems. (What should be)
  • Bob: consults AWS, Azure, GCE and internal vCenter and reports 2010 running systems. (What is)
  • Carol: remembers that 5 internal VMs are running 33 Docker containers, and asks if they should be included or not. (Exception)

What happened?

The supposed running VMs reported by an Excel is not compliant with the real world. The real world is reported by the vCenter, AWS, Azure and Google Compute Engine. We can call them Source Of Truth.

Source of Truth (SOT) The source of truth is a trusted data source that gives a complete picture of the data object as a whole. (What is)

The Excel file reports what should be, and we can call it Source Of Record, and because it should be single, we can call it Single Source Of Record:

System of Record (SOR) A system of record is the authoritative data source for a given data element or piece of information. (What should be)

I know that the above definitions are different from what others are using, but I guess you agree that vCenter is a Source Of Truth, not an Excel file. Let me give you an example:

Suppose you have a fantastic system that synchronize the vCenter with an Excel file. Are you sure that a manually created VM will be added to the Excel file? Are you sure that a powered off, orphaned VM will be added to the Excel file? Are you sure Excel is reporting the real number of the VM in any moment?

That’s why I’m convinced that the definition of “Single Source Of Truth” is wrong, and that’s why I changed into “Single System Of Record”.

Suppose now we want to store all IT data into a single place: obviously Excel is not the right place. We should use a different and more flexible data structure, depending on what our needs are: we could use CMDBuild, ITOP, or simply Git/CVS, depending on requisites.

In this example we suppose we want to store every single IT item (device, service, database schema…) in a single place. In this case we need a CMDB:

A configuration management database (CMDB) is a repository that acts as a data warehouse for information technology (IT) installations. It holds data relating to a collection of IT assets (commonly referred to as configuration items (CI)), as well as to descriptive relationships between such assets.

Let’s now focus on CMDBuild as a CMDB.

CMDB as a single source of record

CMDBuild installation on Ubuntu 16.04

Before starting, download the following three packages:

  1. latest JRE
  2. latest Tomcat
  3. latest CMDBuild

Be sure your locale is set to en_US.UTF-8, if not, reconfigure:

# dpkg-reconfigure locales

Install PostgreSQL and set the admin password:

# dpkg-reconfigure locales
# apt-get -y install postgresql
# sudo -u postgres psql
\password postgres
\q

Unpack Java and set it as a default for the system:

# tar -xzf jre-8u141-linux-x64.tar.gz -C /opt
# update-alternatives --install /usr/bin/java java /opt/jre1.8.0_141/bin/java 1
# echo 1 | update-alternatives --config java
# java -version
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)

Unpack Tomcat, add CMDBuild app to it and start the container:

# tar -xzf apache-tomcat-8.5.16.tar.gz -C /opt
# unzip cmdbuild-2.4.3.zip
# cp cmdbuild-2.4.3/cmdbuild-2.4.3.war /opt/apache-tomcat-8.5.16/webapps
# cp cmdbuild-2.4.3/extras/tomcat-libs/6.0\ or\ higher/postgresql-9.4.1207.jar /opt/apache-tomcat-8.5.16/lib
# /opt/apache-tomcat-8.5.16/bin/startup.sh
Using CATALINA_BASE:   /opt/apache-tomcat-8.5.16
Using CATALINA_HOME:   /opt/apache-tomcat-8.5.16
Using CATALINA_TMPDIR: /opt/apache-tomcat-8.5.16/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /opt/apache-tomcat-8.5.16/bin/bootstrap.jar:/opt/apache-tomcat-8.5.16/bin/tomcat-juli.jar
Tomcat started.

Now CMDBuild is available at http://a.b.c.d:8080/cmdbuild-2.4.3/ Connect to CMDBuild webapp using http://a.b.c.d:8080/cmdbuild-2.4.3/ and do the first configuration:

  • Type: Empty
  • Name: cmdbuild
  • Host: localhost
  • Port: 5432
  • Super user: postgres
  • Password:
  • User type: Super user

Be sure the database cmdbuild does not exist. If it does (or if you want to start from the beginning), delete it with:

# psql -h localhost -U postgres -W -c "drop database cmdbuild"

If you encounter errors, check also /var/log/postgresql for logs.

Minimal configuration of CMDBuild

The configuration of CMDBuild is out of scope, but a brief introduction is provided because of the scope of this post.

CMDBuild presents two views:

  • Data management module: default after the login process, allows to manage data.
  • Administration module: allows to manage structures.

The first step requires to build the structures for our asset data. Open the administration module, go to classes and the following classes:

  • Name: asset
    • Description: Asset
    • Type: Standard
    • Inherits from: Class
    • Superclass: yes
  • Name: infrastructure
    • Description: Infrastructure
    • Type: Standard
    • Inherits from: Asset
    • Superclass: yes
  • Name: networkdevices
    • Description: Network Devices
    • Type: Standard
    • Inherits from: Infrastructure
    • Superclass: no
  • Name: networks
    • Description: Networks
    • Type: Standard
    • Inherits from: Asset
    • Superclass: yes
  • Name: ipv4addresses
    • Description: IPv4 Addresses
    • Type: Standard
    • Inherits from: Networks
    • Superclass: no
  • Name: ipv4networks
    • Description: IPv4 Networks
    • Type: Standard
    • Inherits from: Networks
    • Superclass: no
  • Name: interfaces
    • Description: Interfaces
    • Type: Standard
    • Inherits from: Networks
    • Superclass: no

The second step requires to build relationship for the above classes. The idea is the following:

  • a network device (a router for example) contains many interfaces;
  • each interface can have VLANs, one VRF, IPv4 addresses and one OSPF area;
  • each IPv4 address is part of an IPv4 network.

Go to domains and add the following domains:

  • Name: networkdevice2interfaces
    • Description: Network Device to Interfaces
    • Origin: Network Devices
    • Destination: Interfaces
    • Direct description: Interface
    • Inverse description: Device
    • Cardinality: 1:N
  • Name: interface2ipv4addresses
    • Description: Interface to IPv4 Addresses
    • Origin: Interfaces
    • Destination: IPv4 Addresses
    • Direct description: IPV4 Address
    • Inverse description: Interface
    • Cardinality: 1:N
  • Name: ipv4addresstoipv4network
    • Description: IPv4 Address to IPv4 Network
    • Origin: IPv4 Addresses
    • Destination: IPv4 Networks
    • Direct description: IPv4 Network
    • Inverse description: IPV4 Address
    • Cardinality: N:1
  • Name: interface2interface
    • Description: Interface to Interface
    • Origin: Interfaces
    • Destination: Interfaces
    • Direct description: Connected to
    • Inverse description: Connected to
    • Cardinality: 1:1

Finally we need to configure attributes for each class:

  • Network Devices:
    • Code: Serial (Unique, Mandatory, STRING(100))
    • Description: Hostname (Unique, Mandatory, STRING(250))
    • Vendor: Vendor (Unique, Mandatory, STRING(256))
    • Version: Version (Mandatory, STRING(256))
  • Interfaces:
    • Code: MAC Address (Mandatory, STRING(100))
    • Description: Interface Name (Unique, Mandatory, STRING(250))
  • IPv4 Addresses:
    • Description: IPV4Address (Unique, Mandatory, STRING(100))
    • FQDN: FQDN (STRING(250))
  • IPv4 Networks:
    • Description: IPv4 Network (Unique, Mandatory, STRING(100))

Now a basic structure of the CMDB is ready.

CMDBuild basic structure

Discovering a network device

Nowadays there are many to discover a network device. The classic one is based on the, widely supported, SNMP protocol. Suppone a Cisco router, and configure it with a SNMPv3 user:

snmp-server view ISO iso included
snmp-server group MONITORING v3 auth read ISO
snmp-server user snmpmonitor MONITORING v3 auth sha snmppassword

Now we can get some useful information via SNMPv3:

# apt-get -y install snmp snmp-mibs-downloader
# snmpwalk -mALL -v3 -l authNoPriv -a SHA -u snmpmonitor -A snmppassword -On 192.168.6.201 sysName
# snmpwalk -mALL -v3 -l authNoPriv -a SHA -u snmpmonitor -A snmppassword -On 192.168.6.201 ifName
# snmpwalk -mALL -v3 -l authNoPriv -a SHA -u snmpmonitor -A snmppassword -On 192.168.6.201 ipAddrTable

Finding all information via SNMP could be hard, because data structures (MIBs) are not so easy to find and understand. Another way to get information from network devices is NAPALM:

# apt-get -y install python3-pip
# pip3 install napalm

Before using NAPALM with a classic Cisco IOS device (via SSH), the device must be configured:

hostname R1
ip domain name infra.example.com
username admin privilege 15 password cisco
crypto key generate rsa
line vty 0 4
 login local
 transport input ssh

Remember you need at least a 768 length key, and, of course, a reachable IP address.

Now devices can be discovered using the first script:

$ ./1-scan.py 192.168.6.201
$ ./1-scan.py 192.168.6.202

After the execution, CMDBuild is populated with some information gathered from the discovered devices. The script:

  • collects hostname, interfaces, MAC addresses and IP addresses;
  • add all collected data to CMDBuild and create reliationships.

Router imported into CMDBuild

Managing the infrastructure from a CMDB

Every network change should be initiated from a CMDB. Let’s see for example how to configure a new interfaces. In Asset -> Networks -> IPv4 Networks add a card: 10.1.2.0/24. Now go to Asset -> Networks -> IPv4 Addresses and add two IP addresses: 10.1.2.1 and 10.1.2.2. Be sure also you link each IP address to the parent network (10.1.2.0/24) creating the proper relationship. Now go to Asset -> Networks -> Interfaces and create two relationship, each between an interface and an IP address.

Router view from CMDBuild

The configuration is complete, now push it to the devices:

$ ./2-push.py R1
$ ./2-push.py R2

Now R1 can ping R2:

$ ./3-test.py 192.168.6.201 10.1.2.2 | python -m json.tool
{
    "success": {
        "packet_loss": 0,
        "probes_sent": 5,
        "results": [
            {
                "ip_address": "10.1.2.2",
                "rtt": 0.0
            },
            {
                "ip_address": "10.1.2.2",
                "rtt": 0.0
            },
            {
                "ip_address": "10.1.2.2",
                "rtt": 0.0
            },
            {
                "ip_address": "10.1.2.2",
                "rtt": 0.0
            },
            {
                "ip_address": "10.1.2.2",
                "rtt": 0.0
            }
        ],
        "rtt_avg": 1.0,
        "rtt_max": 2.0,
        "rtt_min": 1.0,
        "rtt_stddev": 0.0
    }
}

CCMDBuild useful API

I found the offical documentation by Tecnoteca not so exaustive. Here the APIs I used:

Authentication and sessions

The first request must authenticate against CMDBuild and get a session token:

$ curl -s -X POST -H "Content-Type: application/json" --data-binary '{"username": "admin", "password": "admin"}' "http://localhost:8080/cmdbuild-2.4.3/services/rest/v1/sessions" | python3 -m json.tool
{
    "data": {
        "username": "admin",
        "role": "SuperUser",
        "availableRoles": [
            "SuperUser"
        ],
        "_id": "hb90u9d3l4jqunf8ni6s7jisli"
    }
}

The session token is hb90u9d3l4jqunf8ni6s7jisli. All queries must include the token in the HTTP headers: CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli.

List the classes

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/classes" | python3 -m json.tool
{
    "data": [
        {
            "name": "Class",
            "description": "Class",
            "parent": null,
            "prototype": true,
            "_id": "Class"
        },
        {
            "name": "asset",
            "description": "Asset",
            "parent": "Class",
            "prototype": true,
            "_id": "asset"
        },
        {
            "name": "infrastructure",
            "description": "Infrastructure",
            "parent": "asset",
            "prototype": true,
            "_id": "infrastructure"
        },
        {
            "name": "interfaces",
            "description": "Interfaces",
            "parent": "networks",
            "prototype": false,
            "_id": "interfaces"
        },
        {
            "name": "ipv4addresses",
            "description": "IPv4 Addresses",
            "parent": "networks",
            "prototype": false,
            "_id": "ipv4addresses"
        },
        {
            "name": "ipv4networks",
            "description": "IPv4 Networks",
            "parent": "networks",
            "prototype": false,
            "_id": "ipv4networks"
        },
        {
            "name": "networkdevices",
            "description": "Network Devices",
            "parent": "infrastructure",
            "prototype": false,
            "_id": "networkdevices"
        },
        {
            "name": "networks",
            "description": "Networks",
            "parent": "asset",
            "prototype": true,
            "_id": "networks"
        }
    ],
    "meta": {
        "total": 8,
        "positions": {},
        "references": {}
    }
}

List the all entries (cards) for a class

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/classes/networkdevices/cards" | python3 -m json.tool
{
    "data": [
        {
            "Description": "R1",
            "Version": "Linux Software (I86BI_LINUX-ADVENTERPRISEK9-M), Version 15.5(2)T, DEVELOPMENT TEST SOFTWARE",
            "_type": "networkdevices",
            "Vendor": "Cisco",
            "_id": 275,
            "Code": "67108880",
            "Notes": null
        },
        {
            "Description": "R2",
            "Version": "Linux Software (I86BI_LINUX-ADVENTERPRISEK9-M), Version 15.5(2)T, DEVELOPMENT TEST SOFTWARE",
            "_type": "networkdevices",
            "Vendor": "Cisco",
            "_id": 301,
            "Code": "67108896",
            "Notes": null
        }
    ],
    "meta": {
        "total": 2,
        "positions": {},
        "references": {}
    }
}

List a specific entry (card) for a class

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/classes/networkdevices/cards/275" | python3 -m json.tool
{
    "data": {
        "Description": "R1",
        "Version": "Linux Software (I86BI_LINUX-ADVENTERPRISEK9-M), Version 15.5(2)T, DEVELOPMENT TEST SOFTWARE",
        "_type": "networkdevices",
        "Vendor": "Cisco",
        "_id": 275,
        "Code": "67108880",
        "Notes": null
    },
    "meta": {
        "total": null,
        "positions": {},
        "references": {}
    }
}

Filter the output (cards)

Prepare a filter as the following:

{
    "attribute": {
        "simple": {
            "attribute": "Description",
			"operator": "equal",
            "value": [
                "R1"
            ]
        }
    }
}

Operators can be contain, equal, notequal, like (using %), isnull, isnotnull, greater, lower .

Filters can be concatenated with and or or:

{
	"attribute": {
		"or": [{
			"simple": {
				"attribute": "Description",
				"operator": "equal",
				"value": ["R1"]
			}
		}, {
			"simple": {
				"attribute": "Description",
				"operator": "equal",
				"value": ["R2"]
			}
		}]
	}
}

Apply the URLencoded filter using a GET request:

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" --data-urlencode 'filter={"attribute":{"simple":{"attribute":"Description","operator":"equal","value":["R1"]}}}' -G "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/classes/networkdevices/cards" | python3 -m json.tool
{
    "data": [
        {
            "Description": "R1",
            "Version": "Linux Software (I86BI_LINUX-ADVENTERPRISEK9-M), Version 15.5(2)T, DEVELOPMENT TEST SOFTWARE",
            "_type": "networkdevices",
            "Vendor": "Cisco",
            "_id": 275,
            "Code": "67108880",
            "Notes": null
        }
    ],
    "meta": {
        "total": 1,
        "positions": {},
        "references": {}
    }
}

List all relationships (domains)

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/domains" | python3 -m json.tool
{
    "data": [
        {
            "name": "networkdevice2interfaces",
            "description": "Network Device to Interfaces",
            "_id": "networkdevice2interfaces"
        },
        {
            "name": "ipv4addresstoipv4network",
            "description": "IPv4 Address to IPv4 Network",
            "_id": "ipv4addresstoipv4network"
        },
        {
            "name": "interface2ipv4addresses",
            "description": "Interface to IPv4 Addresses",
            "_id": "interface2ipv4addresses"
        },
        {
            "name": "interface2interface",
            "description": "Interface to Interface",
            "_id": "interface2interface"
        }
    ],
    "meta": {
        "total": 4,
        "positions": {},
        "references": {}
    }
}

List all relationships for a specific domain

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/domains/networkdevice2interfaces/relations" | python -m json.tool
{
    "data": [
        {
            "_destinationDescription": "R1:Ethernet0/0",
            "_destinationId": 289,
            "_destinationType": "interfaces",
            "_id": 291,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R1:Ethernet0/1",
            "_destinationId": 277,
            "_destinationType": "interfaces",
            "_id": 279,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R1:Ethernet0/2",
            "_destinationId": 281,
            "_destinationType": "interfaces",
            "_id": 283,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R1:Ethernet0/3",
            "_destinationId": 285,
            "_destinationType": "interfaces",
            "_id": 287,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R2:Ethernet0/0",
            "_destinationId": 311,
            "_destinationType": "interfaces",
            "_id": 313,
            "_sourceDescription": "R2",
            "_sourceId": 301,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R2:Ethernet0/1",
            "_destinationId": 307,
            "_destinationType": "interfaces",
            "_id": 309,
            "_sourceDescription": "R2",
            "_sourceId": 301,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R2:Ethernet0/2",
            "_destinationId": 303,
            "_destinationType": "interfaces",
            "_id": 305,
            "_sourceDescription": "R2",
            "_sourceId": 301,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R2:Ethernet0/3",
            "_destinationId": 321,
            "_destinationType": "interfaces",
            "_id": 323,
            "_sourceDescription": "R2",
            "_sourceId": 301,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        }
    ],
    "meta": {
        "positions": {},
        "references": {},
        "total": 8
    }
}

Filter the output (domains)

Prepare a filter as the following:

{
	"attribute": {
		"or": [{
			"simple": {
				"attribute": "_sourceId",
				"operator": "equal",
				"value": ["275"]
			}
		}, {
			"simple": {
				"attribute": "_destinationId",
				"operator": "equal",
				"value": ["275"]
			}
		}]
	}
}

Apply the URLencoded filter using a GET request:

$ curl -s -X GET -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" --data-urlencode 'filter={"attribute":{"or":[{"simple":{"attribute":"_sourceId","operator":"equal","value":["275"]}},{"simple":{"attribute":"_destinationId","operator":"equal","value":["275"]}}]}}' -G "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/domains/networkdevice2interfaces/relations" | python -m json.tool
{
    "data": [
        {
            "_destinationDescription": "R1:Ethernet0/0",
            "_destinationId": 289,
            "_destinationType": "interfaces",
            "_id": 291,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R1:Ethernet0/1",
            "_destinationId": 277,
            "_destinationType": "interfaces",
            "_id": 279,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R1:Ethernet0/2",
            "_destinationId": 281,
            "_destinationType": "interfaces",
            "_id": 283,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        },
        {
            "_destinationDescription": "R1:Ethernet0/3",
            "_destinationId": 285,
            "_destinationType": "interfaces",
            "_id": 287,
            "_sourceDescription": "R1",
            "_sourceId": 275,
            "_sourceType": "networkdevices",
            "_type": "networkdevice2interfaces"
        }
    ],
    "meta": {
        "positions": {},
        "references": {},
        "total": 4
    }
}

Add a new entry (card)

$ curl -s -X POST -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" -H "Content-Type: application/json" --data-binary '{"Code":"67108912","Description":"Router R3","Vendor":"Cisco","Version":"1"}' "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/classes/networkdevices/cards" | python3 -m json.tool
{
    "data": 363
}

Let’s add also a new interface:

$ curl -s -X POST -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" -H "Content-Type: application/json" --data-binary '{"Code":"00:11:22:33:44:55","Description":"R3:Ethernet0/0"}' "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/classes/interfaces/cards" | python3 -m json.tool
{
    "data": 367
}

Add a new relationship (domain)

$ curl -s -X POST -H "CMDBuild-Authorization: hb90u9d3l4jqunf8ni6s7jisli" -H "Content-Type: application/json" --data-binary '{"_sourceType":"networkdevices","_sourceId":363,"_destinationType":"interfaces","_destinationId":367,"DefaultGroup":"false"}' "http://localhost:8080/cmdbuild-2.4.3/services/rest/v2/domains/networkdevice2interfaces/relations" | python3 -m json.tool
{
    "data": 369
}

References

Posted on 22 Nov 2017 by Andrea.
  • Gmail icon
  • Twitter icon
  • Facebook icon
  • LinkedIN icon
  • Google+ icon