Automatically import AWX inventory from zabbix inventories

AWX 인벤토리를 zabbix 인벤토리에서 가져 오기

 

참고 URL : https://docs.ansible.com/ansible-tower/latest/html/administration/custom_inventory_script.html

참고 URL : https://github.com/ansible/ansible/tree/stable-2.9/contrib/inventory

 

AWX (Ansible Tower) 혹은 Ansible 에서 인벤토리에 호스트를 추가하는 일은 어렵지 않습니다. 하지만 추가할 호스트가 많아지면 엄청난 시간을 할애해야 할 것입니다.

이 같은 단순반복 작업을 편하게 할 수 있게 inventory Scripts 라는 메뉴가 존재 합니다. 위의 github 에서 제공되고 있는 참고 링크를 보시면 다양한 커스텀 스크립트들이 지원되고 있습니다.

현재 회사에는 자빅스의 디스커버리룰을 이용하여 호스트 및 그룹을 자동 등록하고 있기 때문에 자빅스 API 를 이용하여 AWX 의 인벤토리도 자동으로 가져오는 방법을 이용해 보았습니다.

 

1. AWX 서버에 zabbix.ini github 를 참고해서

/etc/ansible/zabbix.ini

파일을 생성 후 자신의 환경에 맞게 작성합니다.
: vi /etc/ansible/zabbix.ini

# Ansible Zabbix external inventory script settings
#
[zabbix]
# Server location
server = https://zabbix.umount.net
# Login
username = zbxapiadmin
password = zbxapiadminpassword
# Verify the server’s SSL certificate
validate_certs = True
# Read zabbix inventory per host
read_host_inventory = True
# Set ansible_ssh_host based on first interface settings
use_host_interface = True

 

2. AWX 서버에 pip 을 이용하여 zabbix-api 를 설치해줍니다.

pip-3 install zabbix-api

 

3. AWX WEB UI 에서 [Inventory Scripts] 메뉴를 선택 후 Create 버튼을 누릅니다. Name 에는 Inventory Script 의 이름을 원하시는대로 작성해 주시고 Organization 을 선택합니다.

Custom Script 부분에 zabbix.py github 의 내용을 그대로 복사해 줍니다.

저는 경로를 명확히 해주기 위해 54번 라인의 conf_path 경로를 절대경로로 수정하였습니다.

#!/usr/bin/env python
# (c) 2013, Greg Buehler
# (c) 2018, Filippo Ferrazini
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
“””
Zabbix Server external inventory script.
========================================
Returns hosts and hostgroups from Zabbix Server.
If you want to run with –limit against a host group with space in the
name, use asterisk. For example –limit=”Linux*servers”.
Configuration is read from `zabbix.ini`.
Tested with Zabbix Server 2.0.6, 3.2.3 and 3.4.
“””
from __future__ import print_function
import os
import sys
import argparse
from ansible.module_utils.six.moves import configparser
try:
from zabbix_api import ZabbixAPI
except Exception:
print(“Error: Zabbix API library must be installed: pip install zabbix-api.”,
file=sys.stderr)
sys.exit(1)
import json
class ZabbixInventory(object):
def read_settings(self):
config = configparser.SafeConfigParser()
conf_path = ‘/etc/ansible/zabbix.ini’
if not os.path.exists(conf_path):
conf_path = os.path.dirname(os.path.realpath(__file__)) + ‘/zabbix.ini’
if os.path.exists(conf_path):
config.read(conf_path)
# server
if config.has_option(‘zabbix’, ‘server’):
self.zabbix_server = config.get(‘zabbix’, ‘server’)
# login
if config.has_option(‘zabbix’, ‘username’):
self.zabbix_username = config.get(‘zabbix’, ‘username’)
if config.has_option(‘zabbix’, ‘password’):
self.zabbix_password = config.get(‘zabbix’, ‘password’)
# ssl certs
if config.has_option(‘zabbix’, ‘validate_certs’):
if config.get(‘zabbix’, ‘validate_certs’) in [‘false’, ‘False’, False]:
self.validate_certs = False
# host inventory
if config.has_option(‘zabbix’, ‘read_host_inventory’):
if config.get(‘zabbix’, ‘read_host_inventory’) in [‘true’, ‘True’, True]:
self.read_host_inventory = True
# host interface
if config.has_option(‘zabbix’, ‘use_host_interface’):
if config.get(‘zabbix’, ‘use_host_interface’) in [‘false’, ‘False’, False]:
self.use_host_interface = False
def read_cli(self):
parser = argparse.ArgumentParser()
parser.add_argument(‘–host’)
parser.add_argument(‘–list’, action=‘store_true’)
self.options = parser.parse_args()
def hoststub(self):
return {
‘hosts’: []
}
def get_host(self, api, name):
api_query = {‘output’: ‘extend’, ‘selectGroups’: ‘extend’, “filter”: {“host”: [name]}}
if self.use_host_interface:
api_query[‘selectInterfaces’] = [‘useip’, ‘ip’, ‘dns’]
if self.read_host_inventory:
api_query[‘selectInventory’] = “extend”
data = {‘ansible_ssh_host’: name}
if self.use_host_interface or self.read_host_inventory:
try:
hosts_data = api.host.get(api_query)[0]
if ‘interfaces’ in hosts_data:
# use first interface only
if hosts_data[‘interfaces’][0][‘useip’] == 0:
data[‘ansible_ssh_host’] = hosts_data[‘interfaces’][0][‘dns’]
else:
data[‘ansible_ssh_host’] = hosts_data[‘interfaces’][0][‘ip’]
if (‘inventory’ in hosts_data) and (hosts_data[‘inventory’]):
data.update(hosts_data[‘inventory’])
except IndexError:
# Host not found in zabbix
pass
return data
def get_list(self, api):
api_query = {‘output’: ‘extend’, ‘selectGroups’: ‘extend’}
if self.use_host_interface:
api_query[‘selectInterfaces’] = [‘useip’, ‘ip’, ‘dns’]
if self.read_host_inventory:
api_query[‘selectInventory’] = “extend”
hosts_data = api.host.get(api_query)
data = {‘_meta’: {‘hostvars’: {}}}
data[self.defaultgroup] = self.hoststub()
for host in hosts_data:
hostname = host[‘name’]
hostvars = dict()
data[self.defaultgroup][‘hosts’].append(hostname)
for group in host[‘groups’]:
groupname = group[‘name’]
if groupname not in data:
data[groupname] = self.hoststub()
data[groupname][‘hosts’].append(hostname)
if ‘interfaces’ in host:
# use first interface only
if host[‘interfaces’][0][‘useip’] == 0:
hostvars[‘ansible_ssh_host’] = host[‘interfaces’][0][‘dns’]
else:
hostvars[‘ansible_ssh_host’] = host[‘interfaces’][0][‘ip’]
if (‘inventory’ in host) and (host[‘inventory’]):
hostvars.update(host[‘inventory’])
data[‘_meta’][‘hostvars’][hostname] = hostvars
return data
def __init__(self):
self.defaultgroup = ‘group_all’
self.zabbix_server = None
self.zabbix_username = None
self.zabbix_password = None
self.validate_certs = True
self.read_host_inventory = False
self.use_host_interface = True
self.meta = {}
self.read_settings()
self.read_cli()
if self.zabbix_server and self.zabbix_username:
try:
api = ZabbixAPI(server=self.zabbix_server, validate_certs=self.validate_certs)
api.login(user=self.zabbix_username, password=self.zabbix_password)
# zabbix_api tries to exit if it cannot parse what the zabbix server returned
# so we have to use SystemExit here
except (Exception, SystemExit) as e:
print(“Error: Could not login to Zabbix server. Check your zabbix.ini.”, file=sys.stderr)
sys.exit(1)
if self.options.host:
data = self.get_host(api, self.options.host)
print(json.dumps(data, indent=2))
elif self.options.list:
data = self.get_list(api)
print(json.dumps(data, indent=2))
else:
print(“usage: –list ..OR.. –host <hostname>”, file=sys.stderr)
sys.exit(1)
else:
print(“Error: Configuration of server and credentials are required. See zabbix.ini.”, file=sys.stderr)
sys.exit(1)
ZabbixInventory()

awx-inventory-from-zabbix-01

 

4. AWX WEB UI 에서 [Inventories] 메뉴를 선택 후 새 Inventory 를 생성해 줍니다. Name 과 Organization 만 입력하고 SAVE 하면 됩니다. SAVE 를 클릭하면 Source 를 수정할 수 있게 됩니다. Source 를 클릭 후 Create 버튼을 클릭 합니다.

awx-inventory-from-zabbix-02

 

5. Name 에 원하는 이름을 적고, Source 는 Custom Script 를 선택하면 Source Details 부분이 생겨납니다. Custom Inventory Script 의 돋보기 버튼을 누르면 Inventory Scripts 에서 생성한 스크립트가 나옵니다. 선택 후 저장합니다.

awx-inventory-from-zabbix-03

 

6. 다시 AWX WEB UI 에서 [Inventories] 메뉴를 선택 후 추가한 인벤토리를 클릭하고 Source 를 클릭하여 Custom Script 가 추가된 것을 확인 할 수 있습니다.

좌측 Sources 이름 옆에 구름 아이콘이 회색으로 있습니다. 우측 Actions 를 보시면 연필, 화살표, 휴지통이 보입니다. 화살표 버튼이 싱크 버튼입니다. 싱크 버튼을 클릭하면 구름 아이콘이 반짝입니다. 구름 아이콘을 클릭 하면 싱크가 되는 상황을 자세히 볼 수 있습니다. 싱크가 정상이면 구름 아이콘이 녹색으로 변경 됩니다.

awx-inventory-from-zabbix-04

awx-inventory-from-zabbix-05

 

7. 성공적으로 싱크가 완료 되었다면 다시 AWX WEB UI 에서 [Inventories] 메뉴를 선택 후 추가한 인벤토리를 선택 후 Hosts 및 Groups 를 보시면 추가된 호스트와 그룹을 확인할 수 있습니다.

awx-inventory-from-zabbix-06

awx-inventory-from-zabbix-07

 

# 추가한 인벤토리 스크립트는 인벤토리 메뉴내에서 스케쥴을 이용하여 자동으로 가져오도록 설정할수도 있습니다.

# 특정 그룹 또는 호스트별로 job 을 실행 시키기 위해서는 템플릿에서 LIMIT 부분을 활용하시면 됩니다. LIMIT 패턴 설정은 이곳을 확인하시면 됩니다.

 

이 글은 umount 블로그 에서도 보실 수 있습니다.

0 0 votes
Article Rating
Subscribe
Notify of
guest

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.

0 Comments
Inline Feedbacks
View all comments
Scroll to top
0
Would love your thoughts, please comment.x
()
x