├── README.md ├── XenServerVM-regist.pl ├── citrix-trap.conf ├── zabbix-agentd ├── citrix-storage.py └── citrix.py └── zbx_export_templates_citrix.xml /README.md: -------------------------------------------------------------------------------- 1 | zabbix-xenserver-templates 2 | ================================== 3 | 4 | Zabbix templates for Citrix XenServer and zabbix-sender for monitoring. 5 | 6 | This templates and programs inspire by following programs of Emir Imamagic. 7 | 8 | Citrix XenAPI/RRDs monitoring 9 | https://www.zabbix.com/forum/showthread.php?t=25121 10 | 11 | Version 12 | ---------------- 13 | 0.1 14 | 15 | Usage 16 | ---------------- 17 | TBD 18 | - import templates. 19 | - /usr/libexec/zabbix-agentd 20 | - cp zabbix-agentd/*.py /usr/libexec/zabbix-agentd 21 | - cp citrix.conf /etc/zabbix/agent-conf.d 22 | - edit XenServerVM-regist.pl 23 | - exec XenServerVM-regist.pl 24 | 25 | ## TODO 26 | - Rewrite by perl or python.. 27 | - code cleanup. 28 | - Write "Usage" 29 | 30 | -------------------------------------------------------------------------------- /XenServerVM-regist.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Data::Dumper; 6 | 7 | use ZabbixAPI; 8 | 9 | use RPC::XML; 10 | use RPC::XML::Client; 11 | use Data::Dumper; 12 | 13 | use Getopt::Long; 14 | use Pod::Usage 'pod2usage'; 15 | 16 | 17 | my $DEBUG = 0; 18 | 19 | # for Zabbix API 20 | my $user = 'zabbix'; 21 | my $pass = 'zabbix'; 22 | my $api_url = 'http://localhost/zabbix/'; 23 | 24 | 25 | # Master server of XenServer pool. 26 | my $master = '192.0.2.1'; 27 | my $xen_user = 'root'; 28 | my $xen_pass = 'pass'; 29 | 30 | { 31 | my $za = ZabbixAPI->new("$api_url"); 32 | $za->login( "$user", "$pass" ); 33 | 34 | # Establish the XEN API connection 35 | my $xen = RPC::XML::Client->new("http://$master"); 36 | my $session = extractvalue($xen->simple_request("session.login_with_password", 37 | $xen_user,$xen_pass)); 38 | 39 | if (! $session) 40 | { 41 | print "connection failure : $master\n" if $DEBUG > 0; 42 | exit; # or NEXT; 43 | } 44 | 45 | # Template_Citrix_Trap_VM 46 | my $template_name = 'Template_Citrix_Trap_VM'; 47 | my $templateid_citrix_trap_vm = $za->template_get( {filter=>{host=>$template_name}})->[0]->{templateid}; 48 | 49 | #$host_ref = extractvalue($xen->simple_request("session.get_this_host", 50 | # $session,$session)); 51 | # Get the reference for all the VMs on the xen dom0 host. 52 | #$domU_ref = extractvalue($xen->simple_request("host.get_resident_VMs", 53 | # $session, $host_ref)); 54 | my $pool_hosts = extractvalue($xen->simple_request("host.get_all", $session)); 55 | 56 | foreach my $host (@$pool_hosts) { 57 | my $hostname = extractvalue($xen->simple_request("host.get_hostname", $session,$host)); 58 | my $hostgroup_exist = $za->hostgroup_exists( { name => $hostname }); 59 | 60 | if ( $hostgroup_exist ) { 61 | print "$hostname : exist : True\n" if $DEBUG > 0; 62 | 63 | } else { 64 | my $create_hostgroup = $za->hostgroup_create( 65 | { 66 | 'name' => $hostname, 67 | } 68 | ); 69 | } 70 | 71 | my @groups = undef; 72 | push(@groups , $hostname); 73 | my $groupids = $za->hostgroup_get( { filter => { name => \@groups } }); 74 | print Dumper $groupids if $DEBUG > 0; 75 | my $vms = extractvalue($xen->simple_request("host.get_resident_VMs", $session,$host)); 76 | foreach my $vm_ref (@$vms) { 77 | my $record = extractvalue($xen->simple_request("VM.get_record", $session, $vm_ref)); 78 | next if $record->{is_control_domain} == 1; 79 | 80 | my $hostname = $record->{uuid}; 81 | 82 | my $hostname_exist = $za->host_exists( { host => $hostname }); 83 | print "HOST: $hostname\n" if $DEBUG > 0; 84 | 85 | my @templates; 86 | push (@templates, {templateid => $templateid_citrix_trap_vm}); 87 | 88 | my $vif_record = extractvalue($xen->simple_request("VM.get_VIFs", $session, $vm_ref)); 89 | foreach my $vif_ref (@$vif_record) { 90 | my $vifs = extractvalue($xen->simple_request("VIF.get_record", $session, $vif_ref)); 91 | my $template_name = 'Template_Citrix_Trap_Network_vif_' . $vifs->{device}; 92 | my $templateid = $za->template_get( {filter=>{host=>$template_name}})->[0]->{templateid}; 93 | push(@templates, {templateid => $templateid}); 94 | } 95 | my $vbd_record = extractvalue($xen->simple_request("VM.get_VBDs", $session, $vm_ref)); 96 | foreach my $vbd_ref (@$vbd_record) { 97 | my $vbd = extractvalue($xen->simple_request("VBD.get_record", $session, $vbd_ref)); 98 | next if $vbd->{type} eq 'CD'; 99 | my $template_name = 'Template_Citrix_Trap_Disk_' . $vbd->{device}; 100 | my $templateid = $za->template_get( {filter=>{host=>$template_name}})->[0]->{templateid}; 101 | push (@templates, { templateid => $templateid}); 102 | } 103 | if ($hostname_exist) { 104 | print "$hostname true\n" if $DEBUG > 0; 105 | my @hosts = undef; 106 | push (@hosts, $hostname); 107 | my $hostid = $za->host_get( { filter => { host => \@hosts}} ); 108 | 109 | print Dumper $hostid if $DEBUG > 0; 110 | print Dumper $groupids if $DEBUG > 0; 111 | print Dumper @templates if $DEBUG > 0; 112 | print $record->{name_label} . "\n" if $DEBUG > 0; 113 | print $hostid->[0]->{hostid} . "\n" if $DEBUG > 0; 114 | $hostid = $za->host_update( 115 | { 116 | hostid => $hostid->[0]->{hostid}, 117 | name => $record->{name_label}, 118 | groups => $groupids, 119 | templates => \@templates, 120 | } 121 | ); 122 | print Dumper $hostid if $DEBUG > 0; 123 | } else { 124 | my $hostid = $za->host_create( 125 | { 126 | host => $hostname, 127 | name => $record->{name_label}, 128 | interfaces => [ { 129 | type => 1, 130 | main => 1, 131 | useip => 1, 132 | dns => '', 133 | ip => '127.0.0.1', 134 | port => '10050', 135 | },], 136 | groups => $groupids, 137 | templates => \@templates, 138 | } 139 | ); 140 | print Dumper $hostid if $DEBUG > 0; 141 | 142 | } 143 | 144 | } 145 | } 146 | #my $all_vm = $xen->simple_request("VM.get_all", $session); 147 | #print Dumper($all_vm); 148 | 149 | exit; 150 | } 151 | 152 | sub extractvalue 153 | { 154 | my ($val) = @_; 155 | if ($val->{'Status'} eq "Success") 156 | { 157 | print "Success?\n" if $DEBUG > 0; 158 | return $val->{'Value'}; 159 | } 160 | else 161 | { 162 | print "fail?\n" if $DEBUG > 0; 163 | return undef; 164 | } 165 | } 166 | 167 | -------------------------------------------------------------------------------- /citrix-trap.conf: -------------------------------------------------------------------------------- 1 | UserParameter=citrix[*],/bin/egrep -i "$2 $3 " /var/tmp/zabbixCitrixFile_$1 | awk '{print $$3}' 2 | UserParameter=citrix.storage[*],/bin/egrep -i "$2 $3 " /var/tmp/zabbixCitrixStorageFile_$1 | awk '{print $$3}' 3 | UserParameter=citrix.trap.api[*],/usr/libexec/zabbix-agentd/citrix.py $1 $2 $3 /var/tmp/zabbixCitrixFile_$1 /var/tmp/zabbixCitrixFile_vm_$1 && echo 1 || echo 0 4 | UserParameter=citrix.trap.storageapi[*],/usr/libexec/zabbix-agentd/citrix-storage.py $1 $2 $3 /var/tmp/zabbixCitrixStorageFile_$1 /var/tmp/zabbixCitrixStorageFile_vm_$1 && echo 1 || echo 0 5 | UserParameter=citrix.trap.push[*],zabbix_sender -c /etc/zabbix/zabbix_agentd.conf -i /var/tmp/zabbixCitrixFile_vm_$1 >/dev/null && echo 1 || echo 0 6 | UserParameter=citrix.trap.pushstorage[*],zabbix_sender -c /etc/zabbix/zabbix_agentd.conf -i /var/tmp/zabbixCitrixStorageFile_vm_$1 >/dev/null && echo 1 || echo 0 7 | -------------------------------------------------------------------------------- /zabbix-agentd/citrix-storage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Probe for extracting storage information from 4 | # XenServer. 5 | # by Emir Imamagic (eimamagi@srce.hr) 6 | # 7 | # The following info is extracted: 8 | # vbd_xvda_size - total size of VDIs (B) 9 | # sr_size_Local_storage - total size of SR (B) 10 | # sr_virt_alloc_Local_storage - allocated space on SR (B) 11 | # 12 | # Data is stored to in zabbix_sender 13 | # friendly format: 14 | # hostname metric value 15 | # vmname metric value 16 | # ... 17 | # 18 | # If the parameter vmfilename is defined VM date 19 | # will be stored into vmfilename instead of filename. 20 | 21 | import urllib2 22 | import xml.dom.minidom 23 | import sys, time 24 | import itertools 25 | import re 26 | import shutil 27 | 28 | sys.path.append('/usr/local/lib/python') 29 | import XenAPI 30 | 31 | def printStats(hostname, username, password, filename, vmfilename): 32 | virtual = False 33 | 34 | f=open(filename, 'w') 35 | 36 | if (vmfilename != ""): 37 | vf = open(vmfilename, 'w') 38 | virtual = True 39 | else: 40 | vf = f 41 | 42 | url = 'https://%s' % hostname 43 | session = XenAPI.Session(url) 44 | try: 45 | session.login_with_password(username,password) 46 | except XenAPI.Failure, e: 47 | if (e.details[0] == 'HOST_IS_SLAVE'): 48 | session=XenAPI.Session("https://" + e.details[1]) 49 | session.login_with_password(username,password) 50 | else: 51 | raise 52 | sx = session.xenapi 53 | retval = False 54 | 55 | for host in sx.host.get_all(): 56 | hostname = sx.host.get_hostname(host) 57 | for pbd in sx.host.get_PBDs(host): 58 | sr = sx.PBD.get_SR(pbd) 59 | name = sx.SR.get_name_label(sr).replace(' ','_') 60 | if (re.match(r"(DVD_drives|Removable_storage|XenServer_Tools)", name)): 61 | continue 62 | if (sr != 'OpaqueRef:NULL'): 63 | f.write("%s sr_size_%s %s\n" % (hostname, name, sx.SR.get_physical_size(sr))) 64 | f.write("%s sr_virt_alloc_%s %s\n" % (hostname, name, sx.SR.get_virtual_allocation(sr))) 65 | 66 | for vm in sx.host.get_resident_VMs(host): 67 | hostname = sx.VM.get_name_label(vm) 68 | vm_uuid = sx.VM.get_uuid(vm) 69 | 70 | # skip control domain 71 | if ('Control domain on host' in hostname): 72 | continue 73 | 74 | for vbd in sx.VM.get_VBDs(vm): 75 | vdi = sx.VBD.get_VDI(vbd) 76 | if (vdi != 'OpaqueRef:NULL'): 77 | sr = sx.VDI.get_SR(vdi) 78 | type = sx.SR.get_type(sr) 79 | if (re.match(r"iso", type)): 80 | continue 81 | #vf.write("%s vbd_%s_size %s\n" % (hostname, sx.VBD.get_device(vbd), sx.VDI.get_virtual_size(vdi))) 82 | vf.write("%s vbd_%s_size %s\n" % (vm_uuid, sx.VBD.get_device(vbd), sx.VDI.get_virtual_size(vdi))) 83 | 84 | sx.logout() 85 | f.close() 86 | if (virtual): 87 | vf.close() 88 | 89 | 90 | def main(hostname, username, password, filename, vmfilename = ""): 91 | tmpfilename = filename + '.tmp' 92 | tmpvmfilename = "" 93 | 94 | if (vmfilename != ""): 95 | tmpvmfilename = vmfilename + '.tmp' 96 | 97 | printStats(hostname, username, password, tmpfilename, tmpvmfilename) 98 | 99 | shutil.move(tmpfilename, filename) 100 | if (vmfilename != ""): 101 | shutil.move(tmpvmfilename, vmfilename) 102 | 103 | 104 | if __name__ == "__main__": 105 | if len(sys.argv) >= 5: 106 | main(*sys.argv[1:]) 107 | elif len(sys.argv) == 1: 108 | main("xenserver", "root", "XXX", "/var/tmp/zabbixCitrixFile") 109 | else: 110 | print "Usage:" 111 | print sys.argv[0], " [vmfilename]" 112 | sys.exit(1) 113 | 114 | -------------------------------------------------------------------------------- /zabbix-agentd/citrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Probe for extracting performance data from 4 | # XAPI RRDs. 5 | # by Emir Imamagic (eimamagi@srce.hr) 6 | # 7 | # For more info on RRDs see: 8 | # http://wiki.xensource.com/xenwiki/XAPI_RRDs 9 | # 10 | # In addition to VM performance data probe aggregates 11 | # CPU usage over all CPUs (metric cpu) and counts 12 | # number of CPUs (metric cpu_count) on hosts and VMs. 13 | # 14 | # Data is stored to in zabbix_sender 15 | # friendly format: 16 | # hostname metric value 17 | # vmname metric value 18 | # ... 19 | # 20 | # If the parameter vmfilename is defined VM date 21 | # will be stored into vmfilename instead of filename. 22 | 23 | import urllib2 24 | #import socket 25 | import xml.dom.minidom 26 | import sys, time 27 | import itertools 28 | import re 29 | import shutil 30 | 31 | sys.path.append('/usr/local/lib/python') 32 | import XenAPI 33 | 34 | #socket.setdefaulttimeout(20) 35 | 36 | def getHostsVms(hostname, username, password, hosts, vms): 37 | url = 'https://%s' % hostname 38 | session = XenAPI.Session(url) 39 | try: 40 | session.login_with_password(username,password) 41 | except XenAPI.Failure, e: 42 | if (e.details[0] == 'HOST_IS_SLAVE'): 43 | session=XenAPI.Session("https://" + e.details[1]) 44 | session.login_with_password(username,password) 45 | else: 46 | raise 47 | finally: 48 | session.logout() 49 | sx = session.xenapi 50 | retval = False 51 | 52 | for host in sx.host.get_all(): 53 | hosts[sx.host.get_uuid(host)] = sx.host.get_hostname(host) 54 | 55 | for vm in sx.host.get_resident_VMs(host): 56 | vms[sx.VM.get_uuid(vm)] = sx.VM.get_name_label(vm) 57 | 58 | retval = True 59 | 60 | sx.logout() 61 | 62 | return retval 63 | 64 | def getStatsXML(hostname, username, password, delay): 65 | start = int(time.time()) - 2 * int(delay) 66 | theurl = 'http://%s/rrd_updates?start=%s&host=true&cf=ave&interval=%s' % (hostname, start, delay) 67 | 68 | # taken from: 69 | # http://www.voidspace.org.uk/python/articles/authentication.shtml 70 | passman = urllib2.HTTPPasswordMgrWithDefaultRealm() 71 | passman.add_password(None, theurl, username, password) 72 | authhandler = urllib2.HTTPBasicAuthHandler(passman) 73 | opener = urllib2.build_opener(authhandler) 74 | urllib2.install_opener(opener) 75 | pagehandle = urllib2.urlopen(theurl) 76 | 77 | return pagehandle.read() 78 | 79 | def getStats(hosts, username, password, delay): 80 | legendArr = [] 81 | valueArr = [] 82 | 83 | for hostname in hosts.itervalues(): 84 | page = getStatsXML(hostname, username, password, delay) 85 | 86 | dom = xml.dom.minidom.parseString(page) 87 | legends = dom.getElementsByTagName("legend")[0] 88 | for legend in legends.getElementsByTagName("entry"): 89 | legendArr.append(legend.childNodes[0].data) 90 | 91 | values = dom.getElementsByTagName("row")[0] 92 | for v in values.getElementsByTagName("v"): 93 | valueArr.append(v.childNodes[0].data) 94 | 95 | # taken from: 96 | # http://stackoverflow.com/questions/209840/map-two-lists-into-a-dictionary-in-python 97 | return dict(itertools.izip(legendArr,valueArr)) 98 | 99 | def printMetric(f, host, hostsCpu, hostsCpuCount, metric, value): 100 | # summarize cpu usage (TODO: make optional) 101 | if (re.match(r"cpu\d+", metric)): 102 | if (hostsCpu.has_key(host)): 103 | hostsCpuCount[host] += 1 104 | hostsCpu[host] += float(value) 105 | else: 106 | hostsCpuCount[host] = 1 107 | hostsCpu[host] = float(value) 108 | else: 109 | name = host.replace(':','-') 110 | f.write("%s %s %s\n" % (name, metric, value)) 111 | 112 | def printHostCpu(f, hostsCpu, hostsCpuCount): 113 | for key, value in hostsCpu.iteritems(): 114 | name = key.replace(':','-') 115 | f.write("%s cpu %s\n" % (name, value)) 116 | 117 | for key, value in hostsCpuCount.iteritems(): 118 | name = key.replace(':','-') 119 | f.write("%s cpu_count %s\n" % (name, value)) 120 | 121 | def printStats(values, hosts, vms, filename, vmfilename): 122 | hostsCpu = dict() 123 | vmsCpu = dict() 124 | hostsCpuCount = dict() 125 | vmsCpuCount = dict() 126 | virtual = False 127 | 128 | f=open(filename, 'w') 129 | 130 | if (vmfilename != ""): 131 | vf = open(vmfilename, 'w') 132 | virtual = True 133 | else: 134 | vf = f 135 | 136 | for key, value in values.iteritems(): 137 | match = re.match(r"(\S+)\:(\S+)\:(\S+)\:(\S+)", key) 138 | # TODO: enable selecting type 139 | if (match.group(1) == 'AVERAGE'): 140 | metric = match.group(4) 141 | 142 | # find hostname 143 | if (match.group(2) == 'host'): 144 | if (hosts.has_key(match.group(3))): 145 | host = hosts[match.group(3)] 146 | else: 147 | continue 148 | printMetric(f, host, hostsCpu, hostsCpuCount, metric, value) 149 | elif (match.group(2) == 'vm'): 150 | if (vms.has_key(match.group(3))): 151 | #host = vms[match.group(3)] 152 | host = match.group(3) 153 | else: 154 | continue 155 | # skip control domain 156 | if ('Control domain on host' in host): 157 | continue 158 | printMetric(vf, host, vmsCpu, vmsCpuCount, metric, value) 159 | else: 160 | continue 161 | 162 | printHostCpu(f, hostsCpu, hostsCpuCount) 163 | printHostCpu(vf, vmsCpu, vmsCpuCount) 164 | 165 | f.close() 166 | if (virtual): 167 | vf.close() 168 | 169 | def main(hostname, username, password, filename, vmfilename = ""): 170 | hosts = dict() 171 | vms = dict() 172 | delay = 60 173 | tmpfilename = filename + '.tmp' 174 | tmpvmfilename = "" 175 | if (vmfilename != ""): 176 | tmpvmfilename = vmfilename + '.tmp' 177 | 178 | if (not getHostsVms(hostname, username, password, hosts, vms) ): 179 | print "ERROR: host not found" 180 | 181 | values = getStats(hosts, username, password, delay) 182 | 183 | printStats(values, hosts, vms, tmpfilename, tmpvmfilename) 184 | 185 | shutil.move(tmpfilename, filename) 186 | if (vmfilename != ""): 187 | shutil.move(tmpvmfilename, vmfilename) 188 | 189 | if __name__ == "__main__": 190 | if len(sys.argv) >= 5: 191 | main(*sys.argv[1:]) 192 | elif len(sys.argv) == 1: 193 | main("xenserver", "root", "XXX", "/var/tmp/zabbixCitrixFile") 194 | else: 195 | print "Usage:" 196 | print sys.argv[0], " [vmfilename]" 197 | sys.exit(1) 198 | 199 | --------------------------------------------------------------------------------