1# encoding: utf-8
2
3# Nagstamon - Nagios status monitor for your desktop
4# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
19
20import urllib.request
21import urllib.parse
22import urllib.error
23import socket
24import sys
25import re
26import copy
27
28from datetime import datetime, timedelta
29
30from Nagstamon.Objects import *
31from Nagstamon.Servers.Generic import GenericServer
32from Nagstamon.Config import conf
33from Nagstamon.Helpers import webbrowser_open
34
35
36class CentreonServer(GenericServer):
37    TYPE = 'Centreon'
38
39    # centreon generic web interface uses a sid which is needed to ask for news
40    SID = None
41
42    # HARD/SOFT state mapping
43    HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'}
44
45    # apparently necessesary because of non-english states as in https://github.com/HenriWahl/Nagstamon/issues/91 (Centeron 2.5)
46    TRANSLATIONS = {'INDISPONIBLE': 'DOWN',
47                    'INJOIGNABLE': 'UNREACHABLE',
48                    'CRITIQUE': 'CRITICAL',
49                    'INCONNU': 'UNKNOWN',
50                    'ALERTE': 'WARNING'}
51
52    # Entries for monitor default actions in context menu
53    MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime']
54
55    # Centreon works better or at all with html.parser for BeautifulSoup
56    PARSER = 'html.parser'
57
58    # Needed to detect each Centreon's version
59    centreon_version = None
60    # Token that centreon use to protect the system
61    centreon_token = None
62    # To only detect broker once
63    first_login = True
64    # limit number of services retrived
65    limit_services_number = 9999
66    # default value, applies to version 2.2 and others
67    XML_PATH = 'xml'
68
69    def init_config(self):
70        '''
71        dummy init_config, called at thread start, not really needed here, just omit extra properties
72        '''
73        # set URLs here already
74        self.init_HTTP()
75        if not self.tls_error and self.centreon_version is not None:
76            self._define_url()
77
78    def init_HTTP(self):
79        """
80        initialize HTTP connection
81        """
82        if self.session is None:
83            GenericServer.init_HTTP(self)
84
85        if self.centreon_version is None:
86            result_versioncheck = self.FetchURL(self.monitor_cgi_url + '/index.php', giveback='raw')
87            raw_versioncheck, error_versioncheck = result_versioncheck.result, result_versioncheck.error
88            if error_versioncheck == '':
89                if re.search('2\.2\.[0-9]', raw_versioncheck):
90                    self.centreon_version = 2.2
91                    if conf.debug_mode is True:
92                        self.Debug(server=self.get_name(), debug='Centreon version detected : 2.2')
93                    # URLs for browser shortlinks/buttons on popup window
94                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1',
95                                        'hosts': '$MONITOR$/main.php?p=20103&o=hpb',
96                                        'services': '$MONITOR$/main.php?p=20202&o=svcpb',
97                                        'history': '$MONITOR$/main.php?p=203'}
98                elif re.search('2\.[3-6]\.[0-5]', raw_versioncheck):
99                    self.centreon_version = 2.3456
100                    if conf.debug_mode is True:
101                        self.Debug(server=self.get_name(), debug='Centreon version detected : 2.3 <=> 2.6.5')
102                    # URLs for browser shortlinks/buttons on popup window
103                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1',
104                                        'hosts': '$MONITOR$/main.php?p=20103&o=hpb',
105                                        'services': '$MONITOR$/main.php?p=20202&o=svcpb',
106                                        'history': '$MONITOR$/main.php?p=203'}
107                elif re.search('2\.6\.[6-9]', raw_versioncheck):
108                    self.centreon_version = 2.66
109                    if conf.debug_mode is True:
110                        self.Debug(server=self.get_name(), debug='Centreon version detected : 2.6.6')
111                    # URLs for browser shortlinks/buttons on popup window
112                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1',
113                                        'hosts': '$MONITOR$/main.php?p=20103&o=hpb',
114                                        'services': '$MONITOR$/main.php?p=20202&o=svcpb',
115                                        'history': '$MONITOR$/main.php?p=203'}
116                elif re.search('2\.7\.[0-9]', raw_versioncheck):
117                    # Centreon 2.7 only support C. Broker
118                    self.centreon_version = 2.7
119                    if conf.debug_mode is True:
120                        self.Debug(server=self.get_name(), debug='Centreon version detected : 2.7')
121                    # URLs for browser shortlinks/buttons on popup window
122                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?',
123                                        'hosts': '$MONITOR$/main.php?p=20202&o=hpb',
124                                        'services': '$MONITOR$/main.php?p=20201&o=svcpb',
125                                        'history': '$MONITOR$/main.php?p=203'}
126                elif re.search('2\.8\.[0-9]', raw_versioncheck):
127                    # Centreon 2.8 only support C. Broker
128                    self.centreon_version = 2.8
129                    if conf.debug_mode is True:
130                        self.Debug(server=self.get_name(), debug='Centreon version detected : 2.8')
131                    # URLs for browser shortlinks/buttons on popup window
132                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?',
133                                        'hosts': '$MONITOR$/main.php?p=20202',
134                                        'services': '$MONITOR$/main.php?p=20201',
135                                        'history': '$MONITOR$/main.php?p=203'}
136                elif re.search('18\.10\.[0-9]', raw_versioncheck):
137                    self.centreon_version = 18.10
138                    if conf.debug_mode is True:
139                        self.Debug(server=self.get_name(), debug='Centreon version detected : 18.10')
140                    # URLs for browser shortlinks/buttons on popup window
141                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?',
142                                        'hosts': '$MONITOR$/main.php?p=20202',
143                                        'services': '$MONITOR$/main.php?p=20201',
144                                        'history': '$MONITOR$/main.php?p=203'}
145                elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck):
146                    self.centreon_version = 19.04
147                    if conf.debug_mode is True:
148                        self.Debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10')
149                    # URLs for browser shortlinks/buttons on popup window
150                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?',
151                                        'hosts': '$MONITOR$/main.php?p=20202',
152                                        'services': '$MONITOR$/main.php?p=20201',
153                                        'history': '$MONITOR$/main.php?p=203'}
154                else:
155                    # unsupported version or unable do determine
156                    self.centreon_version = 19.04
157                    if conf.debug_mode is True:
158                        self.Debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 19.04')
159                    # URLs for browser shortlinks/buttons on popup window
160                    self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?',
161                                        'hosts': '$MONITOR$/main.php?p=20202&o=hpb',
162                                        'services': '$MONITOR$/main.php?p=20201&o=svcpb',
163                                        'history': '$MONITOR$/main.php?p=203'}
164            else:
165                if conf.debug_mode is True:
166                    self.Debug(server=self.get_name(), debug='Error getting the home page : ' + error_versioncheck)
167
168            if self.first_login:
169                self.SID = self._get_sid().result
170                self.first_login = False
171
172            del result_versioncheck, raw_versioncheck, error_versioncheck
173
174    def reset_HTTP(self):
175        '''
176        Centreon needs deletion of SID
177        '''
178        self.SID = None
179        self.SID = self._get_sid().result
180
181    def open_monitor(self, host, service=''):
182        if self.use_autologin is True:
183            auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key
184        else:
185            auth = ''
186
187        #  Meta
188        if host == '_Module_Meta':
189            # Centreon < 2.7
190            if self.centreon_version < 2.7:
191                webbrowser_open(self.urls_centreon['index'] + '?' + urllib.parse.urlencode({'p': 20206, 'o': 'meta'}) + auth )
192            #  Centreon 2.7
193            elif self.centreon_version == 2.7:
194                webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20206, 'o':'meta'}) + auth )
195            #  Centreon 2.8
196            elif self.centreon_version == 2.8:
197                m =  re.search(r'^.+ \((?P<rsd>.+)\)$', service)
198                if m:
199                    service = m.group('rsd')
200                    webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth )
201            # Starting from Centreon 18.10
202            else:
203                m =  re.search(r'^.+ \((?P<rsd>.+)\)$', service)
204                if m:
205                    service = m.group('rsd')
206                    webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth )
207
208        # must be a host if service is empty
209        elif service == '':
210            if self.centreon_version < 2.7:
211                webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':201,'o':'hd', 'host_name':host}) + auth )
212            elif self.centreon_version in [2.7, 2.8]:
213                webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth )
214            else:
215                webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth )
216
217        # so it's a service
218        else:
219            if self.centreon_version < 2.7:
220                webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':202, 'o':'svcd',  'host_name':host, 'service_description':service}) + auth )
221            elif self.centreon_version in [2.7, 2.8]:
222                webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth )
223            else:
224                webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth )
225
226
227    def _get_sid(self):
228        '''
229        gets a shiny new SID for XML HTTP requests to Centreon cutting it out via .partition() from raw HTML
230        additionally get php session cookie
231        '''
232        try:
233            # Aulogin with key, BROWSER_URLS needs the key
234            if self.use_autologin == True:
235                auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key
236                self.BROWSER_URLS= { 'monitor': self.BROWSER_URLS['monitor'] + auth,\
237                                    'hosts': self.BROWSER_URLS['hosts'] + auth,\
238                                    'services': self.BROWSER_URLS['services'] + auth,\
239                                    'history': self.BROWSER_URLS['history'] + auth}
240                raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw')
241                if conf.debug_mode == True:
242                    self.Debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key)
243                # Gathering of the token who will be used to interact with Centreon (start with 2.66)
244                if  self.centreon_version >= 2.66 and self.centreon_version < 19.04:
245                    page = self.FetchURL(self.monitor_cgi_url + '/main.get.php')
246                    self.centreon_token = page.result.find('input', {'name': "centreon_token"})['value']
247
248            # Password auth
249            else:
250                login = self.FetchURL(self.monitor_cgi_url + '/index.php')
251                if login.error == '' and login.status_code == 200:
252                    # Centreon >= 2.6.6 implement a token
253                    if  self.centreon_version >= 2.66 and self.centreon_version <= 19.04:
254                        form = login.result.find('form')
255                        form_inputs = {}
256                        # Need to catch the centreon_token for login to work
257                        for form_input in ('centreon_token', 'submitLogin'):
258                            form_inputs[form_input] = form.find('input', {'name': form_input})['value']
259                        self.centreon_token = form_inputs['centreon_token']
260                        form_inputs['useralias'] = self.username
261                        form_inputs['password'] = self.password
262                        # fire up login button with all needed data
263                        raw = self.FetchURL(self.monitor_cgi_url + '/index.php', cgi_data=form_inputs)
264                    else:
265                        login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"}
266                        raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw")
267                if conf.debug_mode == True:
268                    self.Debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password)
269
270            sid = self.session.cookies['PHPSESSID']
271            if conf.debug_mode == True:
272                self.Debug(server=self.get_name(), debug='SID : ' + sid)
273                if  self.centreon_version >= 2.66 and self.centreon_version < 19.04:
274                    self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token)
275            # those broker urls would not be changing too often so this check migth be done here
276            if self.first_login:
277                self._get_xml_path(sid)
278                self.first_login = False
279            return Result(result=sid)
280
281        except:
282            import traceback
283            traceback.print_exc(file=sys.stdout)
284            result, error = self.Error(sys.exc_info())
285            return Result(result=result, error=error)
286
287
288    def get_start_end(self, host):
289        '''
290        get start and end time for downtime from Centreon server
291        '''
292        try:
293            # It's not possible since 18.10 to get date from the webinterface
294            # because it's set in javascript
295            if self.centreon_version < 18.10:
296                cgi_data = {'o':'ah',\
297                            'host_name':host}
298                if self.centreon_version < 2.7:
299                    cgi_data['p'] = '20106'
300                elif self.centreon_version == 2.7:
301                    cgi_data['p'] = '210'
302                elif self.centreon_version == 2.8:
303                    cgi_data['o'] = 'a'
304                    cgi_data['p'] = '210'
305                result = self.FetchURL(self.urls_centreon['main'], cgi_data = cgi_data, giveback='obj')
306
307                html, error = result.result, result.error
308                if error == '':
309                    start_date = html.find(attrs={'name':'start'}).attrs['value']
310                    start_hour = html.find(attrs={'name':'start_time'}).attrs['value']
311                    start_time = start_date + ' ' + start_hour
312
313                    end_date = html.find(attrs={'name':'end'}).attrs['value']
314                    end_hour = html.find(attrs={'name':'end_time'}).attrs['value']
315                    end_time = end_date + ' ' + end_hour
316                    return start_time, end_time
317
318            else:
319                start_time = datetime.now().strftime("%m/%d/%Y %H:%M")
320                end_time = datetime.now() + timedelta(hours=2)
321                end_time = end_time.strftime("%m/%d/%Y %H:%M")
322                return start_time, end_time
323
324        except:
325            self.Error(sys.exc_info())
326            return 'n/a', 'n/a'
327
328
329    def GetHost(self, host):
330        '''
331        Centreonified way to get host ip - attribute 'a' in down hosts xml is of no use for up
332        hosts so we need to get ip anyway from web page
333        '''
334        # the fastest method is taking hostname as used in monitor
335        if conf.connect_by_host == True or host == '':
336            return Result(result=host)
337
338        # do a web interface search limited to only one result - the hostname
339        cgi_data = {'sid': self.SID,
340                    'search': host,
341                    'num': 0,
342                    'limit': 1,
343                    'sort_type':'hostname',
344                    'order': 'ASC',
345                    'date_time_format_status': 'd/m/Y H:i:s',
346                    'o': 'h',
347                    'p': 20102,
348                    'time': 0}
349
350        centreon_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode(cgi_data)
351
352        result = self.FetchURL(centreon_hosts, giveback='xml')
353        xmlobj, error, status_code = result.result, result.error, result.status_code
354
355        # initialize ip string
356        ip = ''
357
358        if len(xmlobj) != 0:
359            ip = str(xmlobj.l.a.text)
360            # when connection by DNS is not configured do it by IP
361            try:
362                if conf.connect_by_dns == True:
363                   # try to get DNS name for ip (reverse DNS), if not available use ip
364                    try:
365                        address = socket.gethostbyaddr(ip)[0]
366                    except:
367                        if conf.debug_mode == True:
368                            self.Debug(server=self.get_name(), debug='Unable to do a reverse DNS lookup on IP: ' + ip)
369                        address = ip
370                else:
371                    address = ip
372            except:
373                result, error = self.Error(sys.exc_info())
374                return Result(result=result, error=error)
375
376        else:
377            result, error = self.Error(sys.exc_info())
378            return Result(error=error)
379
380        del xmlobj
381
382        # print IP in debug mode
383        if conf.debug_mode == True:
384            self.Debug(server=self.get_name(), debug='IP of %s:' % (host) + ' ' + address)
385
386        # give back host or ip
387        return Result(result=address)
388
389
390    def _get_xml_path(self, sid):
391        '''
392        Find out where this instance of Centreon is publishing the status XMLs
393        Centreon 2.6 + ndo/c.broker - /include/monitoring/status/Hosts/xml/{ndo,broker}/hostXML.php according to configuration
394        Centreon 2.7 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php
395        Centreon 2.8 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php
396        regexping HTML for Javascript
397        '''
398        if self.centreon_version <= 2.66:
399            # 2.6 support NDO and C. Broker, we must check which one is used
400            cgi_data = {'p':201, 'sid':sid}
401            result = self.FetchURL(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw')
402            raw, error = result.result, result.error
403            if error == '':
404                if re.search('var _addrXML.*xml\/ndo\/host', raw):
405                  self.XML_PATH = 'xml/ndo'
406                  if conf.debug_mode == True:
407                      self.Debug(server=self.get_name(), debug='Detected broker : NDO')
408                elif re.search('var _addrXML.*xml\/broker\/host', raw):
409                    self.XML_PATH = 'xml/broker'
410                    if conf.debug_mode == True:
411                        self.Debug(server=self.get_name(), debug='Detected broker : C. Broker')
412                else:
413                    if conf.debug_mode == True:
414                        self.Debug(server=self.get_name(), debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker')
415                    self.XML_PATH = 'xml/broker'
416                del raw
417            else:
418                if conf.debug_mode == True:
419                    self.Debug(server=self.get_name(), debug='Unable to fetch the main page to detect the broker : ' + error)
420            del result, error
421        else:
422            if conf.debug_mode == True:
423                self.Debug(server=self.get_name(), debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH)
424
425
426    def _define_url(self):
427        urls_centreon_2_2 = {
428            'main': self.monitor_cgi_url + '/main.php',
429            'index': self.monitor_cgi_url + '/index.php',
430            'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php',
431            'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php',
432            'xml_meta': self.monitor_cgi_url + '/include/monitoring/status/Meta/' + self.XML_PATH + '/metaServiceXML.php',
433            'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php',
434            'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php',
435            'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php',
436            # no idea if this really exist in centreon < 2.7
437            'autologoutXMLresponse': self.monitor_cgi_url + '/include/common/javascript/autologoutXMLresponse.php'
438        }
439
440        # inconsistant url in Centreon 2.7
441        urls_centreon_2_7 = {
442            'main': self.monitor_cgi_url + '/main.php',
443            'index': self.monitor_cgi_url + '/index.php',
444            'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php',
445            'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/broker/hostXML.php',
446            'xml_meta': self.monitor_cgi_url + '/include/monitoring/status/Meta/' + self.XML_PATH + '/broker/metaServiceXML.php',
447            'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php',
448            'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php',
449            'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php',
450            'autologoutXMLresponse': self.monitor_cgi_url + '/include/common/javascript/autologoutXMLresponse.php'
451        }
452
453        urls_centreon_2_8 = {
454            'main': self.monitor_cgi_url + '/main.php',
455            'index': self.monitor_cgi_url + '/index.php',
456            'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php',
457            'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php',
458            'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php',
459            'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php',
460            'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php',
461            'autologoutXMLresponse': self.monitor_cgi_url + '/include/core/autologout/autologoutXMLresponse.php'
462        }
463
464        urls_centreon_18_10 = {
465            'main': self.monitor_cgi_url + '/main.get.php',
466            # needed to get the frames around the page when opening the monitoring on a host/service
467            'main_with_frames': self.monitor_cgi_url + '/main.php',
468            'index': self.monitor_cgi_url + '/index.php',
469            'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php',
470            'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php',
471            'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php',
472            'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php',
473            'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php',
474            'keepAlive': self.monitor_cgi_url + '/api/internal.php?object=centreon_keepalive&action=keepAlive'
475        }
476
477        if self.centreon_version < 2.7:
478            self.urls_centreon = urls_centreon_2_2
479        elif self.centreon_version == 2.7:
480            self.urls_centreon = urls_centreon_2_7
481        elif self.centreon_version == 2.8:
482            self.urls_centreon = urls_centreon_2_8
483        # 18.10 and beyond
484        elif self.centreon_version >= 18.10:
485            self.urls_centreon = urls_centreon_18_10
486        if conf.debug_mode == True:
487            self.Debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version))
488
489
490    def _get_host_id(self, host):
491        '''
492        get host_id via parsing raw html
493        '''
494        if self.centreon_version < 2.7:
495            cgi_data = {'p': 20102, 'o': 'hd', 'host_name': host, 'sid': self.SID}
496        else:
497            cgi_data = {'p': 20202, 'o': 'hd', 'host_name': host, 'sid': self.SID}
498
499        url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data)
500
501        result = self.FetchURL(url, giveback='raw')
502        raw, error = result.result, result.error
503
504        if error == '':
505            host_id = raw.partition("var host_id = '")[2].partition("'")[0]
506            del raw
507        else:
508            if conf.debug_mode == True:
509                self.Debug(server=self.get_name(), debug='Host ID could not be retrieved.')
510
511        # some cleanup
512        del result, error
513
514        # only if host_id is an usable integer return it
515        try:
516            if int(host_id):
517                if conf.debug_mode == True:
518                    self.Debug(server=self.get_name(), host=host, debug='Host ID is ' + host_id)
519                return host_id
520            else:
521                return ''
522        except:
523            return ''
524
525
526    def _get_host_and_service_id(self, host, service):
527        '''
528        parse a ton of html to get a host and a service id...
529        '''
530        cgi_data = {'p':'20201',\
531                    'host_name':host,\
532                    'service_description':service,\
533                    'o':'svcd'}
534
535        # This request must be done in a GET, so just encode the parameters and fetch
536        result = self.FetchURL(self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw")
537        raw, error = result.result, result.error
538
539        if error == '':
540            host_id = raw.partition("var host_id = '")[2].partition("'")[0]
541            svc_id = raw.partition("var svc_id = '")[2].partition("'")[0]
542            del raw
543            if conf.debug_mode == True:
544                self.Debug(server=self.get_name(), host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id)
545        else:
546            if conf.debug_mode == True:
547                self.Debug(server=self.get_name(), host=host, service=service, debug='- IDs could not be retrieved.')
548
549        # some cleanup
550        del result, error
551
552        # only if host_id is an usable integer return it
553        try:
554            if int(host_id) and int(svc_id):
555                if conf.debug_mode == True:
556                    self.Debug(server=self.get_name(), host=host, service=service, debug='- Host & Service ID are valid (int)')
557                return host_id,svc_id
558            else:
559                return '',''
560        except:
561            return '',''
562
563
564    def _get_status(self):
565        '''
566        Get status from Centreon Server
567        '''
568        # Be sure that the session is still active
569        result = self._check_session()
570        if result is not None:
571            if result.result == 'ERROR':
572                if 'urls_centreon' in result.error:
573                    result.error = 'Connection error'
574                return result
575
576        # services (unknown, warning or critical?)
577        if self.centreon_version < 2.7:
578            nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'sort_type':'status', 'sid':self.SID})
579        else:
580            nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'p':20201, 'nc':0, 'criticality':0, 'statusService':'svcpb', 'sSetOrderInMemory':1, 'sid':self.SID})
581
582        # hosts (up or down or unreachable)
583        # define hosts xml URL, because of inconsistant url
584        if self.centreon_version < 2.7:
585            nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'sort_type':'status', 'sid':self.SID})
586        elif self.centreon_version >= 2.7 and self.centreon_version < 19.04:
587            nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1, 'sid':self.SID})
588        else:
589            nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1})
590
591        # hosts - mostly the down ones
592        # unfortunately the hosts status page has a different structure so
593        # hosts must be analyzed separately
594        try:
595            result = self.FetchURL(nagcgiurl_hosts, giveback='xml')
596            xmlobj, error, status_code = result.result, result.error, result.status_code
597
598            # check if any error occured
599            errors_occured = self.check_for_error(xmlobj, error, status_code)
600
601            # if there are errors return them
602            if errors_occured != False:
603                return(errors_occured)
604
605            # Check if the result is not empty
606            if len(xmlobj) == 0:
607                if conf.debug_mode == True:
608                    self.Debug(server=self.get_name(), debug='Empty host XML result')
609                return Result(result=None, error="Empty host XML result")
610
611            # in case there are no children session ID is expired
612            if xmlobj.text.lower() == 'bad session id':
613                del xmlobj
614                if conf.debug_mode == True:
615                    self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...')
616
617                # try again...
618                self.SID = self._get_sid().result
619                result = self.FetchURL(nagcgiurl_hosts, giveback='xml')
620                xmlobj, error, status_code = result.result, result.error, result.status_code
621                errors_occured = self.check_for_error(xmlobj, error, status_code)
622                # if there are errors return them
623                if errors_occured != False:
624                    return(errors_occured)
625
626                # a second time a bad session id should raise an error
627                if xmlobj.text.lower() == 'bad session id':
628                    if conf.debug_mode == True:
629                        self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML')
630                    return Result(result='ERROR',
631                                  error='Bad session ID',
632                                  status_code=status_code)
633
634            for l in xmlobj.findAll('l'):
635                try:
636                    # host objects contain service objects
637                    if not l.hn.text in self.new_hosts:
638                        self.new_hosts[str(l.hn.text)] = GenericHost()
639                        self.new_hosts[str(l.hn.text)].name =  str(l.hn.text)
640                        self.new_hosts[str(l.hn.text)].server = self.name
641                        self.new_hosts[str(l.hn.text)].status = str(l.cs.text)
642                        # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91
643                        if self.new_hosts[str(l.hn.text)].status in self.TRANSLATIONS:
644                            self.new_hosts[str(l.hn.text)].status = self.TRANSLATIONS[self.new_hosts[str(l.hn.text)].status]
645                        self.new_hosts[str(l.hn.text)].attempt, self.new_hosts[str(l.hn.text)].status_type  = str(l.tr.text).split(' ')
646                        self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type]
647                        self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text)
648                        self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text)
649                        self.new_hosts[str(l.hn.text)].status_information= str(l.ou.text)
650                        if l.find('cih') != None:
651                            self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text)
652                        else:
653                            self.new_hosts[str(l.hn.text)].criticality = ''
654                        self.new_hosts[str(l.hn.text)].acknowledged = bool(int(str(l.ha.text)))
655                        self.new_hosts[str(l.hn.text)].scheduled_downtime = bool(int(str(l.hdtm.text)))
656                        if l.find('is') != None:
657                            self.new_hosts[str(l.hn.text)].flapping = bool(int(str(l.find('is').text)))
658                        else:
659                            self.new_hosts[str(l.hn.text)].flapping = False
660                        self.new_hosts[str(l.hn.text)].notifications_disabled = not bool(int(str(l.ne.text)))
661                        self.new_hosts[str(l.hn.text)].passiveonly = not bool(int(str(l.ace.text)))
662                except:
663                    import traceback
664                    traceback.print_exc(file=sys.stdout)
665                    # set checking flag back to False
666                    self.isChecking = False
667                    result, error = self.Error(sys.exc_info())
668                    return Result(result=result, error=error)
669
670            del xmlobj
671
672        except:
673            import traceback
674            traceback.print_exc(file=sys.stdout)
675            # set checking flag back to False
676            self.isChecking = False
677            result, error = self.Error(sys.exc_info())
678            return Result(result=result, error=error)
679
680        # services
681        try:
682            result = self.FetchURL(nagcgiurl_services, giveback='xml')
683            xmlobj, error, status_code = result.result, result.error, result.status_code
684
685            # check if any error occured
686            errors_occured = self.check_for_error(xmlobj, error, status_code)
687            # if there are errors return them
688            if errors_occured != False:
689                return(errors_occured)
690
691            # Check if the result is not empty
692            if len(xmlobj) == 0:
693                if conf.debug_mode == True:
694                    self.Debug(server=self.get_name(), debug='Empty service XML result')
695                return Result(result=None, error="Empty service XML result")
696
697            # in case there are no children session id is invalid
698            if xmlobj.text.lower() == 'bad session id':
699                # debug
700                if conf.debug_mode == True:
701                    self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...')
702                # try again...
703                self.SID = self._get_sid().result
704                result = self.FetchURL(nagcgiurl_services, giveback='xml')
705                xmlobj, error, status_code = result.result, result.error, result.status_code
706                errors_occured = self.check_for_error(xmlobj, error, status_code)
707                # if there are errors return them
708                if errors_occured != False:
709                    return(errors_occured)
710
711                # a second time a bad session id should raise an error
712                if xmlobj.text.lower() == 'bad session id':
713                    return Result(result='ERROR',
714                                  error='Bad session ID',
715                                  status_code=status_code)
716
717            # In Centreon 2.8, Meta are merged with regular services
718            if self.centreon_version < 2.8:
719                # define meta-services xml URL
720                if self.centreon_version == 2.7:
721                    nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID})
722                else:
723                    nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID})
724
725                # retrive meta-services xml STATUS
726                result_meta = self.FetchURL(nagcgiurl_meta_services, giveback='xml')
727                xmlobj_meta, error_meta, status_code_meta = result_meta.result, result_meta.error, result_meta.status_code
728
729                # check if any error occured
730                errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta)
731
732                # if there are errors return them
733                if errors_occured != False:
734                    return(errors_occured)
735
736                # a second time a bad session id should raise an error
737                if xmlobj_meta.text.lower() == 'bad session id':
738                    if conf.debug_mode == True:
739                        self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML')
740
741                    return Result(result='ERROR',
742                                  error='Bad session ID',
743                                  status_code=status_code_meta)
744
745                # INSERT META-services xml at the end of the services xml
746                try:
747                        xmlobj.append(xmlobj_meta.reponse)
748                except:
749                        import traceback
750                        traceback.print_exc(file=sys.stdout)
751                        # set checking flag back to False
752                        self.isChecking = False
753                        result, error = self.Error(sys.exc_info())
754                        return Result(result=result, error=error)
755                # do some cleanup
756                del xmlobj_meta
757
758            for l in xmlobj.findAll('l'):
759                try:
760                    # host objects contain service objects
761                    ###if not self.new_hosts.has_key(str(l.hn.text)):
762                    if not l.hn.text in self.new_hosts:
763                        self.new_hosts[str(l.hn.text)] = GenericHost()
764                        self.new_hosts[str(l.hn.text)].name = str(l.hn.text)
765                        self.new_hosts[str(l.hn.text)].status = 'UP'
766                    # if a service does not exist create its object
767                    if not l.sd.text in self.new_hosts[str(l.hn.text)].services:
768                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)] = GenericService()
769                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host = str(l.hn.text)
770                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = str(l.sd.text)
771                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].server = self.name
772                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = str(l.cs.text)
773
774                        if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta':
775                            # ajusting service name for Meta services
776                            if self.centreon_version < 2.8:
777                                self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sd.text), l.rsd.text)
778                                self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt = str(l.ca.text)
779                            else:
780                                self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sdn.text), l.sdl.text)
781                                self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \
782                                self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ')
783                        else:
784                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \
785                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ')
786
787                        # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91
788                        # Still needed in Centreon 2.8 at least : https://github.com/HenriWahl/Nagstamon/issues/344
789                        # Need enhancement, we can do service state matching with this field <sc>service_unknown</sc>
790                        #if self.centreon_version < 2.66:
791                        if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status in self.TRANSLATIONS:
792                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = self.TRANSLATIONS[\
793                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status]
794
795                        if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'):
796                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type =\
797                            self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type]
798
799                        if conf.debug_mode == True:
800                            self.Debug(server=self.get_name(), debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type)
801                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text)
802                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text)
803                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_information = str(l.po.text).replace('\n', ' ').strip()
804
805                        if l.find('cih') != None:
806                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = str(l.cih.text)
807                        else:
808                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = ''
809
810                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].acknowledged = bool(int(str(l.pa.text)))
811                        self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].notifications_disabled = not bool(int(str(l.ne.text)))
812
813                        # for features not available in centreon < 2.8 and meta services
814                        if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'):
815                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].scheduled_downtime = bool(int(str(l.dtm.text)))
816                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].flapping = bool(int(str(l.find('is').text)))
817                            self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].passiveonly = not bool(int(str(l.ac.text)))
818
819                except:
820                    import traceback
821                    traceback.print_exc(file=sys.stdout)
822                    # set checking flag back to False
823                    self.isChecking = False
824                    result, error = self.Error(sys.exc_info())
825                    return Result(result=result, error=error)
826
827            # do some cleanup
828            del xmlobj
829
830        except:
831            import traceback
832            traceback.print_exc(file=sys.stdout)
833            # set checking flag back to False
834            self.isChecking = False
835            result, error = self.Error(sys.exc_info())
836            return Result(result=result, error=error)
837
838        # return True if all worked well
839        return Result()
840
841
842    def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]):
843        # decision about host or service - they have different URLs
844        try:
845            if service == '':
846                # host
847                cgi_data = {'cmd': '14',
848                            'host_name': host,
849                            'author': author,
850                            'comment': comment,
851                            'submit': 'Add',
852                            'notify': int(notify),
853                            'persistent': int(persistent),
854                            'sticky': int(sticky),
855                            'ackhostservice': '0',
856                            'en': '1'}
857                if self.centreon_version < 2.7:
858                    cgi_data['p'] = '20105'
859                    cgi_data['o'] = 'hpb'
860                else:
861                    cgi_data['p'] = '20202'
862                    cgi_data['o'] = 'hpb'
863                    cgi_data['centreon_token'] = self.centreon_token
864
865                # Post
866                raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw')
867                del raw
868
869            # if host is acknowledged and all services should be to or if a service is acknowledged
870            # (and all other on this host too)
871            if service != '' or len(all_services) > 0:
872                # service(s) @ host
873                # if all_services is empty only one service has to be checked - the one clicked
874                # otherwise if there all services should be acknowledged
875                if len(all_services) == 0: all_services = [service]
876
877                # acknowledge all services on a host
878                for s in all_services:
879                    cgi_data = {'cmd': '15',
880                                'host_name': host,
881                                'author': author,
882                                'comment': comment,
883                                'submit': 'Add',
884                                'notify': int(notify),
885                                'service_description': s,
886                                'force_check': '1',
887                                'persistent': int(persistent),
888                                # following not needed in 18.10, required in wich version ?
889                                'persistant': int(persistent),
890                                'sticky': int(sticky),
891                                'o': 'svcd',
892                                'en': '1'}
893                    if self.centreon_version < 2.7:
894                        cgi_data['p'] = '20215'
895                    else:
896                        cgi_data['p'] = '20201'
897                        cgi_data['centreon_token'] = self.centreon_token
898
899                    # in case of a meta-service, extract the 'rsd' field from the service name :
900                    if host == '_Module_Meta':
901                        m =  re.search(r'^.+ \((?P<rsd>.+)\)$', s)
902                        if m:
903                            rsd = m.group('rsd')
904                            if self.centreon_version < 2.8:
905                                cgi_data = {'p': '20206',
906                                            'o': 'meta',
907                                            'cmd': '70',
908                                            'select[' + host + ';' + rsd + ']': '1',
909                                            'limit': '0'}
910                            elif self.centreon_version in [2.8, 18.10]:
911                                cgi_data['service_description'] = rsd
912
913                    # POST, for some strange reason only working if giveback is 'raw'
914                    raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw')
915                    del raw
916        except:
917            self.Error(sys.exc_info())
918
919
920    def _set_recheck(self, host, service):
921        '''
922        host and service ids are needed to tell Centreon what whe want
923        '''
924        try:
925        # decision about host or service - they have different URLs
926            #  Meta
927            if host == '_Module_Meta':
928                if conf.debug_mode == True:
929                    self.Debug(server=self.get_name(), debug='Recheck on a Meta service, more work to be done')
930                m =  re.search(r'^.+ \((?P<rsd>.+)\)$', service)
931                if m:
932                    rsd = m.group('rsd')
933                    if self.centreon_version < 2.8:
934                        url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p': '20206','o': 'meta','cmd': '3','select[' + host + ';' + rsd + ']': '1','limit':'0'})
935                    else:
936                        url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p': '202','o': 'svc','cmd': '3','select[' + host + ';' + rsd + ']': '1','limit':'1','centreon_token':self.centreon_token})
937
938            elif service == '':
939                # ... it can only be a host, so check all his services and there is a command for that
940                host_id = self._get_host_id(host)
941
942                if self.centreon_version < 2.7:
943                    url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id,'sid':self.SID})
944                else:
945                    url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id})
946                del host_id
947
948            else:
949                # service @ host
950                host_id, service_id = self._get_host_and_service_id(host, service)
951
952                # Starting from 19.04 this must be in POST
953                if self.centreon_version < 19.04:
954                    # fill and encode URL data
955                    cgi_data = urllib.parse.urlencode({'cmd':'service_schedule_check', 'actiontype':1,\
956                                                 'host_id':host_id, 'service_id':service_id, 'sid':self.SID})
957
958                    url = self.urls_centreon['xml_serviceSendCommand'] + '?' + cgi_data
959                    del host_id, service_id
960                else:
961                    cgi_data = {'cmd': 'service_schedule_check',
962                                'host_id': host_id,
963                                'service_id': service_id,
964                                'actiontype': '0'}
965                    del host_id, service_id
966
967            if self.centreon_version < 19.04:
968                # execute GET request
969                raw = self.FetchURL(url, giveback='raw')
970                del raw
971            else:
972                # running remote cgi command with POST method, for some strange reason only working if
973                # giveback is 'raw'
974                raw = self.FetchURL(self.urls_centreon['xml_serviceSendCommand'], cgi_data=cgi_data, giveback='raw')
975                del raw
976        except:
977            self.Error(sys.exc_info())
978
979
980    def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes):
981        '''
982        gets actual host and service ids and apply them to downtime cgi
983        '''
984        try:
985            # duration unit is minute
986            duration = (hours * 60) + minutes
987            # need cmdPopup.php needs boolean
988            if fixed == 1:
989                fixed = 'true'
990            else:
991                fixed = 'false'
992
993            # Host downtime
994            if service == '':
995                if self.centreon_version < 19.04:
996                    cgi_data = {'cmd':75,\
997                                'duration':duration,\
998                                'duration_scale':'m',\
999                                'start':start_time,\
1000                                'end':end_time,\
1001                                'comment':comment,\
1002                                'fixed':fixed,\
1003                                'downtimehostservice':'true',\
1004                                'author':author,\
1005                                'sid':self.SID,\
1006                                'select['+host+']':1
1007                                }
1008                # Params has changed starting from 19.04
1009                else:
1010                    cgi_data = {'cmd':75,
1011                                'duration':duration,
1012                                'duration_scale':'m',
1013                                'comment':comment,
1014                                'start':start_time,
1015                                'end':end_time,
1016                                'host_or_centreon_time':0,
1017                                'fixed':fixed,
1018                                'downtimehostservice':'true',
1019                                'author':author,
1020                                'resources':'["'+host+'"]'
1021                                }
1022
1023            # Service downtime
1024            else:
1025                # Centreon 2.8 only, in case of a meta-service, extract the 'rsd' field from the service name :
1026                if host == '_Module_Meta' and self.centreon_version in [2.8, 18.10]:
1027                    m =  re.search(r'^.+ \((?P<rsd>.+)\)$', service)
1028                    if m:
1029                        rsd = m.group('rsd')
1030                        service = rsd
1031                if self.centreon_version < 19.04:
1032                    cgi_data = {'cmd':74,\
1033                                'duration':duration,\
1034                                'duration_scale':'m',\
1035                                'start':start_time,\
1036                                'end':end_time,\
1037                                'comment':comment,\
1038                                'fixed':fixed,\
1039                                'downtimehostservice':0,\
1040                                'author':author,\
1041                                'sid':self.SID,\
1042                                'select['+host+';'+service+']':1
1043                                }
1044
1045                # Params has changed starting from 19.04
1046                else:
1047                    cgi_data = {'cmd':74,
1048                                'duration':duration,
1049                                'duration_scale':'m',
1050                                'comment':comment,
1051                                'start':start_time,
1052                                'end':end_time,
1053                                'host_or_centreon_time':0,
1054                                'fixed':fixed,
1055                                'downtimehostservice':0,
1056                                'author':author,
1057                                'resources':'["'+host+'%3B'+service+'"]'
1058                                }
1059
1060            if self.centreon_version < 19.04:
1061                # This request must be done in a GET, so just encode the parameters and fetch
1062                raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw")
1063                del raw
1064            # Starting from 19.04, must be POST
1065            else:
1066                # Do it in POST
1067                raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'], cgi_data=cgi_data, giveback='raw')
1068                del raw
1069
1070        except:
1071            self.Error(sys.exc_info())
1072
1073
1074    def _check_session(self):
1075        if conf.debug_mode == True:
1076            self.Debug(server=self.get_name(), debug='Checking session status')
1077        if 'url_centreon' not in self.__dict__:
1078            self.init_config()
1079        try:
1080            if self.centreon_version >= 18.10:
1081                result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw')
1082                self.raw, self.error, self.status_code = result.result, result.error, result.status_code
1083                # Return 200 & null a session is open
1084                if conf.debug_mode == True:
1085                    self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code))
1086                # 401 if no valid session is present
1087                if self.status_code == 401:
1088                    self.SID = self._get_sid().result
1089                    if conf.debug_mode == True:
1090                        self.Debug(server=self.get_name(), debug='Session renewed')
1091
1092            else:
1093                result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml')
1094                xmlobj, error, status_code = result.result, result.error, result.status_code
1095                self.session_state = xmlobj.find("state").text.lower()
1096                if conf.debug_mode == True:
1097                    self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state)
1098                if self.session_state == "nok":
1099                    self.SID = self._get_sid().result
1100                    if conf.debug_mode == True:
1101                        self.Debug(server=self.get_name(), debug='Session renewed')
1102
1103        except:
1104            import traceback
1105            traceback.print_exc(file=sys.stdout)
1106            result, error = self.Error(sys.exc_info())
1107            return Result(result=result, error=error)
1108