summaryrefslogtreecommitdiffstats
path: root/plugins/config_generator.py
blob: 746cefa9a9c85e1f73ddb558a6e7705e4a3fa4fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
#!/usr/bin/python
#
# config_generator.py - Nagios configuration generator for gluster
# entities. Copyright (C) 2014 Red Hat Inc
#
# This program 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 2
# of the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#

from pynag import Model

import server_utils
from glusternagios.glustercli import HostStatus


"""
Change mode helps to identify the change in the defintion.
"ADD" means the entity and all its sub entities are added.
"REMOVE" means the entity and all its sub entities are removed
"UPDATE" means the entity is changes. It may also means sub entities
are added or removed to the entity.
"""
CHANGE_MODE = 'changeMode'
CHANGE_MODE_ADD = "ADD"
CHANGE_MODE_REMOVE = "REMOVE"
CHANGE_MODE_UPDATE = "UPDATE"
HOST_SERVICES = 'host_services'
GENERATED_BY_AUTOCONFIG = "__GENERATED_BY_AUTOCONFIG"
VOL_NAME = '_VOL_NAME'
BRICK_DIR = '_BRICK_DIR'
HOST_UUID = '_HOST_UUID'
NOTES = 'notes'
GLUSTER_AUTO_CONFIG = "Cluster Auto Config"
SERVICE_FIELDS_TO_FORCE_SYNC = [VOL_NAME, NOTES]


class GlusterNagiosConfManager:

    def __init__(self, configDir):
        self.configDir = configDir

    # Create nagios host configuration with the given attributes
    def createHost(self, hostName, alias, template,
                   address, hostGroups, checkCommand, services, uuid):
        host = {}
        host['host_name'] = hostName
        host['alias'] = alias
        host['use'] = template
        host['address'] = address
        if checkCommand:
            host['check_command'] = checkCommand
        if hostGroups:
            host['hostgroups'] = hostGroups
        # Host service is not a field in host configuration. It helps to
        # aggregate all the host services under the host
        if services:
            host[HOST_SERVICES] = services
        if uuid:
            host[HOST_UUID] = uuid
        return host

    def __createVolumeUtilizationService(self, volume, clusterName):
        volumeService = {}
        volumeService['host_name'] = clusterName
        volumeService['use'] = 'gluster-service-with-graph'
        serviceDesc = 'Volume Utilization - %s' % (volume['name'])
        volumeService['service_description'] = serviceDesc
        volumeService[VOL_NAME] = volume['name']
        checkCommand = 'check_vol_utilization!%s!%s!70!90' % \
                       (clusterName, volume['name'])
        volumeService['check_command'] = checkCommand
        volumeService[NOTES] = "Volume type : %s" % (volume['type'])
        return volumeService

    def __createVolumeStatusService(self, volume, clusterName):
        volumeService = {}
        volumeService['host_name'] = clusterName
        volumeService['use'] = 'gluster-service-without-graph'
        serviceDesc = 'Volume Status - %s' % (volume['name'])
        volumeService['service_description'] = serviceDesc
        volumeService[VOL_NAME] = volume['name']
        checkCommand = 'check_vol_status!%s!%s' % \
                       (clusterName, volume['name'])
        volumeService['check_command'] = checkCommand
        volumeService[NOTES] = "Volume type : %s" % (volume['type'])
        return volumeService

    def __createVolumeQuotaStatusService(self, volume, clusterName):
        volumeService = {}
        volumeService['host_name'] = clusterName
        volumeService['use'] = 'gluster-service-without-graph'
        serviceDesc = 'Volume Quota - %s' % (volume['name'])
        volumeService['service_description'] = serviceDesc
        volumeService[VOL_NAME] = volume['name']
        checkCommand = 'check_vol_quota_status!%s!%s' % \
                       (clusterName, volume['name'])
        volumeService['check_command'] = checkCommand
        volumeService[NOTES] = "Volume type : %s" % (volume['type'])
        return volumeService

    def __createVolumeHealStatusService(self, volume, clusterName):
        volumeService = {}
        volumeService['host_name'] = clusterName
        volumeService['use'] = 'gluster-service-without-graph'
        serviceDesc = 'Volume Split-brain status - %s' % (volume['name'])
        volumeService['service_description'] = serviceDesc
        volumeService[VOL_NAME] = volume['name']
        checkCommand = 'check_vol_heal_status!%s!%s' % \
                       (clusterName, volume['name'])
        volumeService['check_command'] = checkCommand
        return volumeService

    def __createVolumeHealInfoService(self, volume, clusterName):
        volumeService = {}
        volumeService['host_name'] = clusterName
        volumeService['use'] = 'gluster-heal-service-with-graph'
        serviceDesc = 'Volume Heal info - %s' % (volume['name'])
        volumeService['service_description'] = serviceDesc
        volumeService[VOL_NAME] = volume['name']
        checkCommand = 'check_vol_heal_info!%s!%s' % \
                       (clusterName, volume['name'])
        volumeService['check_command'] = checkCommand
        return volumeService

    def __createVolumeGeoRepStatusService(self, volume, clusterName):
        volumeService = {}
        volumeService['host_name'] = clusterName
        volumeService['use'] = 'gluster-service-without-graph'
        serviceDesc = 'Volume Geo-Replication - %s' % (volume['name'])
        volumeService['service_description'] = serviceDesc
        volumeService[VOL_NAME] = volume['name']
        checkCommand = 'check_vol_georep_status!%s!%s' % \
                       (clusterName, volume['name'])
        volumeService['check_command'] = checkCommand
        return volumeService

    def createClusterUtilizationService(self, clusterName):
        service = {}
        service['host_name'] = clusterName
        service['use'] = 'gluster-service-with-graph'
        service['service_description'] = 'Cluster Utilization'
        service['check_command'] = 'check_cluster_vol_usage!80!90'
        return service

    def createClusterQuorumService(self, clusterName):
        service = {}
        service['host_name'] = clusterName
        service['use'] = 'gluster-passive-freshness-service'
        service['service_description'] = 'Cluster - Quorum Status'
        service['check_command'] = 'check_quorum_status'
        return service

    def createClusterAutoConfigService(self, clusterName, hostIp):
        service = {}
        service['host_name'] = clusterName
        service['use'] = 'gluster-service'
        service['check_interval'] = '1440'
        service['service_description'] = GLUSTER_AUTO_CONFIG
        service['check_command'] = "gluster_auto_discovery!%s" % (hostIp)
        return service

    # Create all volume related services for the given volume
    def createrVolumeServices(self, volumes, clusterName):
        volumeServices = []
        for volume in volumes:
            volumeService = self.__createVolumeUtilizationService(volume,
                                                                  clusterName)
            volumeServices.append(volumeService)
            if volume.get('quota') == "on":
                volumeService = self.__createVolumeQuotaStatusService(
                    volume, clusterName)
                volumeServices.append(volumeService)

            if 'REPLICATE' in volume['type'].upper():
                volumeService = self.__createVolumeHealStatusService(
                    volume, clusterName)
                volumeServices.append(volumeService)
                volumeService = self.__createVolumeHealInfoService(
                    volume, clusterName)
                volumeServices.append(volumeService)
            if volume.get('geo-rep') == "on":
                volumeService = self.__createVolumeGeoRepStatusService(
                    volume, clusterName)
                volumeServices.append(volumeService)

            volumeService = self.__createVolumeStatusService(volume,
                                                             clusterName)
            volumeServices.append(volumeService)
        return volumeServices

    def __createBrickUtilizationService(self, brick, hostName):
        brickService = {}
        brickService['use'] = 'brick-service'
        brickService['host_name'] = hostName
        serviceDesc = "Brick Utilization - %s" % brick['brickpath']
        brickService['service_description'] = serviceDesc
        brickService[BRICK_DIR] = brick['brickpath']
        brickService[VOL_NAME] = brick['volumeName']
        brickService[NOTES] = "Volume : %s" % (brick['volumeName'])
        return brickService

    def __createBrickStatusService(self, brick, hostName):
        brickService = {}
        brickService['use'] = 'gluster-brick-status-service'
        brickService['host_name'] = hostName
        serviceDesc = "Brick - %s" % brick['brickpath']
        brickService['service_description'] = serviceDesc
        brickService[BRICK_DIR] = brick['brickpath']
        brickService[VOL_NAME] = brick['volumeName']
        brickService[NOTES] = "Volume : %s" % (brick['volumeName'])
        return brickService

    # Create all Brick related service here.
    def createBrickServices(self, host):
        brickServices = []
        for brick in host['bricks']:
            brickService = self.__createBrickUtilizationService(
                brick, host['hostname'])
            brickServices.append(brickService)
            brickService = self.__createBrickStatusService(
                brick, host['hostname'])
            brickServices.append(brickService)
        return brickServices

    # Create a host group with the name
    def createHostGroup(self, name):
        return {'hostgroup_name': name, 'alias': name}

    # Create the Nagios configuration model in run time using list and
    # dictionary
    # Nagios config model hierarchy
    #########################################################################
    # Hostgroup
    #  --'_host' ---> List of host configurations in the host group
    #    --'host_services' ----> List of services in the host
    #########################################################################
    def generateNagiosConfig(self, cluster):
        hostGroup = self.createHostGroup(cluster['name'])
        hostsConfigs = []
        clusterServices = self.createrVolumeServices(
            cluster.get('volumes'), cluster['name'])
        # If there are volumes, then create a cluster utilization service
        if cluster.get('volumes'):
            clusterServices.append(self.createClusterUtilizationService(
                cluster['name']))
            clusterServices.append(self.createClusterQuorumService(
                cluster['name']))
        clusterServices.append(self.createClusterAutoConfigService(
            cluster['name'], cluster['hosts'][0]['hostip']))
        # Create host config for Gluster cluster with volume related services
        clusterHostConfig = self.createHost(
            cluster['name'], cluster['name'], "gluster-cluster",
            cluster['name'], None, None, clusterServices, None)
        hostsConfigs.append(clusterHostConfig)
        # Create host config for all hosts in the cluster with brick related
        # services
        for host in cluster['hosts']:
            if host['status'] == HostStatus.CONNECTED:
                brickServices = self.createBrickServices(host)
                hostGroups = "gluster_hosts,%s" % (cluster['name'])
                hostConfig = self.createHost(
                    host['hostname'], host['hostname'], "gluster-host",
                    host['hostip'], hostGroups, None, brickServices,
                    host.get('uuid'))
                hostsConfigs.append(hostConfig)
        hostGroup["_hosts"] = hostsConfigs
        return hostGroup

    # Get the config file name for the given hostname
    def getCfgFileName(self, hostname):
        return self.configDir + "/" + hostname + ".cfg"

    # Create Nagios config service for the given host group with all hosts.
    # Host group should contain the delta to be written to the configuration.
    # Delta will be processed using the change mode.
    def writeHostGroup(self, hostgroup):
        changeMode = hostgroup[CHANGE_MODE]
        if changeMode == CHANGE_MODE_ADD:
            hostgroupModel = Model.Hostgroup()
            hostgroupModel['hostgroup_name'] = hostgroup['hostgroup_name']
            hostgroupModel['alias'] = hostgroup['alias']
            hostgroupModel.set_filename(
                self.getCfgFileName(hostgroup['hostgroup_name']))
            hostgroupModel.save()
        # Process all the hosts in the hostgroup. ChangeMode of the hostgroup
        # will be used to proces the host if there is not changeMode specified
        # in the host.
        if hostgroup['_hosts']:
            self.writeHosts(hostgroup['_hosts'], changeMode)

    # Fill the pynag model with the given values.
    # 'changeMode' and 'host_services' are special fields which are
    # not meant to be writen to the nagios config, These fields are
    # used to represent the config model and changes.
    def fillModel(self, model, values):
        for key, value in values.iteritems():
            if key not in [CHANGE_MODE, HOST_SERVICES]:
                model[key] = value
        return model

    # Write service to nagios config
    def writeService(self, service, hostname):
        if service[CHANGE_MODE] == CHANGE_MODE_ADD:
            serviceModel = Model.Service()
            serviceModel = self.fillModel(serviceModel, service)
            serviceModel.set_filename(self.getCfgFileName(hostname))
            serviceModel[GENERATED_BY_AUTOCONFIG] = 1
            serviceModel.save()
        elif service[CHANGE_MODE] == CHANGE_MODE_REMOVE:
            serviceModel = Model.Service.objects.filter(
                host_name=hostname,
                service_description=service['service_description'])
            if serviceModel:
                serviceModel[0].delete()
        elif service[CHANGE_MODE] == CHANGE_MODE_UPDATE:
            serviceModel = server_utils.getServiceConfig(
                service['service_description'], service['host_name'])
            self.fillModel(serviceModel, service)
            serviceModel.save()

    # Write all services in the host.
    # host_services filed contains the list of services to be written to
    # nagios configuration
    def writeHostServices(self, host):
        for service in host[HOST_SERVICES]:
            if service.get(CHANGE_MODE) is None:
                service[CHANGE_MODE] = host[CHANGE_MODE]
            self.writeService(service, host['host_name'])

    # Write the host configuration with list of services
    # to nagios configuration
    def writeHost(self, host):
        if host[CHANGE_MODE] == CHANGE_MODE_REMOVE:
            hostModel = Model.Host.objects.filter(
                host_name=host['host_name'])
            if hostModel:
                hostModel[0].delete(recursive=True)
            return
        if host[CHANGE_MODE] == CHANGE_MODE_ADD:
            hostModel = Model.Host()
            hostModel = self.fillModel(hostModel, host)
            hostModel.set_filename(self.getCfgFileName(host['host_name']))
            hostModel.save()

        if host.get(HOST_SERVICES):
            self.writeHostServices(host)

    def writeHosts(self, hosts, chageMode):
        for host in hosts:
            if host.get(CHANGE_MODE) is None:
                host[CHANGE_MODE] = chageMode
            self.writeHost(host)

    # Write the hostgroup delta to nagios configuration.
    def generateConfigFiles(self, delta):
        self.writeHostGroup(delta)