diff --git a/Jenkinsfile b/Jenkinsfile index 3e5e2be1a85cfc3c0ad0d70c46fff8dd2ffd967a..f9348236bff042cb87c1ab42ca913266e5057bd5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -163,7 +163,7 @@ try { build job: 'des-mbi-bundle-centos7', wait: false } } catch(e) { - if (env.BRANCH_NAME == '2.8.x' && !(${e} =~ /^.+FlowInterruptedException$/)) { + if (env.BRANCH_NAME == '2.8.x' && !(${e} =~ /^.+FlowInterruptedException\$/)) { slackSend channel: "#monitoring-metrology", color: "#F30031", message: "*FAILURE*: `CENTREON WEB` <${env.BUILD_URL}|build #${env.BUILD_NUMBER}> on branch ${env.BRANCH_NAME}\n" + diff --git a/behat.yml b/behat.yml index 28d1509a20b887f94534b6fcc2ed333be2ec202e..590842dc7b540ef55b4dab6c7264028673eb4377 100644 --- a/behat.yml +++ b/behat.yml @@ -105,6 +105,10 @@ default: paths: [ %paths.base%/features/DowntimeDST.feature ] contexts: [ DowntimeDSTContext ] + downtime_recurrent: + paths: [ %paths.base%/features/DowntimeRecurrent.feature ] + contexts: [ DowntimeRecurrentContext ] + command_arguments: paths: [ %paths.base%/features/CommandArguments.feature ] contexts: [ CommandArgumentsContext ] diff --git a/doc/en/_static/images/developer/lua/add_parameter.png b/doc/en/_static/images/developer/lua/add_parameter.png new file mode 100644 index 0000000000000000000000000000000000000000..453b84cb0f21ceb7b0cd55a76e95b9fd34c81785 Binary files /dev/null and b/doc/en/_static/images/developer/lua/add_parameter.png differ diff --git a/doc/en/_static/images/developer/lua/add_stream_connector.png b/doc/en/_static/images/developer/lua/add_stream_connector.png new file mode 100644 index 0000000000000000000000000000000000000000..1e5b27c08a7e98de5807f5b30fafeb7be18daac6 Binary files /dev/null and b/doc/en/_static/images/developer/lua/add_stream_connector.png differ diff --git a/doc/en/_static/images/developer/lua/archi_broker_lua_script.png b/doc/en/_static/images/developer/lua/archi_broker_lua_script.png new file mode 100644 index 0000000000000000000000000000000000000000..ffaa5b94b156bf5995967dd3ac30b69d0555f712 Binary files /dev/null and b/doc/en/_static/images/developer/lua/archi_broker_lua_script.png differ diff --git a/doc/en/_static/images/developer/lua/archi_broker_regular.png b/doc/en/_static/images/developer/lua/archi_broker_regular.png new file mode 100644 index 0000000000000000000000000000000000000000..470a08ec12665be407fecb6c10883f45be5e06a9 Binary files /dev/null and b/doc/en/_static/images/developer/lua/archi_broker_regular.png differ diff --git a/doc/en/_static/images/developer/lua/archi_broker_stream.png b/doc/en/_static/images/developer/lua/archi_broker_stream.png new file mode 100644 index 0000000000000000000000000000000000000000..364e73e60eaac84e40b24ed7d8db221707d980e6 Binary files /dev/null and b/doc/en/_static/images/developer/lua/archi_broker_stream.png differ diff --git a/doc/en/_static/images/developer/lua/broker_influxdb_output.png b/doc/en/_static/images/developer/lua/broker_influxdb_output.png new file mode 100644 index 0000000000000000000000000000000000000000..857d23dc7b210591013f583e1c9cb6286e3ed255 Binary files /dev/null and b/doc/en/_static/images/developer/lua/broker_influxdb_output.png differ diff --git a/doc/en/_static/images/developer/lua/describe_output.png b/doc/en/_static/images/developer/lua/describe_output.png new file mode 100644 index 0000000000000000000000000000000000000000..16270525de72786ac68207b511755fa8b6bef71b Binary files /dev/null and b/doc/en/_static/images/developer/lua/describe_output.png differ diff --git a/doc/en/_static/images/developer/lua/visualize_data_grafana.png b/doc/en/_static/images/developer/lua/visualize_data_grafana.png new file mode 100644 index 0000000000000000000000000000000000000000..e09321dcd3d5f8170868ecaf21e642cc70892eea Binary files /dev/null and b/doc/en/_static/images/developer/lua/visualize_data_grafana.png differ diff --git a/doc/en/api/api_rest/index.rst b/doc/en/api/api_rest/index.rst index ceb9c619035730e53ed03c25587bc812a7ae2983..844efd45db15b6d667b736e093bdaeb25ea4798c 100644 --- a/doc/en/api/api_rest/index.rst +++ b/doc/en/api/api_rest/index.rst @@ -17,8 +17,18 @@ Permissions ----------- To perform API calls using a specific Centreon user, you need permissions to do so. -You have to edit user settings on the menu **Configuration > Users > Contacts/Users**, -edit user and on second tab check box **Reach API**. + +There are two types of permission: + +You can give access to the configuration for a specific Centreon user. To do so you have +to edit user settings on the menu **Configuration > Users > Contacts/Users**, +edit user and on second tab check box **Reach API Configuration**. + +You can give access to the realtime for a specific Centreon user. To do so you have +to edit user settings on the menu **Configuration > Users > Contacts/Users**, +edit user and on second tab check box **Reach API Realtime**. + +If you want both then check **both** checkboxes Authentication diff --git a/doc/en/developer/index.rst b/doc/en/developer/index.rst index 00fa4f69c44e0bd99c0e9097e94c8256ed745762..6fb96d1b6b44cf405bcf97346ba18c458bec0af3 100644 --- a/doc/en/developer/index.rst +++ b/doc/en/developer/index.rst @@ -7,4 +7,5 @@ Developer writemodule writewidget + writestreamconnector translatecentreon diff --git a/doc/en/developer/writestreamconnector.rst b/doc/en/developer/writestreamconnector.rst new file mode 100644 index 0000000000000000000000000000000000000000..01d2210956d9f478e5656e65323b0b12d2550634 --- /dev/null +++ b/doc/en/developer/writestreamconnector.rst @@ -0,0 +1,901 @@ +=============================== +How to write a Stream Connector +=============================== + +******** +Overview +******** + +Centreon Stream Connector is a feature introduced in Centreon 3.4.6. It allows +one to export Centreon data (events and metrics) to an external storage or +application such as ElasticSearch, Splunk, InfluxDB, files, etc. + +In a Centreon platform, the component that carries information between the +remote pollers and the Centreon central server is called Centreon Broker. This +broker stores received data into the Centreon local storage: MariaDB and +RRDtool. + +The following diagram explains the transfer of collected data and insertion into +storages: + +.. image:: /_static/images/developer/lua/archi_broker_regular.png + :align: center + :scale: 65% + +The Stream Connector functionality is a new Centreon Broker output getting data +from Centreon Broker Master (also known as Centreon Broker SQL) to aggregate and +forward it to external storage: + +.. image:: /_static/images/developer/lua/archi_broker_stream.png + :align: center + :scale: 65% + +This output loads a Lua script called a Stream Connector, which job is to +handle, aggregate and enrich the data before forwarding it to the defined +protocol: + +.. image:: /_static/images/developer/lua/archi_broker_lua_script.png + :align: center + :scale: 65% + +Because it is an output of Centreon Broker, the principle of creating retention +files upon interrupting external storage access is retained. In the same way, +it is possible to filter input on the categories of flow to handle. + +************ +Requirements +************ + +To use the Centreon Stream connector functionality you need to update your Centreon +platform to Centreon 3.4.6: + +* Centreon Web >= 2.8.18 +* Centreon Broker >= 3.0.13 +* Lua >= 5.1.x + +************************* +Creating a new Lua script +************************* + +The complete technical documentation is available `here <https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html>`_. +In this how-to, we will write two scripts: + +* The first one, easy, that explains the basics of Stream Connectors. Its goal + is to export data to a log file. +* The second one is more exigent for the reader ; it exports performance data + to the TSDB InfluxDB but is easily adaptable to export to another TSDB. + +Programming language +==================== + +Centreon chose the Lua programming language to let you handle, aggregate and +transfer data. Lua is a programming language that is easy to use. You can find +more information with the `Lua official documentation <https://www.lua.org/docs.html>`_ + +Storage of Lua scripts +====================== +Broker's Lua scripts can be stored in any directory readable by the +**centreon-broker** user. + +We recommend to store them in **/usr/share/centreon-broker/lua**. + +.. note:: + In a near future, this directory will be in the *default path* of the Lua + scripts launched by broker. It will then be easier to use user defined + Lua libraries because you will just have to add your libraries there like + stream connectors. + +Write all information into a file +================================= + +Store raw data +************** + +Let's start with the first script. Our goal is to store all events +given by Broker in a log file. We will call our stream connector +**bbdo2file.lua**. + +As we said previously, we will store this file into the +**/usr/share/centreon-broker/lua** directory on the Centreon central server. + +If the directory does not exist, as root, we can create it with the following +command: + +.. code-block:: bash + + mkdir -p /usr/share/centreon-broker/lua + +Centreon Broker provides several log functions to write logs, warnings or errors +into a file. We will use one of these functions *info()* to write Broker events. +`See technical documentation for more information +<https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-broker-log-object>`_. + +The function *info()* makes part of the *broker_log* object. To call it, the +syntax is the following: + +.. code-block:: lua + + broker_log:info(level, text) + +* *level* is an integer from 1 (most important) to 3 (least important). +* *text* is the text to write as log. + +.. note:: + Did you notice the separator between **broker_log** and **info**, yes it is a + colon! Objects functions, also called *methods* are called like this in Lua. + +Let's start our script. The more important function in a stream connector is +the **write()** function. Each time an event is received from a poller through +Broker, this function is called with the event as an argument. + +.. note:: + You will never have to call the **write()** function by yourself, it is always + Broker's work to do so. And it would be a fault to make such a call. In other + words, there should not be any call to the **write()** function in your script. + +`See technical documentation for more information +<https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-write-function>`_. + +Here is the **bbdo2file.lua** first version: + +.. code-block:: lua + + function init(conf) + broker_log:set_parameters(3, "/var/log/centreon-broker/bbdo2file.log") + end + + function write(d) + for k,v in pairs(d) do + broker_log:info(3, k .. " => " .. tostring(v)) + end + return true + end + +.. note:: + Information about the initialization of the Broker's log function + and its parameters are given here `see technical documentation <https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-broker-log-object>`_. + +Let's explain what we are doing in this script. + +We must provide an **init()** function, it is described in the `technical documentation <https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-init-function>`_. + +This function is called during the stream connector initialization. +Here, we use it to initialize the **broker_log** object. To achieve this, +we call the **broker_log::set_parameters()** method that needs two parameters : + +* A max level (from 1 to 3). If you give 2 here, only logs of levels 1 and 2 + will be returned. +* A file to write the logs in. This file must be in a writable directory for + the **centreon-broker** user. + +The second function is the **write()** function. We already said its argument +is a Broker event. This type of object is a collection of keys/values. For example: + +.. code-block:: json + + { + "check_hosts_freshness": false, + "active_host_checks": true, + "category": 1, + "event_handlers": true, + "instance_id": 1, + "last_command_check": 1522836592, + "type": 65552, + "global_service_event_handler": "", + "obsess_over_services": false, + "passive_service_checks": true, + "last_alive": 1522836593, + "active_service_checks": true, + "check_services_freshness": true, + "flap_detection": false, + "global_host_event_handler": "", + "notifications": true, + "obsess_over_hosts": false, + "passive_host_checks": true, + "element": 16 + } + +In all events, you will find *category*, *element* and *type*. + +* Information about the *category* can be found `here in the bbdo documentation <https://documentation.centreon.com/docs/centreon-broker/en/latest/dev/bbdo.html#event-categories>`_ +* The *element* is the *sub-category* (also called *type* in the bbdo + documentation). +* The *type* is a number built from the *category* and the *element* (binary + concatenation). + +In this example, the *category* is 1 and the *element* is 16. So, by reading +the documentation, we can say this event is a NEB event with sub-category +*instance-status*. + +To finish with the **write()** function, we make a loop on the **d** event +parameters. For each step, *k* is a key and *v* is the corresponding value. +And we send to the log file a string `k .. " => " .. tostring(v)` that means +the *concatenation* of *k*, *=>* and *v* converted into a string. You will see +an example of the result below. + +Another possibility would be to use the **broker.json_encode(d)** function that +converts any Lua object to a *json* string representation of it. So, we could +write the function like this: + +.. code-block:: lua + + function write(d) + broker_log:info(3, broker.json_encode(d)) + return true + end + +.. note:: + + You can notice that **broker.json_encode(d)** is made of **broker** and + **json_encode(d)** separated by a *dot* and not a *colon*. This is because + **broker** is not a Lua object. In fact, you can see it as a functions set + provided by *Centreon Broker*. + +Once your file **/usr/share/centreon-broker/lua/bbdo2file.lua** is ready, verify +it is readable by the **centreon-broker** user (or the **centreon-engine** +user who is the owner of the **centreon-broker** group), if it is not the case, +as root you can enter:: + + # chown centreon-engine:centreon-engine /usr/share/centreon-broker/lua/bbdo2file.lua + +Then configure the new output into Centreon Web interface in +**Configuration > Pollers > Broker configuration > Central Broker**. In **Output** +tab select **Generic – Stream connector** and click **Add**: + +.. image:: /_static/images/developer/lua/add_stream_connector.png + :align: center + +Define the name of this output and the path to the Lua connector: + +.. image:: /_static/images/developer/lua/describe_output.png + :align: center + +Then click **Save** and go to generate the configuration and restart **cbd**. + +Once the Centreon Broker will be restarted on your Centreon central server, data +will appear in your **/var/log/centreon-broker/bbdo2file.log** log file:: + + mer. 28 mars 2018 14:27:35 CEST: INFO: flap_detection => true + mer. 28 mars 2018 14:27:35 CEST: INFO: enabled => true + mer. 28 mars 2018 14:27:35 CEST: INFO: host_id => 102 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_time_ok => 1522240053 + mer. 28 mars 2018 14:27:35 CEST: INFO: state => 0 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_update => 1522240054 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_check => 1522240053 + mer. 28 mars 2018 14:27:35 CEST: INFO: execution_time => 0.005025 + mer. 28 mars 2018 14:27:35 CEST: INFO: acknowledged => false + mer. 28 mars 2018 14:27:35 CEST: INFO: service_id => 778 + mer. 28 mars 2018 14:27:35 CEST: INFO: active_checks => true + mer. 28 mars 2018 14:27:35 CEST: INFO: notify => false + mer. 28 mars 2018 14:27:35 CEST: INFO: max_check_attempts => 3 + mer. 28 mars 2018 14:27:35 CEST: INFO: obsess_over_service => true + mer. 28 mars 2018 14:27:35 CEST: INFO: check_type => 0 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_hard_state_change => 1522165654 + mer. 28 mars 2018 14:27:35 CEST: INFO: category => 1 + mer. 28 mars 2018 14:27:35 CEST: INFO: perfdata => used=41986296644o;48103633715;54116587930;0;60129542144 size=60129542144o + mer. 28 mars 2018 14:27:35 CEST: INFO: check_interval => 5 + mer. 28 mars 2018 14:27:35 CEST: INFO: output => Disk /var - used : 39.10 Go - size : 56.00 Go - percent : 69 % + mer. 28 mars 2018 14:27:35 CEST: INFO: check_command => check-bench-disk + mer. 28 mars 2018 14:27:35 CEST: INFO: check_period => 24x7 + mer. 28 mars 2018 14:27:35 CEST: INFO: type => 65560 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_hard_state => 0 + +.. note:: + This log file will grow quickly, do not forget to add a log rotate. + +Use parameters +************** + +The Centreon Broker log functions should be used for log only. To write into a +file, we must use the Lua dedicated function. Moreover, it is possible to use +parameters to define the name of the log file. + +So it is time to improve our Stream Connector: + +.. code-block:: lua + + function init(conf) + logFile = conf['logFile'] + broker_log:set_parameters(3, "/var/log/centreon-broker/debug.log") + end + + function write(d) + for k,v in pairs(d) do + writeIntoFile(k .. " => " .. tostring(v) .. "\n") + end + return true + end + + function writeIntoFile(output) + local file,err = io.open(logFile, 'a') + if file == nil then + broker_log:info(3, "Couldn't open file: " .. err) + else + file:write(output) + file:close() + end + end + +Did you notice that expression `local file,err = io.open(logFile, 'a')`? + +Lua is able to store several variables at the same time. Also, Lua functions can +return several variables! + +For example, if you want to swap variables *a* and *b*, you can enter: + +.. code-block:: lua + + a, b = b, a + +Another example that illustrates several values returned: + +.. code-block:: lua + + function fib(a, b) + return b, a + b + end + +So, this call to **io.open** returns two variables, a first variable +**file** that is a *file descriptor* used to access the file and a second +variable not always defined that contains error if one occurs or **nil** +(not defined) otherwise. + +The **init()** function allows to get parameters and define these from Centreon +web interface. See technical documentation for more information. Here, we add +the possibility to choose the destination file name. The **conf** table has +a key *logFile* defined in the web interface. The corresponding value is +the file name used to store events. + +Edit your Broker output to declare this parameter: + +.. image:: /_static/images/developer/lua/add_parameter.png + :align: center + +It is important that the name of the parameter in the web interface matches the +key name in the **conf** table. Here, it is *logFile*. + +Then click **Save** and go to generate the configuration and restart **cbd**. + +Data are stored into **/var/log/centreon-broker/bbdo2file.log** log file as +this:: + + name => error + category => 3 + interval => 300 + rrd_len => 3456000 + value => 0 + value_type => 0 + type => 196612 + ctime => 1522315660 + index_id => 4880 + element => 4 + state => 0 + category => 3 + interval => 300 + rrd_len => 3456000 + is_for_rebuild => false + service_id => 1056 + type => 196609 + ctime => 1522315660 + host_id => 145 + element => 1 + is_for_rebuild => false + metric_id => 11920 + +Manipulate data +*************** + +Here, we continue to improve our stream connector by choosing what events to +export and also by improving outputs. + +We will select only the NEB category and the events regarding hosts and +services status. + +We know that NEB is the category 1, also service status is the sub-category 24, +whereas host status is the sub-category 14. + +So, only events with the following criteria: + +* category = 1 +* element = 14 or element = 24 + +are interesting for us. + +Moreover, we would prefer to have a host name instead of a host id and a service +description instead of a service id. + +At last, we would be interested to get status information and outputs. + +NEB Events with elements 14 and 24 give almost all we want except host names and +service descriptions. + +To get those two information, we will have to use the **broker_cache** object. +This one is filled when pollers are restarted or reloaded. So, do not forget +to restart your pollers if you want something in your **broker_cache** object! + +If the cache is well filled, it is easy to get a host name from the host id:: + + broker_cache:get_hostname(host_id) + +And it is also easy to get the service description from the host id and service +id:: + + broker_cache:get_service_description(host_id, service_id) + +To install the filter on events, there is a useful function called **filter()** +that takes two parameters into account: *category*, *element*. + +This function, if defined, is called just before **write()**. If it returns +**true**, the **write()** function will be called, otherwise, the event will +be thrown away. + +Let's complete our Lua script: + +.. code-block:: lua + + function init(conf) + logFile = conf['logFile'] + broker_log:set_parameters(3, "/var/log/centreon-broker/debug.log") + end + + function write(d) + local output = "" + + local host_name = broker_cache:get_hostname(d.host_id) + if not host_name then + broker_log:info(3, "Unable to get name of host, please restart centengine") + host_name = d.host_id + end + + if d.element == 14 then + output = "HOST:" .. host_name .. ";" .. d.host_id .. ";" .. d.state .. ";" .. d.output + writeIntoFile(output) + broker_log:info(output) + elseif d.element == 24 then + local service_description = broker_cache:get_service_description(d.host_id, d.service_id) + if not service_description then + broker_log:info(3, "Unable to get description of service, please restart centengine") + service_description = d.service_id + end + output = "SERVICE:" .. host_name .. ";" .. d.host_id .. ";" .. service_description .. ";" .. d.service_id .. ";" .. d.state .. ";" .. d.output + writeIntoFile(output) + broker_log:info(output) + end + return true + end + + function filter(category, element) + -- Get only host status and services status from NEB category + if category == 1 and (element == 14 or element == 24) then + return true + end + return false + end + + local function writeIntoFile(output) + local file,err = io.open(logFile, 'a') + if file == nil then + broker_log:info(3, "Couldn't open file: " .. err) + else + file:write(output) + file:close() + end + end + +Just several remarks on this new script before showing what we get. + +In the **init()** function, we access the *logFile* key in the *conf* table +by using `conf['logFile']`. Whereas, in the **write()** function, we access +the *element* key in the *d* table by using `d.element`... + +In fact, the two syntaxes are allowed : `d.element` is the same value than +`d['element']`. + +Another remark, in the **write()** function we can see something like:: + + if not host_name then + +And in the **writeIntoFile()** function, we can see that:: + + if file == nil then + +Do they mean the same thing? Where is the difference? + +You must know that in Lua, a variable is considered to be **true** if it is +defined and not **false**: + +so, the following code + +.. code:: lua + + if toto then + print("Good") + else + print("Bad") + end + +will write *Good* if *toto* is defined and not **false**. More precisely, it will +write *Good* in the following cases: + +* toto=12 +* toto=true +* toto="A string" +* toto=0 (surprising!) + +It will write *Bad* in these cases: + +* toto=nil (by default a variable is nil, which means not defined) +* toto=false + +The **/var/log/centreon-broker/bbdo2file.log** file will now contain:: + + HOST:srv-DC-djakarta;215;0;OK - srv-DC-djakarta: rta 0.061ms, lost 0% + SERVICE:mail-titan-gateway;92;disk-/usr;623;0;Disk /usr - used : 42.98 Go - size : 142.00 Go - percent : 30 % + SERVICE:mail-sun-master;87;memory-stats;535;0;Memory usage (Total 13.0GB): 0.12GB [buffer:0.00GB] [cache:0.01GB] [pages_tables:0.00GB] [mapped:0.00GB] [active:0.07GB] [inactive:0.00GB] [apps:0.02GB] [unused:12.88GB] + SERVICE:mail-saturn-frontend;86;traffic-eth1;512;0;Traffic In : 4.73 Mb/s (4.73 %), Out : 4.79 Mb/s (4.79 %) - Total RX Bits In : 396.01 Gb, Out : 393.88 Gb + SERVICE:mail-saturn-frontend;86;memory-stats;515;0;Memory usage (Total 16.0GB): 8.89GB [buffer:0.43GB] [cache:0.95GB] [pages_tables:0.27GB] [mapped:0.15GB] [active:3.92GB] [inactive:0.29GB] [apps:2.88GB] [unused:7.11GB] + SERVICE:mail-neptune-frontend;80;traffic-eth1;392;0;Traffic In : 4.82 Mb/s (4.82 %), Out : 6.48 Mb/s (6.48 %) - Total RX Bits In : 398.40 Gb, Out : 396.44 Gb + HOST:srv-DC-casablanca;207;0;OK - srv-DC-casablanca: rta 2.042ms, lost 0% + SERVICE:mail-neptune-frontend;80;memory-stats;395;0;Memory usage (Total 9.0GB): 0.54GB [buffer:0.03GB] [cache:0.00GB] [pages_tables:0.01GB] [mapped:0.00GB] [active:0.48GB] [inactive:0.00GB] [apps:0.01GB] [unused:8.46GB] + SERVICE:mail-mercury-frontend;82;traffic-eth1;432;0;Traffic In : 8.28 Mb/s (8.28 %), Out : 1.23 Mb/s (1.23 %) - Total RX Bits In : 397.71 Gb, Out : 400.34 Gb + SERVICE:mail-mercury-frontend;82;memory-stats;435;0;Memory usage (Total 12.0GB): 1.58GB [buffer:0.00GB] [cache:0.63GB] [pages_tables:0.00GB] [mapped:0.00GB] [active:0.75GB] [inactive:0.00GB] [apps:0.19GB] [unused:10.42GB] + SERVICE:mail-mars-frontend;84;traffic-eth1;472;0;Traffic In : 7.24 Mb/s (7.24 %), Out : 3.36 Mb/s (3.36 %) - Total RX Bits In : 399.93 Gb, Out : 395.67 Gb + SERVICE:mail-mars-frontend;84;memory-stats;475;0;Memory usage (Total 3.0GB): 1.19GB [buffer:0.01GB] [cache:0.59GB] [pages_tables:0.00GB] [mapped:0.00GB] [active:0.15GB] [inactive:0.04GB] [apps:0.39GB] [unused:1.81GB] + SERVICE:mail-jupiter-frontend;85;traffic-eth1;492;0;Traffic In : 1.41 Mb/s (1.41 %), Out : 9.08 Mb/s (9.08 %) - Total RX Bits In : 388.86 Gb, Out : 394.85 Gb + SERVICE:mail-jupiter-frontend;85;memory-stats;495;0;Memory usage (Total 12.0GB): 0.57GB [buffer:0.04GB] [cache:0.23GB] [pages_tables:0.02GB] [mapped:0.02GB] [active:0.07GB] [inactive:0.03GB] [apps:0.16GB] [unused:11.43GB] + SERVICE:mail-io-backend;88;traffic-eth1;547;0;Traffic In : 1.51 Mb/s (1.51 %), Out : 7.12 Mb/s (7.12 %) - Total RX Bits In : 389.61 Gb, Out : 390.54 Gb + SERVICE:mail-io-backend;88;diskio-system;551;0;Device /dev/sda: avg read 4.78 (MB/s) and write 9.08 (MB/s) + + +*********************************** +Export performance data to InfluxDB +*********************************** + +Now, you have already seen many things about stream connectors. It is time to +create something more useful! + +`InfluxDB <https://www.influxdata.com/>`_ is a Time Series database. We will use +this storage to insert performance data collected by the Centreon platform. For +this example, we will use the predefined `InfluxDB Docker <https://hub.docker.com/_/influxdb/>`_. + +To send data to InfluxDB, we need parameters to access to InfluxDB storage: + +* **http_server_address**: IP address of the storage +* **http_server_port**: 8086 by default +* **http_server_protocol**: http or https +* **influx_database**: name of database +* **influx_user**: user to access to database if defined +* **influx_password**: password of user to access to database if defined + +In order to not saturate the storage, we will add all events in a queue and +once its max size is reached, we will send data by bulk. + +We need to define the size of the queue and the maximum +delay before sending events: + +* max_buffer_size +* max_buffer_age + +To create this queue, we introduce a code a little more complicated. We +construct an object **event_queue**. It is composed of parameters such as +*events*, *influx_database* and methods like *new()*, *add()*. + +To understand how to create such an object in Lua, we recommend the Lua +documentation `here for classes <https://www.lua.org/pil/16.1.html>`_ +and `there for metatables <https://www.lua.org/pil/13.html>`_. + +To send data to a server, we provide a **broker_tcp_socket** object. + +Its API is very simple (too simple?). This *socket* +is a TCP socket, it does not support encryption and it can be tricky to send +data in http. Here is an example: + +.. code-block:: lua + + -- Here, we create our socket + local socket = broker_tcp_socket.new() + + -- We establish the connection with the server + socket:connect(address, port) + + -- Now, we can send data + socket:write("This is a text to send") + + -- If, we want an answer, we also have a function to read + local content = socket:read() + + -- When exchanges are finished, we can close the socket + socket:close() + +For our purpose, we do not use **broker_tcp_socket** because of its limitations. +We want to be able to send data to an https server. + +A prerequisite is to install the `lua-socket library <http://w3.impa.br/~diego/software/luasocket/>`_. This library provides several functionalities, we +need two of them: + +* http socket +* ltn12 + +To access them, Lua provides the **require** function. + +Let's introduce the beginning of our new Stream Connector. + +The queue parameters +==================== + +.. code-block:: lua + + -- We declare the objects to import here + local http = require("socket.http") + local ltn12 = require("ltn12") + + -- Here are predefined queue parameters + local event_queue = { + __internal_ts_last_flush = nil, + http_server_address = "", + http_server_port = 8086, + http_server_protocol = "http", + events = {}, + influx_database = "mydb", + influx_user = "", + influx_password = "", + max_buffer_size = 5000, + max_buffer_age = 5 + } + + +In this table, we give default values to parameters that can possibly +be changed during the **init()** call. This table will be used to store important +data for the script and is also our queue object. + +A method to create the queue +============================ + +To declare this table as a Lua object, we need a constructor. So, here it is: + +.. code-block:: lua + + -- Constructor of the event_queue + function event_queue:new(o, conf) + o = o or {} + setmetatable(o, self) + self.__index = self + for i,v in pairs(conf) do + if self[i] and i ~= "events" and string.sub(i, 1, 11) ~= "__internal_" then + broker_log:info(1, "event_queue:new: getting parameter " .. i .. " => " .. v) + self[i] = v + else + broker_log:warning(1, "event_queue:new: ignoring parameter " .. i .. " => " .. v) + end + end + self.__internal_ts_last_flush = os.time() + broker_log:info(2, "event_queue:new: setting the internal timestamp to " .. self.__internal_ts_last_flush) + return o + end + +.. note:: + In this function, we use a Lua sugar "o = o or {}" that means *o* stays the + same if it is **true**, otherwise it is affected with an empty table `{}`. + + Another point to notice is the **~=** operator that means **different from**. + + And to finish on this function, the variable **self** is implicitly defined + when we declare an object's method. Its meaning is the same as **this** in + Java or in C++. It represents the object we are working on. + +A method to add event in queue +============================== + +We have a queue object. It would be great to use it like this: + +.. code-block:: lua + + -- We construct it + local queue = event_queue:new(nil, conf) + + -- We add an event to it + queue:add(event) + + -- When the queue is full, we would like to do something like this + queue:flush() + + +Let's do it! Below, we present an **add()** method that retrieves a host name +and service description from the cache, builds a string from the event and +pushes it on its stack. + +.. code-block:: lua + + function event_queue:add(e) + local metric = e.name + -- time is a reserved word in influxDB so I rename it + if metric == "time" then + metric = "_" .. metric + end + + -- retrieve objects names instead of IDs + local host_name = broker_cache:get_hostname(e.host_id) + local service_description = broker_cache:get_service_description(e.host_id, e.service_id) + + -- what if we could not get them from cache + if not host_name then + broker_log:warning(1, "event_queue:add: host_name for id " .. e.host_id .. " not found. Restarting centengine should fix this.") + host_name = e.host_id + end + if not service_description then + broker_log:warning(1, "event_queue:add: service_description for id " .. e.host_id .. "." .. e.service_id .. " not found. Restarting centengine should fix this.") + service_description = e.service_id + else + service_description = service_description:gsub(" ", "_") + end + + -- we finally append the event to the events table + metric = metric:gsub(" ", "_") + broker_log:info(3, 'event_queue:add: adding ' .. service_description .. ",host=" .. host_name .. " " .. metric .. "=" .. e.value .. " " .. e.ctime .. '000000000" to event list.') + self.events[#self.events + 1] = service_description .. ",host=" .. host_name .. " " .. metric .. "=" .. e.value .. " " .. e.ctime .. "000000000\n" + + -- then we check whether it is time to send the events to the receiver and flush + if #self.events >= self.max_buffer_size then + broker_log:info(2, "event_queue:add: flushing because buffer size reached " .. self.max_buffer_size .. " elements.") + self:flush() + return true + elseif os.time() - self.__internal_ts_last_flush >= self.max_buffer_age then + broker_log:info(2, "event_queue:add: flushing " .. #self.events .. " elements because buffer age reached " .. (os.time() - self.__internal_ts_last_flush) .. "s and max age is " .. self.max_buffer_age .. "s.") + self:flush() + return true + else + return false + end + end + +A method to flush the queue +=========================== + +Once the events added in the queue and the maximum size of the queue or the +timeout is reached, events will be sent to the InfluxDB storage. + +This function builds data from the queue and sends them to the storage. If an +error occurs, it dumps a log error. + +It is here that we use the **http** and **ltn12** objects loaded at the +beginning of the script. + +.. code-block:: lua + + function event_queue:flush() + broker_log:info(2, "event_queue:flush: Concatenating all the events as one string") + -- we concatenate all the events + local http_post_data = "" + local http_result_body = {} + for i, raw_event in ipairs(self.events) do + http_post_data = http_post_data .. raw_event + end + broker_log:info(2, 'event_queue:flush: HTTP POST request "' .. self.http_server_protocol .. "://" .. self.http_server_address .. ":" .. self.http_server_port .. "/write?db=" .. self.influx_database .. '"') + broker_log:info(3, "event_queue:flush: HTTP POST data are: '" .. http_post_data .. "'") + + -- build url + local influxdb_url = self.http_server_protocol .. "://" .. self.http_server_address .. ":" .. self.http_server_port .. "/write?db=" .. self.influx_database + -- add authentication if needed + if string.len(self.influx_user) >= 1 and string.len(self.influx_password) >= 1 then + influxdb_url = influxdb_url .. "&u=" .. self.influx_user .. "&p="..self.influx_password + end + + local hr_result, hr_code, hr_header, hr_s = http.request{ + url = influxdb_url, + method = "POST", + -- sink is where the request result's body will go + sink = ltn12.sink.table(http_result_body), + -- request body needs to be formatted as a LTN12 source + source = ltn12.source.string(http_post_data), + headers = { + -- mandatory for POST request with body + ["content-length"] = string.len(http_post_data) + } + } + -- Handling the return code + if hr_code == 204 then + broker_log:info(2, "event_queue:flush: HTTP POST request successful: return code is " .. hr_code) + else + broker_log:error(1, "event_queue:flush: HTTP POST request FAILED: return code is " .. hr_code) + for i, v in ipairs(http_result_body) do + broker_log:error(1, "event_queue:flush: HTTP POST request FAILED: message line " .. i .. ' is "' .. v .. '"') + end + end + + -- now that the data has been sent, we empty the events array + self.events = {} + -- and update the timestamp + self.__internal_ts_last_flush = os.time() + end + +The init() function to get parameters and create the queue +========================================================== + +In this case, the **init()** function creates the queue with parameters +defined by users in the web interface or uses default parameters already +defined in the queue. This alternative is managed by the queue constructor. + +.. code-block:: lua + + function init(conf) + broker_log:set_parameters(1, "/var/log/centreon-broker/stream-connector-influxdb.log") + broker_log:info(2, "init: Beginning init() function") + queue = event_queue:new(nil, conf) + broker_log:info(2, "init: Ending init() function, Event queue created") + end + +.. note:: + + **queue** is not defined as local, this is important so that it is accessible + from all the functions. + +The write() function to insert events in queue +============================================== + +The **write()** function is only used to insert filtered events into the queue: + +.. code-block:: lua + + function write(e) + broker_log:info(3, "write: Beginning write() function") + queue:add(e) + broker_log:info(3, "write: Ending write() function\n") + return true + end + +The filter() function to select only performance data events +============================================================ + +To select only performance data, we need to select *category* 3 (“Storage”) +and *element* 1 for *metric*: + +.. code-block:: lua + + function filter(category, element) + if category == 3 and element == 1 then + return true + end + return false + end + +Complete script +=============== + +The complete script can be downloaded `here <https://github.com/centreon/centreon-stream-connector-scripts/tree/master/influxdb>`_. + +Configure Centreon Broker +========================= + +Configure the new output into Centreon Web interface in +**Configuration > Pollers > Broker configuration > Central Broker**. +In **Output** tab select **Generic – Stream connector** and click **Add**: + +.. image:: /_static/images/developer/lua/add_stream_connector.png + :align: center + +Define the name of this output and the path to the Lua connector: + +.. image:: /_static/images/developer/lua/broker_influxdb_output.png + :align: center + :scale: 65% + +Then click **Save** and go to generate the configuration and restart **cbd**. + +.. note:: + Don’t forget to restart “centengine” too to create the Centreon Broker cache. + +If you install the `Grafana <https://grafana.com/>`_ dashboard, you can visualize the stored data: + +.. image:: /_static/images/developer/lua/visualize_data_grafana.png + :align: center + :scale: 65% + +Discover other Centreon Stream Connectors +========================================= + +Centreon provides a Github repository to host Lua scripts developed by Centreon +and the community. Please go to the `dedicated Github <http://github.com/centreon/centreon-stream-connector-scripts>`_. + +Need help to develop your Stream connector? You want to share your experience with +the community? Join the `Centreon community Slack channel <https://centreon.github.io/>`_. + diff --git a/doc/en/release_notes/centreon-2.8/centreon-2.8.20.rst b/doc/en/release_notes/centreon-2.8/centreon-2.8.20.rst index c95b30b0b272acfe8475c0c9301c097fa9f03026..b45a4a2fe55a259f0ceaa035ef907c62dcaf8b95 100644 --- a/doc/en/release_notes/centreon-2.8/centreon-2.8.20.rst +++ b/doc/en/release_notes/centreon-2.8/centreon-2.8.20.rst @@ -5,6 +5,28 @@ Centreon Web 2.8.20 Enhancements ============ +* [API] Add default poller - PR #6098 +* [API] Link host with default poller if unknown poller - PR #6099 +* [ACL] Improve performance - #6056 PR #6107 +* [Documentation] Improve Centreon CLAPI usage - PR #6090 #6091 +* [Documentation] Improve documentation to add a new poller - #6075 PR #6086 +* [Documentation] Add notice for 64 bits support only - PR #6101 +* [Monitoring] Display links in output and comments - #5943 PR #6113 + Bug Fixes ========= +* [ACL] Allow nested groups filter in ldap configuration - #6127 PR #6128 +* [API] Export specific service, add host before service in CLAPI - PR #6100 +* [API] CLAPI add resource export filter - PR #6125 +* [API] CLAPI Export contact with contact group - PR #6131 +* [API] CLAPI Export service categories - PR #6134 +* [Configuration] SNMP trap poller generation uses ACL - #6043 PR #6069 +* [Custom Views] Fix share custom view - PR #6109 +* [Poller Stats] Poller Statistics Graphs are displayed in first column only - #6003 PR #6122 + +Others +====== + +* Update copyright date on the login page - PR #6076 +* Remove multiple debug in Centreon - PR #6138 \ No newline at end of file diff --git a/doc/en/release_notes/centreon-2.8/centreon-2.8.21.rst b/doc/en/release_notes/centreon-2.8/centreon-2.8.21.rst index a74c0badd68c52365df1eaeafacba1f4e9262211..cc9ffa5012ae1c2a7ee71e299b291875b97ec8d8 100644 --- a/doc/en/release_notes/centreon-2.8/centreon-2.8.21.rst +++ b/doc/en/release_notes/centreon-2.8/centreon-2.8.21.rst @@ -5,5 +5,17 @@ Centreon Web 2.8.21 Enhancements ============ +* [Documentation] Add chapter about how to write a stream connector - PR #6189 +* [API] Separate REST API configuration and REST API realtime access - PR #6188 + Bug Fixes ========= + +* [ACL] Manage filters (poller, host, service) on servicegroup - PR #6163 +* [Configuration] Fix output stream connector name for fresh install - PR #6159 #6182 +* [Configuration] No "Conf changed" flag set to "yes" when deploying services to selected hosts - #6160 PR #6191 + +Other +===== + +* Fix php warning in realtime host API - PR #6174 \ No newline at end of file diff --git a/doc/en/release_notes/centreon-2.8/centreon-2.8.22.rst b/doc/en/release_notes/centreon-2.8/centreon-2.8.22.rst new file mode 100644 index 0000000000000000000000000000000000000000..2cf9f9b79db2e7df6b1ef29d5496040ca2dfecde --- /dev/null +++ b/doc/en/release_notes/centreon-2.8/centreon-2.8.22.rst @@ -0,0 +1,14 @@ +################### +Centreon Web 2.8.22 +################### + +Enhancements +============ + +Bug Fixes +========= + +* [CLAPI] Fix host services deployment - PR #6212 + +Other +===== diff --git a/doc/en/release_notes/centreon-2.8/centreon-2.8.23.rst b/doc/en/release_notes/centreon-2.8/centreon-2.8.23.rst new file mode 100644 index 0000000000000000000000000000000000000000..603bb166a422992225583595a3c305ce05eefa19 --- /dev/null +++ b/doc/en/release_notes/centreon-2.8/centreon-2.8.23.rst @@ -0,0 +1,32 @@ +################### +Centreon Web 2.8.23 +################### + +Enhancements +============ + +* [API] Add default poller - PR #6098 +* [API] Link host with default poller if unknown poller - PR #6099 +* [ACL] Improve performance - #6056 PR #6107 +* [Documentation] Improve Centreon CLAPI usage - PR #6090 #6091 +* [Documentation] Improve documentation to add a new poller - #6075 PR #6086 +* [Documentation] Add notice for 64 bits support only - PR #6101 +* [Monitoring] Display links in output and comments - #5943 PR #6113 + +Bug Fixes +========= + +* [ACL] Allow nested groups filter in ldap configuration - #6127 PR #6128 +* [API] Export specific service, add host before service in CLAPI - PR #6100 +* [API] CLAPI add resource export filter - PR #6125 +* [API] CLAPI Export contact with contact group - PR #6131 +* [API] CLAPI Export service categories - PR #6134 +* [Configuration] SNMP trap poller generation uses ACL - #6043 PR #6069 +* [Custom Views] Fix share custom view - PR #6109 +* [Poller Stats] Poller Statistics Graphs are displayed in first column only - #6003 PR #6122 + +Others +====== + +* Update copyright date on the login page - PR #6076 +* Remove multiple debug in Centreon - PR #6138 diff --git a/doc/en/release_notes/centreon-2.8/index.rst b/doc/en/release_notes/centreon-2.8/index.rst index 845a9edebbf7f1848f0c03682b431022ff5054c9..1a40f83ac3110f7a40f706bb6b03d0647992f5e9 100644 --- a/doc/en/release_notes/centreon-2.8/index.rst +++ b/doc/en/release_notes/centreon-2.8/index.rst @@ -28,3 +28,5 @@ Please find here the release notes dedicated to the last 2.8.x version of Centre centreon-2.8.19 centreon-2.8.20 centreon-2.8.21 + centreon-2.8.22 + centreon-2.8.23 diff --git a/doc/fr/_static/images/developper/lua/add_parameter.png b/doc/fr/_static/images/developper/lua/add_parameter.png new file mode 100644 index 0000000000000000000000000000000000000000..453b84cb0f21ceb7b0cd55a76e95b9fd34c81785 Binary files /dev/null and b/doc/fr/_static/images/developper/lua/add_parameter.png differ diff --git a/doc/fr/_static/images/developper/lua/add_stream_connector.png b/doc/fr/_static/images/developper/lua/add_stream_connector.png new file mode 100644 index 0000000000000000000000000000000000000000..1e5b27c08a7e98de5807f5b30fafeb7be18daac6 Binary files /dev/null and b/doc/fr/_static/images/developper/lua/add_stream_connector.png differ diff --git a/doc/fr/_static/images/developper/lua/archi_broker_lua_script.png b/doc/fr/_static/images/developper/lua/archi_broker_lua_script.png new file mode 100644 index 0000000000000000000000000000000000000000..ffaa5b94b156bf5995967dd3ac30b69d0555f712 Binary files /dev/null and b/doc/fr/_static/images/developper/lua/archi_broker_lua_script.png differ diff --git a/doc/fr/_static/images/developper/lua/archi_broker_regular.png b/doc/fr/_static/images/developper/lua/archi_broker_regular.png new file mode 100644 index 0000000000000000000000000000000000000000..470a08ec12665be407fecb6c10883f45be5e06a9 Binary files /dev/null and b/doc/fr/_static/images/developper/lua/archi_broker_regular.png differ diff --git a/doc/fr/_static/images/developper/lua/archi_broker_stream.png b/doc/fr/_static/images/developper/lua/archi_broker_stream.png new file mode 100644 index 0000000000000000000000000000000000000000..364e73e60eaac84e40b24ed7d8db221707d980e6 Binary files /dev/null and b/doc/fr/_static/images/developper/lua/archi_broker_stream.png differ diff --git a/doc/fr/_static/images/developper/lua/broker_influxdb_output.png b/doc/fr/_static/images/developper/lua/broker_influxdb_output.png new file mode 100644 index 0000000000000000000000000000000000000000..857d23dc7b210591013f583e1c9cb6286e3ed255 Binary files /dev/null and b/doc/fr/_static/images/developper/lua/broker_influxdb_output.png differ diff --git a/doc/fr/_static/images/developper/lua/describe_output.png b/doc/fr/_static/images/developper/lua/describe_output.png new file mode 100644 index 0000000000000000000000000000000000000000..16270525de72786ac68207b511755fa8b6bef71b Binary files /dev/null and b/doc/fr/_static/images/developper/lua/describe_output.png differ diff --git a/doc/fr/_static/images/developper/lua/visualize_data_grafana.png b/doc/fr/_static/images/developper/lua/visualize_data_grafana.png new file mode 100644 index 0000000000000000000000000000000000000000..e09321dcd3d5f8170868ecaf21e642cc70892eea Binary files /dev/null and b/doc/fr/_static/images/developper/lua/visualize_data_grafana.png differ diff --git a/doc/fr/api/api_rest/index.rst b/doc/fr/api/api_rest/index.rst index 3ee977556796ff183d3abdfcc58173088c7806d9..88cf723750017f90daac93fef07e3c44ab1a12cd 100644 --- a/doc/fr/api/api_rest/index.rst +++ b/doc/fr/api/api_rest/index.rst @@ -17,9 +17,18 @@ Permissions ----------- To perform API calls using a specific Centreon user, you need permissions to do so. -You have to edit user settings on the menu **Configuration > Users > Contacts/Users**, -edit user and on second tab check box **Reach API**. +There are two types of permission: + +You can give access to the configuration for a specific Centreon user. To do so you have +to edit user settings on the menu **Configuration > Users > Contacts/Users**, +edit user and on second tab check box **Reach API Configuration**. + +You can give access to the realtime for a specific Centreon user. To do so you have +to edit user settings on the menu **Configuration > Users > Contacts/Users**, +edit user and on second tab check box **Reach API Realtime**. + +If you want both then check **both** checkboxes Authentication ---------------- diff --git a/doc/fr/configuration_guide/deploy.rst b/doc/fr/configuration_guide/deploy.rst index 2f4ed375a8ffc41119f7012765ce8d8bd8044f9d..4772e5b02ee8befc74616c8dc3cea1bb960fb37f 100644 --- a/doc/fr/configuration_guide/deploy.rst +++ b/doc/fr/configuration_guide/deploy.rst @@ -16,7 +16,7 @@ Première étape #. Rendez-vous dans le menu **Configuration** ==> **Collecteurs** #. Choisissez les collecteurs sur lesquels exporter la configuration -#. Cliquez sur **Appliquez la configurartion** +#. Cliquez sur **Appliquez la configuration** .. image:: /images/guide_utilisateur/configuration/poller_menu_generate.png :align: center diff --git a/doc/fr/developper/index.rst b/doc/fr/developper/index.rst index 039539bf6be2038a061caa3baaa16f72117af73a..61ea642d1a134b1426e7d4971de4bbe099b5a026 100644 --- a/doc/fr/developper/index.rst +++ b/doc/fr/developper/index.rst @@ -9,4 +9,5 @@ Ce chapitre est une reprise sans traduction de la documentation anglaise. writemodule writewidget + writestreamconnector translatecentreon diff --git a/doc/fr/developper/writestreamconnector.rst b/doc/fr/developper/writestreamconnector.rst new file mode 100644 index 0000000000000000000000000000000000000000..cf94eb6cf83eed5946005553482c41c143a8ab16 --- /dev/null +++ b/doc/fr/developper/writestreamconnector.rst @@ -0,0 +1,901 @@ +=============================== +How to write a Stream Connector +=============================== + +******** +Overview +******** + +Centreon Stream Connector is a feature introduced in Centreon 3.4.6. It allows +one to export Centreon data (events and metrics) to an external storage or +application such as ElasticSearch, Splunk, InfluxDB, files, etc. + +In a Centreon platform, the component that carries information between the +remote pollers and the Centreon central server is called Centreon Broker. This +broker stores received data into the Centreon local storage: MariaDB and +RRDtool. + +The following diagram explains the transfer of collected data and insertion into +storages: + +.. image:: /_static/images/developper/lua/archi_broker_regular.png + :align: center + :scale: 65% + +The Stream Connector functionality is a new Centreon Broker output getting data +from Centreon Broker Master (also known as Centreon Broker SQL) to aggregate and +forward it to external storage: + +.. image:: /_static/images/developper/lua/archi_broker_stream.png + :align: center + :scale: 65% + +This output loads a Lua script called a Stream Connector, which job is to +handle, aggregate and enrich the data before forwarding it to the defined +protocol: + +.. image:: /_static/images/developper/lua/archi_broker_lua_script.png + :align: center + :scale: 65% + +Because it is an output of Centreon Broker, the principle of creating retention +files upon interrupting external storage access is retained. In the same way, +it is possible to filter input on the categories of flow to handle. + +************ +Requirements +************ + +To use the Centreon Stream connector functionality you need to update your Centreon +platform to Centreon 3.4.6: + +* Centreon Web >= 2.8.18 +* Centreon Broker >= 3.0.13 +* Lua >= 5.1.x + +************************* +Creating a new Lua script +************************* + +The complete technical documentation is available `here <https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html>`_. +In this how-to, we will write two scripts: + +* The first one, easy, that explains the basics of Stream Connectors. Its goal + is to export data to a log file. +* The second one is more exigent for the reader ; it exports performance data + to the TSDB InfluxDB but is easily adaptable to export to another TSDB. + +Programming language +==================== + +Centreon chose the Lua programming language to let you handle, aggregate and +transfer data. Lua is a programming language that is easy to use. You can find +more information with the `Lua official documentation <https://www.lua.org/docs.html>`_ + +Storage of Lua scripts +====================== +Broker's Lua scripts can be stored in any directory readable by the +**centreon-broker** user. + +We recommend to store them in **/usr/share/centreon-broker/lua**. + +.. note:: + In a near future, this directory will be in the *default path* of the Lua + scripts launched by broker. It will then be easier to use user defined + Lua libraries because you will just have to add your libraries there like + stream connectors. + +Write all information into a file +================================= + +Store raw data +************** + +Let's start with the first script. Our goal is to store all events +given by Broker in a log file. We will call our stream connector +**bbdo2file.lua**. + +As we said previously, we will store this file into the +**/usr/share/centreon-broker/lua** directory on the Centreon central server. + +If the directory does not exist, as root, we can create it with the following +command: + +.. code-block:: bash + + mkdir -p /usr/share/centreon-broker/lua + +Centreon Broker provides several log functions to write logs, warnings or errors +into a file. We will use one of these functions *info()* to write Broker events. +`See technical documentation for more information +<https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-broker-log-object>`_. + +The function *info()* makes part of the *broker_log* object. To call it, the +syntax is the following: + +.. code-block:: lua + + broker_log:info(level, text) + +* *level* is an integer from 1 (most important) to 3 (least important). +* *text* is the text to write as log. + +.. note:: + Did you notice the separator between **broker_log** and **info**, yes it is a + colon! Objects functions, also called *methods* are called like this in Lua. + +Let's start our script. The more important function in a stream connector is +the **write()** function. Each time an event is received from a poller through +Broker, this function is called with the event as an argument. + +.. note:: + You will never have to call the **write()** function by yourself, it is always + Broker's work to do so. And it would be a fault to make such a call. In other + words, there should not be any call to the **write()** function in your script. + +`See technical documentation for more information +<https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-write-function>`_. + +Here is the **bbdo2file.lua** first version: + +.. code-block:: lua + + function init(conf) + broker_log:set_parameters(3, "/var/log/centreon-broker/bbdo2file.log") + end + + function write(d) + for k,v in pairs(d) do + broker_log:info(3, k .. " => " .. tostring(v)) + end + return true + end + +.. note:: + Information about the initialization of the Broker's log function + and its parameters are given here `see technical documentation <https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-broker-log-object>`_. + +Let's explain what we are doing in this script. + +We must provide an **init()** function, it is described in the `technical documentation <https://documentation.centreon.com/docs/centreon-broker/en/latest/exploit/stream_connectors.html#the-init-function>`_. + +This function is called during the stream connector initialization. +Here, we use it to initialize the **broker_log** object. To achieve this, +we call the **broker_log::set_parameters()** method that needs two parameters : + +* A max level (from 1 to 3). If you give 2 here, only logs of levels 1 and 2 + will be returned. +* A file to write the logs in. This file must be in a writable directory for + the **centreon-broker** user. + +The second function is the **write()** function. We already said its argument +is a Broker event. This type of object is a collection of keys/values. For example: + +.. code-block:: json + + { + "check_hosts_freshness": false, + "active_host_checks": true, + "category": 1, + "event_handlers": true, + "instance_id": 1, + "last_command_check": 1522836592, + "type": 65552, + "global_service_event_handler": "", + "obsess_over_services": false, + "passive_service_checks": true, + "last_alive": 1522836593, + "active_service_checks": true, + "check_services_freshness": true, + "flap_detection": false, + "global_host_event_handler": "", + "notifications": true, + "obsess_over_hosts": false, + "passive_host_checks": true, + "element": 16 + } + +In all events, you will find *category*, *element* and *type*. + +* Information about the *category* can be found `here in the bbdo documentation <https://documentation.centreon.com/docs/centreon-broker/en/latest/dev/bbdo.html#event-categories>`_ +* The *element* is the *sub-category* (also called *type* in the bbdo + documentation). +* The *type* is a number built from the *category* and the *element* (binary + concatenation). + +In this example, the *category* is 1 and the *element* is 16. So, by reading +the documentation, we can say this event is a NEB event with sub-category +*instance-status*. + +To finish with the **write()** function, we make a loop on the **d** event +parameters. For each step, *k* is a key and *v* is the corresponding value. +And we send to the log file a string `k .. " => " .. tostring(v)` that means +the *concatenation* of *k*, *=>* and *v* converted into a string. You will see +an example of the result below. + +Another possibility would be to use the **broker.json_encode(d)** function that +converts any Lua object to a *json* string representation of it. So, we could +write the function like this: + +.. code-block:: lua + + function write(d) + broker_log:info(3, broker.json_encode(d)) + return true + end + +.. note:: + + You can notice that **broker.json_encode(d)** is made of **broker** and + **json_encode(d)** separated by a *dot* and not a *colon*. This is because + **broker** is not a Lua object. In fact, you can see it as a functions set + provided by *Centreon Broker*. + +Once your file **/usr/share/centreon-broker/lua/bbdo2file.lua** is ready, verify +it is readable by the **centreon-broker** user (or the **centreon-engine** +user who is the owner of the **centreon-broker** group), if it is not the case, +as root you can enter:: + + # chown centreon-engine:centreon-engine /usr/share/centreon-broker/lua/bbdo2file.lua + +Then configure the new output into Centreon Web interface in +**Configuration > Pollers > Broker configuration > Central Broker**. In **Output** +tab select **Generic – Stream connector** and click **Add**: + +.. image:: /_static/images/developper/lua/add_stream_connector.png + :align: center + +Define the name of this output and the path to the Lua connector: + +.. image:: /_static/images/developper/lua/describe_output.png + :align: center + +Then click **Save** and go to generate the configuration and restart **cbd**. + +Once the Centreon Broker will be restarted on your Centreon central server, data +will appear in your **/var/log/centreon-broker/bbdo2file.log** log file:: + + mer. 28 mars 2018 14:27:35 CEST: INFO: flap_detection => true + mer. 28 mars 2018 14:27:35 CEST: INFO: enabled => true + mer. 28 mars 2018 14:27:35 CEST: INFO: host_id => 102 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_time_ok => 1522240053 + mer. 28 mars 2018 14:27:35 CEST: INFO: state => 0 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_update => 1522240054 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_check => 1522240053 + mer. 28 mars 2018 14:27:35 CEST: INFO: execution_time => 0.005025 + mer. 28 mars 2018 14:27:35 CEST: INFO: acknowledged => false + mer. 28 mars 2018 14:27:35 CEST: INFO: service_id => 778 + mer. 28 mars 2018 14:27:35 CEST: INFO: active_checks => true + mer. 28 mars 2018 14:27:35 CEST: INFO: notify => false + mer. 28 mars 2018 14:27:35 CEST: INFO: max_check_attempts => 3 + mer. 28 mars 2018 14:27:35 CEST: INFO: obsess_over_service => true + mer. 28 mars 2018 14:27:35 CEST: INFO: check_type => 0 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_hard_state_change => 1522165654 + mer. 28 mars 2018 14:27:35 CEST: INFO: category => 1 + mer. 28 mars 2018 14:27:35 CEST: INFO: perfdata => used=41986296644o;48103633715;54116587930;0;60129542144 size=60129542144o + mer. 28 mars 2018 14:27:35 CEST: INFO: check_interval => 5 + mer. 28 mars 2018 14:27:35 CEST: INFO: output => Disk /var - used : 39.10 Go - size : 56.00 Go - percent : 69 % + mer. 28 mars 2018 14:27:35 CEST: INFO: check_command => check-bench-disk + mer. 28 mars 2018 14:27:35 CEST: INFO: check_period => 24x7 + mer. 28 mars 2018 14:27:35 CEST: INFO: type => 65560 + mer. 28 mars 2018 14:27:35 CEST: INFO: last_hard_state => 0 + +.. note:: + This log file will grow quickly, do not forget to add a log rotate. + +Use parameters +************** + +The Centreon Broker log functions should be used for log only. To write into a +file, we must use the Lua dedicated function. Moreover, it is possible to use +parameters to define the name of the log file. + +So it is time to improve our Stream Connector: + +.. code-block:: lua + + function init(conf) + logFile = conf['logFile'] + broker_log:set_parameters(3, "/var/log/centreon-broker/debug.log") + end + + function write(d) + for k,v in pairs(d) do + writeIntoFile(k .. " => " .. tostring(v) .. "\n") + end + return true + end + + function writeIntoFile(output) + local file,err = io.open(logFile, 'a') + if file == nil then + broker_log:info(3, "Couldn't open file: " .. err) + else + file:write(output) + file:close() + end + end + +Did you notice that expression `local file,err = io.open(logFile, 'a')`? + +Lua is able to store several variables at the same time. Also, Lua functions can +return several variables! + +For example, if you want to swap variables *a* and *b*, you can enter: + +.. code-block:: lua + + a, b = b, a + +Another example that illustrates several values returned: + +.. code-block:: lua + + function fib(a, b) + return b, a + b + end + +So, this call to **io.open** returns two variables, a first variable +**file** that is a *file descriptor* used to access the file and a second +variable not always defined that contains error if one occurs or **nil** +(not defined) otherwise. + +The **init()** function allows to get parameters and define these from Centreon +web interface. See technical documentation for more information. Here, we add +the possibility to choose the destination file name. The **conf** table has +a key *logFile* defined in the web interface. The corresponding value is +the file name used to store events. + +Edit your Broker output to declare this parameter: + +.. image:: /_static/images/developper/lua/add_parameter.png + :align: center + +It is important that the name of the parameter in the web interface matches the +key name in the **conf** table. Here, it is *logFile*. + +Then click **Save** and go to generate the configuration and restart **cbd**. + +Data are stored into **/var/log/centreon-broker/bbdo2file.log** log file as +this:: + + name => error + category => 3 + interval => 300 + rrd_len => 3456000 + value => 0 + value_type => 0 + type => 196612 + ctime => 1522315660 + index_id => 4880 + element => 4 + state => 0 + category => 3 + interval => 300 + rrd_len => 3456000 + is_for_rebuild => false + service_id => 1056 + type => 196609 + ctime => 1522315660 + host_id => 145 + element => 1 + is_for_rebuild => false + metric_id => 11920 + +Manipulate data +*************** + +Here, we continue to improve our stream connector by choosing what events to +export and also by improving outputs. + +We will select only the NEB category and the events regarding hosts and +services status. + +We know that NEB is the category 1, also service status is the sub-category 24, +whereas host status is the sub-category 14. + +So, only events with the following criteria: + +* category = 1 +* element = 14 or element = 24 + +are interesting for us. + +Moreover, we would prefer to have a host name instead of a host id and a service +description instead of a service id. + +At last, we would be interested to get status information and outputs. + +NEB Events with elements 14 and 24 give almost all we want except host names and +service descriptions. + +To get those two information, we will have to use the **broker_cache** object. +This one is filled when pollers are restarted or reloaded. So, do not forget +to restart your pollers if you want something in your **broker_cache** object! + +If the cache is well filled, it is easy to get a host name from the host id:: + + broker_cache:get_hostname(host_id) + +And it is also easy to get the service description from the host id and service +id:: + + broker_cache:get_service_description(host_id, service_id) + +To install the filter on events, there is a useful function called **filter()** +that takes two parameters into account: *category*, *element*. + +This function, if defined, is called just before **write()**. If it returns +**true**, the **write()** function will be called, otherwise, the event will +be thrown away. + +Let's complete our Lua script: + +.. code-block:: lua + + function init(conf) + logFile = conf['logFile'] + broker_log:set_parameters(3, "/var/log/centreon-broker/debug.log") + end + + function write(d) + local output = "" + + local host_name = broker_cache:get_hostname(d.host_id) + if not host_name then + broker_log:info(3, "Unable to get name of host, please restart centengine") + host_name = d.host_id + end + + if d.element == 14 then + output = "HOST:" .. host_name .. ";" .. d.host_id .. ";" .. d.state .. ";" .. d.output + writeIntoFile(output) + broker_log:info(output) + elseif d.element == 24 then + local service_description = broker_cache:get_service_description(d.host_id, d.service_id) + if not service_description then + broker_log:info(3, "Unable to get description of service, please restart centengine") + service_description = d.service_id + end + output = "SERVICE:" .. host_name .. ";" .. d.host_id .. ";" .. service_description .. ";" .. d.service_id .. ";" .. d.state .. ";" .. d.output + writeIntoFile(output) + broker_log:info(output) + end + return true + end + + function filter(category, element) + -- Get only host status and services status from NEB category + if category == 1 and (element == 14 or element == 24) then + return true + end + return false + end + + local function writeIntoFile(output) + local file,err = io.open(logFile, 'a') + if file == nil then + broker_log:info(3, "Couldn't open file: " .. err) + else + file:write(output) + file:close() + end + end + +Just several remarks on this new script before showing what we get. + +In the **init()** function, we access the *logFile* key in the *conf* table +by using `conf['logFile']`. Whereas, in the **write()** function, we access +the *element* key in the *d* table by using `d.element`... + +In fact, the two syntaxes are allowed : `d.element` is the same value than +`d['element']`. + +Another remark, in the **write()** function we can see something like:: + + if not host_name then + +And in the **writeIntoFile()** function, we can see that:: + + if file == nil then + +Do they mean the same thing? Where is the difference? + +You must know that in Lua, a variable is considered to be **true** if it is +defined and not **false**: + +so, the following code + +.. code:: lua + + if toto then + print("Good") + else + print("Bad") + end + +will write *Good* if *toto* is defined and not **false**. More precisely, it will +write *Good* in the following cases: + +* toto=12 +* toto=true +* toto="A string" +* toto=0 (surprising!) + +It will write *Bad* in these cases: + +* toto=nil (by default a variable is nil, which means not defined) +* toto=false + +The **/var/log/centreon-broker/bbdo2file.log** file will now contain:: + + HOST:srv-DC-djakarta;215;0;OK - srv-DC-djakarta: rta 0.061ms, lost 0% + SERVICE:mail-titan-gateway;92;disk-/usr;623;0;Disk /usr - used : 42.98 Go - size : 142.00 Go - percent : 30 % + SERVICE:mail-sun-master;87;memory-stats;535;0;Memory usage (Total 13.0GB): 0.12GB [buffer:0.00GB] [cache:0.01GB] [pages_tables:0.00GB] [mapped:0.00GB] [active:0.07GB] [inactive:0.00GB] [apps:0.02GB] [unused:12.88GB] + SERVICE:mail-saturn-frontend;86;traffic-eth1;512;0;Traffic In : 4.73 Mb/s (4.73 %), Out : 4.79 Mb/s (4.79 %) - Total RX Bits In : 396.01 Gb, Out : 393.88 Gb + SERVICE:mail-saturn-frontend;86;memory-stats;515;0;Memory usage (Total 16.0GB): 8.89GB [buffer:0.43GB] [cache:0.95GB] [pages_tables:0.27GB] [mapped:0.15GB] [active:3.92GB] [inactive:0.29GB] [apps:2.88GB] [unused:7.11GB] + SERVICE:mail-neptune-frontend;80;traffic-eth1;392;0;Traffic In : 4.82 Mb/s (4.82 %), Out : 6.48 Mb/s (6.48 %) - Total RX Bits In : 398.40 Gb, Out : 396.44 Gb + HOST:srv-DC-casablanca;207;0;OK - srv-DC-casablanca: rta 2.042ms, lost 0% + SERVICE:mail-neptune-frontend;80;memory-stats;395;0;Memory usage (Total 9.0GB): 0.54GB [buffer:0.03GB] [cache:0.00GB] [pages_tables:0.01GB] [mapped:0.00GB] [active:0.48GB] [inactive:0.00GB] [apps:0.01GB] [unused:8.46GB] + SERVICE:mail-mercury-frontend;82;traffic-eth1;432;0;Traffic In : 8.28 Mb/s (8.28 %), Out : 1.23 Mb/s (1.23 %) - Total RX Bits In : 397.71 Gb, Out : 400.34 Gb + SERVICE:mail-mercury-frontend;82;memory-stats;435;0;Memory usage (Total 12.0GB): 1.58GB [buffer:0.00GB] [cache:0.63GB] [pages_tables:0.00GB] [mapped:0.00GB] [active:0.75GB] [inactive:0.00GB] [apps:0.19GB] [unused:10.42GB] + SERVICE:mail-mars-frontend;84;traffic-eth1;472;0;Traffic In : 7.24 Mb/s (7.24 %), Out : 3.36 Mb/s (3.36 %) - Total RX Bits In : 399.93 Gb, Out : 395.67 Gb + SERVICE:mail-mars-frontend;84;memory-stats;475;0;Memory usage (Total 3.0GB): 1.19GB [buffer:0.01GB] [cache:0.59GB] [pages_tables:0.00GB] [mapped:0.00GB] [active:0.15GB] [inactive:0.04GB] [apps:0.39GB] [unused:1.81GB] + SERVICE:mail-jupiter-frontend;85;traffic-eth1;492;0;Traffic In : 1.41 Mb/s (1.41 %), Out : 9.08 Mb/s (9.08 %) - Total RX Bits In : 388.86 Gb, Out : 394.85 Gb + SERVICE:mail-jupiter-frontend;85;memory-stats;495;0;Memory usage (Total 12.0GB): 0.57GB [buffer:0.04GB] [cache:0.23GB] [pages_tables:0.02GB] [mapped:0.02GB] [active:0.07GB] [inactive:0.03GB] [apps:0.16GB] [unused:11.43GB] + SERVICE:mail-io-backend;88;traffic-eth1;547;0;Traffic In : 1.51 Mb/s (1.51 %), Out : 7.12 Mb/s (7.12 %) - Total RX Bits In : 389.61 Gb, Out : 390.54 Gb + SERVICE:mail-io-backend;88;diskio-system;551;0;Device /dev/sda: avg read 4.78 (MB/s) and write 9.08 (MB/s) + + +*********************************** +Export performance data to InfluxDB +*********************************** + +Now, you have already seen many things about stream connectors. It is time to +create something more useful! + +`InfluxDB <https://www.influxdata.com/>`_ is a Time Series database. We will use +this storage to insert performance data collected by the Centreon platform. For +this example, we will use the predefined `InfluxDB Docker <https://hub.docker.com/_/influxdb/>`_. + +To send data to InfluxDB, we need parameters to access to InfluxDB storage: + +* **http_server_address**: IP address of the storage +* **http_server_port**: 8086 by default +* **http_server_protocol**: http or https +* **influx_database**: name of database +* **influx_user**: user to access to database if defined +* **influx_password**: password of user to access to database if defined + +In order to not saturate the storage, we will add all events in a queue and +once its max size is reached, we will send data by bulk. + +We need to define the size of the queue and the maximum +delay before sending events: + +* max_buffer_size +* max_buffer_age + +To create this queue, we introduce a code a little more complicated. We +construct an object **event_queue**. It is composed of parameters such as +*events*, *influx_database* and methods like *new()*, *add()*. + +To understand how to create such an object in Lua, we recommend the Lua +documentation `here for classes <https://www.lua.org/pil/16.1.html>`_ +and `there for metatables <https://www.lua.org/pil/13.html>`_. + +To send data to a server, we provide a **broker_tcp_socket** object. + +Its API is very simple (too simple?). This *socket* +is a TCP socket, it does not support encryption and it can be tricky to send +data in http. Here is an example: + +.. code-block:: lua + + -- Here, we create our socket + local socket = broker_tcp_socket.new() + + -- We establish the connection with the server + socket:connect(address, port) + + -- Now, we can send data + socket:write("This is a text to send") + + -- If, we want an answer, we also have a function to read + local content = socket:read() + + -- When exchanges are finished, we can close the socket + socket:close() + +For our purpose, we do not use **broker_tcp_socket** because of its limitations. +We want to be able to send data to an https server. + +A prerequisite is to install the `lua-socket library <http://w3.impa.br/~diego/software/luasocket/>`_. This library provides several functionalities, we +need two of them: + +* http socket +* ltn12 + +To access them, Lua provides the **require** function. + +Let's introduce the beginning of our new Stream Connector. + +The queue parameters +==================== + +.. code-block:: lua + + -- We declare the objects to import here + local http = require("socket.http") + local ltn12 = require("ltn12") + + -- Here are predefined queue parameters + local event_queue = { + __internal_ts_last_flush = nil, + http_server_address = "", + http_server_port = 8086, + http_server_protocol = "http", + events = {}, + influx_database = "mydb", + influx_user = "", + influx_password = "", + max_buffer_size = 5000, + max_buffer_age = 5 + } + + +In this table, we give default values to parameters that can possibly +be changed during the **init()** call. This table will be used to store important +data for the script and is also our queue object. + +A method to create the queue +============================ + +To declare this table as a Lua object, we need a constructor. So, here it is: + +.. code-block:: lua + + -- Constructor of the event_queue + function event_queue:new(o, conf) + o = o or {} + setmetatable(o, self) + self.__index = self + for i,v in pairs(conf) do + if self[i] and i ~= "events" and string.sub(i, 1, 11) ~= "__internal_" then + broker_log:info(1, "event_queue:new: getting parameter " .. i .. " => " .. v) + self[i] = v + else + broker_log:warning(1, "event_queue:new: ignoring parameter " .. i .. " => " .. v) + end + end + self.__internal_ts_last_flush = os.time() + broker_log:info(2, "event_queue:new: setting the internal timestamp to " .. self.__internal_ts_last_flush) + return o + end + +.. note:: + In this function, we use a Lua sugar "o = o or {}" that means *o* stays the + same if it is **true**, otherwise it is affected with an empty table `{}`. + + Another point to notice is the **~=** operator that means **different from**. + + And to finish on this function, the variable **self** is implicitly defined + when we declare an object's method. Its meaning is the same as **this** in + Java or in C++. It represents the object we are working on. + +A method to add event in queue +============================== + +We have a queue object. It would be great to use it like this: + +.. code-block:: lua + + -- We construct it + local queue = event_queue:new(nil, conf) + + -- We add an event to it + queue:add(event) + + -- When the queue is full, we would like to do something like this + queue:flush() + + +Let's do it! Below, we present an **add()** method that retrieves a host name +and service description from the cache, builds a string from the event and +pushes it on its stack. + +.. code-block:: lua + + function event_queue:add(e) + local metric = e.name + -- time is a reserved word in influxDB so I rename it + if metric == "time" then + metric = "_" .. metric + end + + -- retrieve objects names instead of IDs + local host_name = broker_cache:get_hostname(e.host_id) + local service_description = broker_cache:get_service_description(e.host_id, e.service_id) + + -- what if we could not get them from cache + if not host_name then + broker_log:warning(1, "event_queue:add: host_name for id " .. e.host_id .. " not found. Restarting centengine should fix this.") + host_name = e.host_id + end + if not service_description then + broker_log:warning(1, "event_queue:add: service_description for id " .. e.host_id .. "." .. e.service_id .. " not found. Restarting centengine should fix this.") + service_description = e.service_id + else + service_description = service_description:gsub(" ", "_") + end + + -- we finally append the event to the events table + metric = metric:gsub(" ", "_") + broker_log:info(3, 'event_queue:add: adding ' .. service_description .. ",host=" .. host_name .. " " .. metric .. "=" .. e.value .. " " .. e.ctime .. '000000000" to event list.') + self.events[#self.events + 1] = service_description .. ",host=" .. host_name .. " " .. metric .. "=" .. e.value .. " " .. e.ctime .. "000000000\n" + + -- then we check whether it is time to send the events to the receiver and flush + if #self.events >= self.max_buffer_size then + broker_log:info(2, "event_queue:add: flushing because buffer size reached " .. self.max_buffer_size .. " elements.") + self:flush() + return true + elseif os.time() - self.__internal_ts_last_flush >= self.max_buffer_age then + broker_log:info(2, "event_queue:add: flushing " .. #self.events .. " elements because buffer age reached " .. (os.time() - self.__internal_ts_last_flush) .. "s and max age is " .. self.max_buffer_age .. "s.") + self:flush() + return true + else + return false + end + end + +A method to flush the queue +=========================== + +Once the events added in the queue and the maximum size of the queue or the +timeout is reached, events will be sent to the InfluxDB storage. + +This function builds data from the queue and sends them to the storage. If an +error occurs, it dumps a log error. + +It is here that we use the **http** and **ltn12** objects loaded at the +beginning of the script. + +.. code-block:: lua + + function event_queue:flush() + broker_log:info(2, "event_queue:flush: Concatenating all the events as one string") + -- we concatenate all the events + local http_post_data = "" + local http_result_body = {} + for i, raw_event in ipairs(self.events) do + http_post_data = http_post_data .. raw_event + end + broker_log:info(2, 'event_queue:flush: HTTP POST request "' .. self.http_server_protocol .. "://" .. self.http_server_address .. ":" .. self.http_server_port .. "/write?db=" .. self.influx_database .. '"') + broker_log:info(3, "event_queue:flush: HTTP POST data are: '" .. http_post_data .. "'") + + -- build url + local influxdb_url = self.http_server_protocol .. "://" .. self.http_server_address .. ":" .. self.http_server_port .. "/write?db=" .. self.influx_database + -- add authentication if needed + if string.len(self.influx_user) >= 1 and string.len(self.influx_password) >= 1 then + influxdb_url = influxdb_url .. "&u=" .. self.influx_user .. "&p="..self.influx_password + end + + local hr_result, hr_code, hr_header, hr_s = http.request{ + url = influxdb_url, + method = "POST", + -- sink is where the request result's body will go + sink = ltn12.sink.table(http_result_body), + -- request body needs to be formatted as a LTN12 source + source = ltn12.source.string(http_post_data), + headers = { + -- mandatory for POST request with body + ["content-length"] = string.len(http_post_data) + } + } + -- Handling the return code + if hr_code == 204 then + broker_log:info(2, "event_queue:flush: HTTP POST request successful: return code is " .. hr_code) + else + broker_log:error(1, "event_queue:flush: HTTP POST request FAILED: return code is " .. hr_code) + for i, v in ipairs(http_result_body) do + broker_log:error(1, "event_queue:flush: HTTP POST request FAILED: message line " .. i .. ' is "' .. v .. '"') + end + end + + -- now that the data has been sent, we empty the events array + self.events = {} + -- and update the timestamp + self.__internal_ts_last_flush = os.time() + end + +The init() function to get parameters and create the queue +========================================================== + +In this case, the **init()** function creates the queue with parameters +defined by users in the web interface or uses default parameters already +defined in the queue. This alternative is managed by the queue constructor. + +.. code-block:: lua + + function init(conf) + broker_log:set_parameters(1, "/var/log/centreon-broker/stream-connector-influxdb.log") + broker_log:info(2, "init: Beginning init() function") + queue = event_queue:new(nil, conf) + broker_log:info(2, "init: Ending init() function, Event queue created") + end + +.. note:: + + **queue** is not defined as local, this is important so that it is accessible + from all the functions. + +The write() function to insert events in queue +============================================== + +The **write()** function is only used to insert filtered events into the queue: + +.. code-block:: lua + + function write(e) + broker_log:info(3, "write: Beginning write() function") + queue:add(e) + broker_log:info(3, "write: Ending write() function\n") + return true + end + +The filter() function to select only performance data events +============================================================ + +To select only performance data, we need to select *category* 3 (“Storage”) +and *element* 1 for *metric*: + +.. code-block:: lua + + function filter(category, element) + if category == 3 and element == 1 then + return true + end + return false + end + +Complete script +=============== + +The complete script can be downloaded `here <https://github.com/centreon/centreon-stream-connector-scripts/tree/master/influxdb>`_. + +Configure Centreon Broker +========================= + +Configure the new output into Centreon Web interface in +**Configuration > Pollers > Broker configuration > Central Broker**. +In **Output** tab select **Generic – Stream connector** and click **Add**: + +.. image:: /_static/images/developper/lua/add_stream_connector.png + :align: center + +Define the name of this output and the path to the Lua connector: + +.. image:: /_static/images/developper/lua/broker_influxdb_output.png + :align: center + :scale: 65% + +Then click **Save** and go to generate the configuration and restart **cbd**. + +.. note:: + Don’t forget to restart “centengine” too to create the Centreon Broker cache. + +If you install the `Grafana <https://grafana.com/>`_ dashboard, you can visualize the stored data: + +.. image:: /_static/images/developper/lua/visualize_data_grafana.png + :align: center + :scale: 65% + +Discover other Centreon Stream Connectors +========================================= + +Centreon provides a Github repository to host Lua scripts developed by Centreon +and the community. Please go to the `dedicated Github <http://github.com/centreon/centreon-stream-connector-scripts>`_. + +Need help to develop your Stream connector? You want to share your experience with +the community? Join the `Centreon community Slack channel <https://centreon.github.io/>`_. + diff --git a/doc/fr/release_notes/centreon-2.8/centreon-2.8.20.rst b/doc/fr/release_notes/centreon-2.8/centreon-2.8.20.rst index 4cd104f88077c2f831a2abbbd076c915728038f2..b45a4a2fe55a259f0ceaa035ef907c62dcaf8b95 100644 --- a/doc/fr/release_notes/centreon-2.8/centreon-2.8.20.rst +++ b/doc/fr/release_notes/centreon-2.8/centreon-2.8.20.rst @@ -5,5 +5,28 @@ Centreon Web 2.8.20 Enhancements ============ +* [API] Add default poller - PR #6098 +* [API] Link host with default poller if unknown poller - PR #6099 +* [ACL] Improve performance - #6056 PR #6107 +* [Documentation] Improve Centreon CLAPI usage - PR #6090 #6091 +* [Documentation] Improve documentation to add a new poller - #6075 PR #6086 +* [Documentation] Add notice for 64 bits support only - PR #6101 +* [Monitoring] Display links in output and comments - #5943 PR #6113 + Bug Fixes ========= + +* [ACL] Allow nested groups filter in ldap configuration - #6127 PR #6128 +* [API] Export specific service, add host before service in CLAPI - PR #6100 +* [API] CLAPI add resource export filter - PR #6125 +* [API] CLAPI Export contact with contact group - PR #6131 +* [API] CLAPI Export service categories - PR #6134 +* [Configuration] SNMP trap poller generation uses ACL - #6043 PR #6069 +* [Custom Views] Fix share custom view - PR #6109 +* [Poller Stats] Poller Statistics Graphs are displayed in first column only - #6003 PR #6122 + +Others +====== + +* Update copyright date on the login page - PR #6076 +* Remove multiple debug in Centreon - PR #6138 \ No newline at end of file diff --git a/doc/fr/release_notes/centreon-2.8/centreon-2.8.21.rst b/doc/fr/release_notes/centreon-2.8/centreon-2.8.21.rst index a74c0badd68c52365df1eaeafacba1f4e9262211..cc9ffa5012ae1c2a7ee71e299b291875b97ec8d8 100644 --- a/doc/fr/release_notes/centreon-2.8/centreon-2.8.21.rst +++ b/doc/fr/release_notes/centreon-2.8/centreon-2.8.21.rst @@ -5,5 +5,17 @@ Centreon Web 2.8.21 Enhancements ============ +* [Documentation] Add chapter about how to write a stream connector - PR #6189 +* [API] Separate REST API configuration and REST API realtime access - PR #6188 + Bug Fixes ========= + +* [ACL] Manage filters (poller, host, service) on servicegroup - PR #6163 +* [Configuration] Fix output stream connector name for fresh install - PR #6159 #6182 +* [Configuration] No "Conf changed" flag set to "yes" when deploying services to selected hosts - #6160 PR #6191 + +Other +===== + +* Fix php warning in realtime host API - PR #6174 \ No newline at end of file diff --git a/doc/fr/release_notes/centreon-2.8/centreon-2.8.22.rst b/doc/fr/release_notes/centreon-2.8/centreon-2.8.22.rst new file mode 100644 index 0000000000000000000000000000000000000000..2cf9f9b79db2e7df6b1ef29d5496040ca2dfecde --- /dev/null +++ b/doc/fr/release_notes/centreon-2.8/centreon-2.8.22.rst @@ -0,0 +1,14 @@ +################### +Centreon Web 2.8.22 +################### + +Enhancements +============ + +Bug Fixes +========= + +* [CLAPI] Fix host services deployment - PR #6212 + +Other +===== diff --git a/doc/fr/release_notes/centreon-2.8/centreon-2.8.23.rst b/doc/fr/release_notes/centreon-2.8/centreon-2.8.23.rst new file mode 100644 index 0000000000000000000000000000000000000000..603bb166a422992225583595a3c305ce05eefa19 --- /dev/null +++ b/doc/fr/release_notes/centreon-2.8/centreon-2.8.23.rst @@ -0,0 +1,32 @@ +################### +Centreon Web 2.8.23 +################### + +Enhancements +============ + +* [API] Add default poller - PR #6098 +* [API] Link host with default poller if unknown poller - PR #6099 +* [ACL] Improve performance - #6056 PR #6107 +* [Documentation] Improve Centreon CLAPI usage - PR #6090 #6091 +* [Documentation] Improve documentation to add a new poller - #6075 PR #6086 +* [Documentation] Add notice for 64 bits support only - PR #6101 +* [Monitoring] Display links in output and comments - #5943 PR #6113 + +Bug Fixes +========= + +* [ACL] Allow nested groups filter in ldap configuration - #6127 PR #6128 +* [API] Export specific service, add host before service in CLAPI - PR #6100 +* [API] CLAPI add resource export filter - PR #6125 +* [API] CLAPI Export contact with contact group - PR #6131 +* [API] CLAPI Export service categories - PR #6134 +* [Configuration] SNMP trap poller generation uses ACL - #6043 PR #6069 +* [Custom Views] Fix share custom view - PR #6109 +* [Poller Stats] Poller Statistics Graphs are displayed in first column only - #6003 PR #6122 + +Others +====== + +* Update copyright date on the login page - PR #6076 +* Remove multiple debug in Centreon - PR #6138 diff --git a/doc/fr/release_notes/centreon-2.8/index.rst b/doc/fr/release_notes/centreon-2.8/index.rst index 845a9edebbf7f1848f0c03682b431022ff5054c9..1a40f83ac3110f7a40f706bb6b03d0647992f5e9 100644 --- a/doc/fr/release_notes/centreon-2.8/index.rst +++ b/doc/fr/release_notes/centreon-2.8/index.rst @@ -28,3 +28,5 @@ Please find here the release notes dedicated to the last 2.8.x version of Centre centreon-2.8.19 centreon-2.8.20 centreon-2.8.21 + centreon-2.8.22 + centreon-2.8.23 diff --git a/features/DowntimeRecurrent.feature b/features/DowntimeRecurrent.feature new file mode 100644 index 0000000000000000000000000000000000000000..6fdc7440bea305bb78263bdef1fc3cfadadc38a9 --- /dev/null +++ b/features/DowntimeRecurrent.feature @@ -0,0 +1,14 @@ +Feature: Testing a recurrent Downtime + As a Centreon user + I want to be certain that the recurrent downtimes work correctly + To release quality products + + Background: + Given I am logged in a Centreon server + + @critical + Scenario: Testing a recurrent Downtime on a HostGroup without any ServiceGroup created (Bugfix) + Given a hostGroup is configured + And a recurrent downtime on a hostGroup + When this one gives a downtime + Then the recurrent downtime started diff --git a/features/bootstrap/DowntimeRecurrentContext.php b/features/bootstrap/DowntimeRecurrentContext.php new file mode 100644 index 0000000000000000000000000000000000000000..3957c0584296102e1af08c1d00611dcbcf58a582 --- /dev/null +++ b/features/bootstrap/DowntimeRecurrentContext.php @@ -0,0 +1,127 @@ +<?php + +use Centreon\Test\Behat\CentreonContext; +use Centreon\Test\Behat\Configuration\ServiceConfigurationPage; +use Centreon\Test\Behat\Configuration\DowntimeConfigurationListingPage; +use Centreon\Test\Behat\Configuration\HostConfigurationPage; +use Centreon\Test\Behat\Configuration\HostGroupConfigurationPage; +use Centreon\Test\Behat\Configuration\RecurrentDowntimeConfigurationPage; + +/** + * Defines application features from the specific context. + */ +class DowntimeRecurrentContext extends CentreonContext +{ + protected $currentPage; + protected $startDate; + protected $endDate; + + protected $host = array( + 'name' => 'host', + 'alias' => 'host', + 'address' => 'host2@localhost', + 'check_command' => 'check_centreon_dummy', + 'location' => 'Europe/Paris' + ); + + protected $hostGroup = array( + 'name' => 'hostGroupName', + 'alias' => 'hostGroupAlias', + 'hosts' => 'host', + 'enabled' => 1 + ); + + protected $service = array( + 'hosts' => 'host', + 'description' => 'service', + 'templates' => 'generic-service', + 'check_command' => 'check_centreon_dummy', + 'check_period' => '24x7', + 'max_check_attempts' => 1, + 'normal_check_interval' => 1, + 'retry_check_interval' => 1, + 'active_checks_enabled' => 1, + 'passive_checks_enabled' => 0, + 'notifications_enabled' => 1, + 'notify_on_recovery' => 1, + 'notify_on_critical' => 1, + 'recovery_notification_delay' => 1, + 'cs' => 'admin_admin' + ); + + /** + * @Given a hostGroup is configured + */ + public function aHostGroupIsConfigured() + { + $this->currentPage = new HostConfigurationPage($this); + $this->currentPage->setproperties($this->host); + $this->currentPage->save(); + $this->currentPage = new HostGroupConfigurationPage($this); + $this->currentPage->setProperties($this->hostGroup); + $this->currentPage->save(); + $this->currentPage = new ServiceConfigurationPage($this); + $this->currentPage->setProperties($this->service); + $this->currentPage->save(); + $this->reloadAllPollers(); + } + + /** + * @Given a recurrent downtime on a hostGroup + */ + public function aRecurrentDowntime() + { + $this->startDate = new DateTime('now'); + + $this->endDate = new DateTime('now'); + $this->endDate->add(new DateInterval('PT360M')); + + $this->currentPage = new RecurrentDowntimeConfigurationPage($this); + $this->currentPage->setProperties(array( + 'name' => 'test_DT', + 'alias' => 'recurrent_DT', + 'days' => array(7, 1, 2, 3, 4, 5, 6), + 'start' => $this->startDate->format('H:i'), + 'end' => $this->endDate->format('H:i'), + 'hostgroup_relation' => $this->hostGroup['name'] + )); + + $this->currentPage->save(); + } + + /** + * @When this one gives a downtime + */ + public function thisOneGivesADowntime() + { + /* faking cron's launchtime. 2 min sooner */ + $this->container->execute( + "faketime -f '-120s' php /usr/share/centreon/cron/downtimeManager.php", + 'web' + ); + } + + /** + * @Then the recurrent downtime started + */ + public function aRecurrentDowntimeIsStarted() + { + /* checking for results */ + $this->spin( + function ($context) { + $found = false; + $this->currentPage = new DowntimeConfigurationListingPage($context); + $this->currentPage->displayDowntimeCycle(); + foreach ($this->currentPage->getEntries() as $entry) { + if ($entry['host'] == $context->host['name'] && + $entry['service'] == $context->service['description'] && + $entry['started'] == true + ) { + $found = true; + } + } + return $found; + } + ); + } +} diff --git a/tests/clapi_export/clapi-configuration.txt b/tests/clapi_export/clapi-configuration.txt index f3a59e1d9fe1b79639c680e07b7e897175cb23da..8058ddca8cae078a8d947945d0bdf2841c184e98 100644 --- a/tests/clapi_export/clapi-configuration.txt +++ b/tests/clapi_export/clapi-configuration.txt @@ -708,6 +708,7 @@ CONTACTTPL;setparam;contact_template;servicenotifopt;n CONTACTTPL;setparam;contact_template;contact_js_effects;0 CONTACTTPL;setparam;contact_template;timezone; CONTACTTPL;setparam;contact_template;reach_api;0 +CONTACTTPL;setparam;contact_template;reach_api_rt;0 CONTACTTPL;setparam;contact_template;contact_enable_notifications;2 CONTACTTPL;setparam;contact_template;contact_type_msg;txt CONTACTTPL;setparam;contact_template;contact_activate;1 @@ -717,6 +718,7 @@ CONTACTTPL;setparam;test_contact-template;servicenotifopt;n CONTACTTPL;setparam;test_contact-template;contact_js_effects;0 CONTACTTPL;setparam;test_contact-template;timezone; CONTACTTPL;setparam;test_contact-template;reach_api;0 +CONTACTTPL;setparam;test_contact-template;reach_api_rt;0 CONTACTTPL;setparam;test_contact-template;contact_enable_notifications;0 CONTACTTPL;setparam;test_contact-template;contact_type_msg;txt CONTACTTPL;setparam;test_contact-template;contact_activate;1 @@ -780,6 +782,7 @@ CONTACT;setparam;admin;servicenotifopt;n CONTACT;setparam;admin;contact_js_effects;0 CONTACT;setparam;admin;timezone; CONTACT;setparam;admin;reach_api;0 +CONTACT;setparam;admin;reach_api_rt;0 CONTACT;setparam;admin;contact_enable_notifications;1 CONTACT;setparam;admin;contact_type_msg;txt CONTACT;setparam;admin;contact_activate;1 @@ -793,6 +796,7 @@ CONTACT;setparam;guest;servicenotifopt;n CONTACT;setparam;guest;contact_js_effects;0 CONTACT;setparam;guest;timezone; CONTACT;setparam;guest;reach_api;0 +CONTACT;setparam;guest;reach_api_rt;0 CONTACT;setparam;guest;contact_enable_notifications;2 CONTACT;setparam;guest;contact_type_msg;txt CONTACT;setparam;guest;contact_activate;0 @@ -806,6 +810,7 @@ CONTACT;setparam;user;servicenotifopt;n CONTACT;setparam;user;contact_js_effects;0 CONTACT;setparam;user;timezone; CONTACT;setparam;user;reach_api;0 +CONTACT;setparam;user;reach_api_rt;0 CONTACT;setparam;user;contact_enable_notifications;2 CONTACT;setparam;user;contact_type_msg;txt CONTACT;setparam;user;contact_activate;0 @@ -817,6 +822,7 @@ CONTACT;setparam;test_contact;servicenotifopt;n CONTACT;setparam;test_contact;contact_js_effects;0 CONTACT;setparam;test_contact;timezone; CONTACT;setparam;test_contact;reach_api;0 +CONTACT;setparam;test_contact;reach_api_rt;0 CONTACT;setparam;test_contact;contact_enable_notifications;0 CONTACT;setparam;test_contact;contact_type_msg;txt CONTACT;setparam;test_contact;contact_activate;1 @@ -826,6 +832,7 @@ CONTACT;setparam;jeanpierre;servicenotifopt;n CONTACT;setparam;jeanpierre;contact_js_effects;0 CONTACT;setparam;jeanpierre;timezone; CONTACT;setparam;jeanpierre;reach_api;0 +CONTACT;setparam;jeanpierre;reach_api_rt;0 CONTACT;setparam;jeanpierre;contact_enable_notifications;2 CONTACT;setparam;jeanpierre;contact_type_msg;txt CONTACT;setparam;jeanpierre;contact_activate;1 diff --git a/www/Themes/Centreon-2/jquery-ui/jquery-ui-centreon.css b/www/Themes/Centreon-2/jquery-ui/jquery-ui-centreon.css index cb996859e1f945f46703eb1d9f7dcb0daea6f75b..0de9bccc3571f4da8946e8d713c87fc758c5b4a7 100644 --- a/www/Themes/Centreon-2/jquery-ui/jquery-ui-centreon.css +++ b/www/Themes/Centreon-2/jquery-ui/jquery-ui-centreon.css @@ -12,7 +12,7 @@ .ui-sortable-placeholder { visibility: hidden; } .ui-button, .ui-button:hover, .ui-button:focus { - padding: .2em 1em .2em .2em; + padding: .4em 1em; color: #ffffff; font-weight: bold; font-family: segoe ui, Arial, sans-serif; @@ -57,6 +57,4 @@ iframe { .ui-tabs .ui-tabs-nav li a { float: left; padding: .4em 1em 0.4em .4em; text-decoration: none; background: #009fdf;} .ui-tabs .ui-tabs-nav li.ui-state-active a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; background: none; } - -.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .2em 1em .2em 2.1em; } #globalView {padding-bottom:10px;} diff --git a/www/Themes/Centreon-2/style.css b/www/Themes/Centreon-2/style.css index a666cc1c13b643b5dda58ca50411a8bc5fb8bb39..9be9fc4a3f245cff8d61baa81fa2c6a10803c1df 100644 --- a/www/Themes/Centreon-2/style.css +++ b/www/Themes/Centreon-2/style.css @@ -1756,6 +1756,10 @@ span.state_badge { background: transparent !important; } +#colorbox { + outline: none; +} + #cboxContent { border-radius: 4px; border: 1px solid #a7a8ac; @@ -1772,6 +1776,7 @@ span.state_badge { width: 18px !important; right: -10px !important; border-radius: 10px; + border-width: 0px; } /* ColorBox CSS */ diff --git a/www/api/class/centreon_administration_widget.class.php b/www/api/class/centreon_administration_widget.class.php index 5a11d198dd9f1b89a76f3f76e562b45d970a1e66..4f6ba0a7c4ce6572e2f1945084e91990081a424c 100644 --- a/www/api/class/centreon_administration_widget.class.php +++ b/www/api/class/centreon_administration_widget.class.php @@ -74,4 +74,21 @@ class CentreonAdministrationWidget extends CentreonWebService $widgetObj = new CentreonWidget($centreon, $this->pearDB); return $widgetObj->getWidgetModels($q, $range); } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + if (parent::authorize($action, $user, $isInternal)) { + return true; + } + + return $user->hasAccessRestApiConfiguration(); + } } diff --git a/www/api/class/centreon_clapi.class.php b/www/api/class/centreon_clapi.class.php index 932b1c08b6e9f55cb8637a05d1466a87c498f6d9..53075b85e7d94df8658a86fc112afe2e28afe778 100644 --- a/www/api/class/centreon_clapi.class.php +++ b/www/api/class/centreon_clapi.class.php @@ -189,4 +189,21 @@ class CentreonClapi extends CentreonWebService } return $return; } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + if (parent::authorize($action, $user, $isInternal)) { + return true; + } + + return $user->hasAccessRestApiConfiguration(); + } } diff --git a/www/api/class/centreon_configuration_objects.class.php b/www/api/class/centreon_configuration_objects.class.php index 55ef93672431546fb0ffca3bc152744398cba883..6d3e4685fdbf563464cbe97ef1b6d99e30d1a252 100644 --- a/www/api/class/centreon_configuration_objects.class.php +++ b/www/api/class/centreon_configuration_objects.class.php @@ -270,4 +270,21 @@ class CentreonConfigurationObjects extends CentreonWebService } return $tmpValues; } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + if (parent::authorize($action, $user, $isInternal)) { + return true; + } + + return $user->hasAccessRestApiConfiguration(); + } } diff --git a/www/api/class/centreon_home_customview.class.php b/www/api/class/centreon_home_customview.class.php index 84b7fa4a5cb73b57b10e1100627b00ee64509dc0..3aa5d7b4611c6b8a57daaad0ae6bd12fda0fa540 100644 --- a/www/api/class/centreon_home_customview.class.php +++ b/www/api/class/centreon_home_customview.class.php @@ -163,4 +163,17 @@ class CentreonHomeCustomview extends CentreonWebService 'tabs' => $tabs ); } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + return true; + } } diff --git a/www/api/class/centreon_keepalive.class.php b/www/api/class/centreon_keepalive.class.php index e055763456be612007242fa8759eef929de8bd17..bc1b9d15d808bd2e7f47b620de7a820e1dfbec99 100644 --- a/www/api/class/centreon_keepalive.class.php +++ b/www/api/class/centreon_keepalive.class.php @@ -50,7 +50,7 @@ class CentreonKeepalive extends CentreonWebService { parent::__construct(); } - + /** * Keep alive */ @@ -59,4 +59,17 @@ class CentreonKeepalive extends CentreonWebService $session = new CentreonSession(); $session->updateSession($this->pearDB); } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + return true; + } } diff --git a/www/api/class/centreon_metric.class.php b/www/api/class/centreon_metric.class.php index 1ae1cc4b73f95eabe4ad825f55acec3a9b1d1a6e..5ca9701cd89ffb15bf4a645e9e2e1643ec348fb1 100644 --- a/www/api/class/centreon_metric.class.php +++ b/www/api/class/centreon_metric.class.php @@ -675,4 +675,17 @@ class CentreonMetric extends CentreonWebService } return $periods; } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + return true; + } } diff --git a/www/api/class/centreon_proxy.class.php b/www/api/class/centreon_proxy.class.php index a2a5ffef4aeb5fd53a896d283b688593a76c1783..13980cfbad9c6da31e383baf1deebb7ff6bfa67a 100644 --- a/www/api/class/centreon_proxy.class.php +++ b/www/api/class/centreon_proxy.class.php @@ -26,4 +26,17 @@ class CentreonProxy extends CentreonWebService 'message' => $message ); } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + return true; + } } diff --git a/www/api/class/centreon_realtime_base.class.php b/www/api/class/centreon_realtime_base.class.php index 30637ed1583f82abe11300be1190cbb21faff995..f2a589789fd4579df1151f445821080e164db01b 100644 --- a/www/api/class/centreon_realtime_base.class.php +++ b/www/api/class/centreon_realtime_base.class.php @@ -247,4 +247,21 @@ class CentreonRealtimeBase extends CentreonWebService } return $tmpValues; } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + if (parent::authorize($action, $user, $isInternal)) { + return true; + } + + return $user->hasAccessRestApiRealtime(); + } } diff --git a/www/api/class/centreon_results_acceptor.class.php b/www/api/class/centreon_results_acceptor.class.php index 999fadcf66b858e983210bafb9aca6df19af3457..d80a32ba82fd84d4bc5b928511d651a960329ad0 100644 --- a/www/api/class/centreon_results_acceptor.class.php +++ b/www/api/class/centreon_results_acceptor.class.php @@ -66,7 +66,7 @@ class CentreonResultsAcceptor extends CentreonConfigurationObjects } /* - * Get poller Listing + * Get poller Listing */ private function getPollers() { @@ -236,4 +236,21 @@ class CentreonResultsAcceptor extends CentreonConfigurationObjects throw new RestBadRequestException('Bad arguments - Cannot find command list'); } } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + if (parent::authorize($action, $user, $isInternal)) { + return true; + } + + return $user->hasAccessRestApiConfiguration(); + } } diff --git a/www/api/class/centreon_wiki.class.php b/www/api/class/centreon_wiki.class.php index e669a6fa2032bdb5d4f142f1dd48231e1e10ca4e..675118177dab0e4d343ebf6fd4a6145826ee3c8a 100644 --- a/www/api/class/centreon_wiki.class.php +++ b/www/api/class/centreon_wiki.class.php @@ -78,4 +78,21 @@ class CentreonWiki extends CentreonWebService 'result' => $result ); } + + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal) + { + if (parent::authorize($action, $user, $isInternal)) { + return true; + } + + return $user->hasAccessRestApiConfiguration(); + } } diff --git a/www/api/class/webService.class.php b/www/api/class/webService.class.php index a6244a46c2a84d23496de232d7088e8aa798739a..169385ce837abcfe0920772f6f01e9aea43750bc 100644 --- a/www/api/class/webService.class.php +++ b/www/api/class/webService.class.php @@ -121,6 +121,23 @@ class CentreonWebService } } + /** + * Authorize to access to the action + * + * @param string $action The action name + * @param array $user The current user + * @param boolean $isInternal If the api is call in internal + * @return boolean If the user has access to the action + */ + public function authorize($action, $user, $isInternal = false) + { + if ($isInternal || $user->admin) { + return true; + } + + return false; + } + /** * Get webservice * @@ -220,8 +237,11 @@ class CentreonWebService * Route the webservice to the good method * @global string _CENTREON_PATH_ * @global type $pearDB3 + * + * @param CentreonUser $user The current user + * @param boolean $isInternal If the Rest API call is internal */ - public static function router() + public static function router($user, $isInternal = false) { global $pearDB; @@ -260,6 +280,10 @@ class CentreonWebService static::sendJson("Method not found", 404); } + if (false === $wsObj->authorize($action, $user, $isInternal)) { + static::sendJson('Forbidden', 403); + } + /* Execute the action */ try { static::updateTokenTtl(); diff --git a/www/api/index.php b/www/api/index.php index 1249620ab7e5c11e9bd140bf2c774c8bf169e70b..101b470335c30d26d7677cca260d4722e1cc6de1 100644 --- a/www/api/index.php +++ b/www/api/index.php @@ -50,11 +50,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && if (false === isset($_POST['username']) || false === isset($_POST['password'])) { CentreonWebService::sendJson("Bad parameters", 400); } - + /* @todo Check if user already have valid token */ require_once _CENTREON_PATH_ . "/www/class/centreonLog.class.php"; require_once _CENTREON_PATH_ . "/www/class/centreonAuth.class.php"; - + /* Authenticate the user */ $log = new CentreonUserLog(0, $pearDB); $auth = new CentreonAuth($_POST['username'], $_POST['password'], 0, $pearDB, $log, 1, "", "API"); @@ -62,10 +62,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && CentreonWebService::sendJson("Bad credentials", 403); exit(); } - + /* Check if user exists in contact table */ $reachAPI = 0; - $res = $pearDB->prepare("SELECT contact_id, reach_api, contact_admin FROM contact WHERE contact_activate = '1' AND contact_register = '1' AND contact_alias = ?"); + $res = $pearDB->prepare("SELECT contact_id, reach_api, reach_api_rt, contact_admin + FROM contact + WHERE contact_activate = '1' AND contact_register = '1' AND contact_alias = ?"); $res = $pearDB->execute($res, array($_POST['username'])); while ($data = $res->fetchRow()) { if (isset($data['contact_admin']) && $data['contact_admin'] == 1) { @@ -73,6 +75,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && } else { if (isset($data['reach_api']) && $data['reach_api'] == 1) { $reachAPI = 1; + } else if (isset($data['reach_api_rt']) && $data['reach_api_rt'] == 1) { + $reachAPI = 1; } } } @@ -111,4 +115,4 @@ if (is_null($userInfos)) { $centreon = new Centreon($userInfos); $oreon = $centreon; -CentreonWebService::router(); +CentreonWebService::router($centreon->user); diff --git a/www/api/internal.php b/www/api/internal.php index 6f39d44a060dcad1d7087092419185ef24034a1c..ea803510138100fb923c7717ace93bbcd8296ed9 100644 --- a/www/api/internal.php +++ b/www/api/internal.php @@ -64,4 +64,4 @@ if (false === isset($centreon) || false === is_object($centreon)) { CentreonWebService::sendJson("Unauthorized", 401); } -CentreonWebService::router(); +CentreonWebService::router($centreon->user, true); diff --git a/www/class/centreon-clapi/centreonContact.class.php b/www/class/centreon-clapi/centreonContact.class.php index 9adf7eed8b7545f6b0c8708125fa391eafe27021..4e2b75f4d6d63688a2ee15d86f2fe47a8bb4e233 100644 --- a/www/class/centreon-clapi/centreonContact.class.php +++ b/www/class/centreon-clapi/centreonContact.class.php @@ -367,7 +367,7 @@ class CentreonContact extends CentreonObject } } } - if ($params[1] != 'reach_api' && $params[1] != 'default_page' && $params[1] != 'ar_id') { + if ($params[1] != 'reach_api' && $params[1] != 'reach_api_rt' && $params[1] != 'default_page' && $params[1] != 'ar_id') { $params[1] = "contact_" . $params[1]; } } diff --git a/www/class/centreon-clapi/centreonHost.class.php b/www/class/centreon-clapi/centreonHost.class.php index bc4d71b56b001e5b8a9a5d6e593cdef4be61631b..df33a299beb1c564ee85979723882a2831fac286 100644 --- a/www/class/centreon-clapi/centreonHost.class.php +++ b/www/class/centreon-clapi/centreonHost.class.php @@ -898,11 +898,20 @@ class CentreonHost extends CentreonObject ); $result = $res->fetchAll(); if (!count($result)) { - $svcId = $svcObj->insert(array('service_description' => $params['service_alias'], + $serviceDesc = array('service_description' => $params['service_alias'], 'service_activate' => '1', 'service_register' => '1', - 'service_template_model_stm_id' => $serviceTemplateId)); + 'service_template_model_stm_id' => $serviceTemplateId); + $svcId = $svcObj->insert($serviceDesc); $hostSvcRel->insert($hostId, $svcId); + $serviceDesc['service_hPars'] = $hostId; + $this->addAuditLog( + 'a', + $svcId, + $params['service_alias'], + $serviceDesc, + 'SERVICE' + ); $svcExtended->insert(array($svcExtended->getUniqueLabelField() => $svcId)); } unset($res); diff --git a/www/class/centreon-clapi/centreonObject.class.php b/www/class/centreon-clapi/centreonObject.class.php index d6d5673a856fa6c697b153e6f6917108420a342d..34f6e05a892488e9e29c1ba2102bf8dca9166ba6 100644 --- a/www/class/centreon-clapi/centreonObject.class.php +++ b/www/class/centreon-clapi/centreonObject.class.php @@ -424,10 +424,11 @@ abstract class CentreonObject * @param int $objId * @param string $objName * @param array $objValues + * @param string|null $objectType - The object type to log if is null use the object type of the class */ - public function addAuditLog($actionType, $objId, $objName, $objValues = array()) + public function addAuditLog($actionType, $objId, $objName, $objValues = array(), $objectType = null) { - $objType = strtoupper($this->action); + $objType = is_null($objectType) ? strtoupper($this->action) : $objectType; $objectTypes = array( 'HTPL' => 'host', 'STPL' => 'service', diff --git a/www/class/centreonDowntime.class.php b/www/class/centreonDowntime.class.php index 7059f9c1d6293f156a4f2369b3a073fd435599fc..887d9dea43c08203d2a65e6044ad8bf9039b0c06 100644 --- a/www/class/centreonDowntime.class.php +++ b/www/class/centreonDowntime.class.php @@ -360,7 +360,7 @@ class CentreonDowntime . 'dtp.dtp_month_cycle, dtp.dtp_day_of_month, dtp.dtp_fixed, dtp.dtp_duration, ' . 'h.host_id, h.host_name, NULL as service_id, NULL as service_description ' . 'FROM downtime_period dtp, downtime dt, ' - . 'downtime_hostgroup_relation dhr, servicegroup sg, ' + . 'downtime_hostgroup_relation dhr, ' . 'host h, hostgroup_relation hgr ' . 'WHERE dtp.dt_id = dhr.dt_id ' . 'AND dtp.dt_id = dt.dt_id ' diff --git a/www/class/centreonHost.class.php b/www/class/centreonHost.class.php index d32d9347d8ee322900f4da2c1781e3105e39003e..7711b5b066b96ba6aea2306df41ef17693e35627 100755 --- a/www/class/centreonHost.class.php +++ b/www/class/centreonHost.class.php @@ -36,6 +36,7 @@ require_once _CENTREON_PATH_ . 'www/class/centreonInstance.class.php'; require_once _CENTREON_PATH_ . 'www/class/centreonService.class.php'; require_once _CENTREON_PATH_ . 'www/class/centreonCommand.class.php'; +require_once _CENTREON_PATH_ . 'www/class/centreonLogAction.class.php'; /* * Class that contains various methods for managing hosts @@ -1743,6 +1744,8 @@ class CentreonHost */ public function deployServices($hostId, $hostTemplateId = null) { + global $centreon; + if (!isset($hostTemplateId)) { $id = $hostId; } else { @@ -1778,15 +1781,17 @@ class CentreonHost $res = $this->db->execute($stmt, $queryValues); if (!$res->numRows()) { - $svcId = $this->serviceObj->insert( - array( - 'service_description' => $service['service_alias'], - 'service_activate' => array('service_activate' => '1'), - 'service_register' => '1', - 'service_template_model_stm_id' => $serviceTemplateId - ) + $serviceDesc = array( + 'service_description' => $service['service_alias'], + 'service_activate' => array('service_activate' => '1'), + 'service_register' => '1', + 'service_template_model_stm_id' => $serviceTemplateId, + 'service_hPars' => $hostId ); + $svcId = $this->serviceObj->insert($serviceDesc); + $fields = CentreonLogAction::prepareChanges($serviceDesc); + $centreon->CentreonLogAction->insertLog("service", $svcId, CentreonDB::escape($service['service_alias']), "a", $fields); $this->insertRelHostService($hostId, $svcId); } unset($res); diff --git a/www/class/centreonPurgeEngine.class.php b/www/class/centreonPurgeEngine.class.php index 6f02b21cc0692ade8041a9532812abd17b92ba93..77fcca65bb5f7d2b79eccb6c17393b7eb63f5f60 100644 --- a/www/class/centreonPurgeEngine.class.php +++ b/www/class/centreonPurgeEngine.class.php @@ -165,7 +165,7 @@ class CentreonPurgeEngine $request .= "AND TABLE_SCHEMA='" . dbcstg . "' "; $request .= "AND CONVERT(PARTITION_DESCRIPTION, SIGNED INTEGER) IS NOT NULL "; $request .= "AND CONVERT(PARTITION_DESCRIPTION, SIGNED INTEGER) < " . $this->tablesToPurge[$table]['retention'] . " "; - $request .= "AND CONVERT(PARTITION_DESCRIPTION, SIGNED INTEGER) NOT LIKE 'pmax' "; + $request .= "AND PARTITION_NAME NOT LIKE 'pmax' "; $DBRESULT = $this->dbCentstorage->query($request); if (PEAR::isError($DBRESULT)) { diff --git a/www/class/centreonUser.class.php b/www/class/centreonUser.class.php index c9af2e59f368a589508daae317dc90754b40b21a..8578b57cd2d1dd39491cc874dc07e24dbfc51297 100644 --- a/www/class/centreonUser.class.php +++ b/www/class/centreonUser.class.php @@ -59,7 +59,10 @@ class CentreonUser var $userCrypted; protected $token; public $default_page; - + + protected $restApi; + protected $restApiRt; + # User LCA # Array with elements ID for loop test var $lcaTopo; @@ -68,7 +71,7 @@ class CentreonUser var $lcaTStr; /** - * + * * @global type $pearDB * @param type $user */ @@ -101,10 +104,16 @@ class CentreonUser */ $this->log = new CentreonUserLog($this->user_id, $pearDB); $this->userCrypted = md5($this->alias); + + /** + * Init rest api auth + */ + $this->restApi = isset($user['reach_api']) && $user['reach_api'] == 1; + $this->restApiRt = isset($user['reach_api_rt']) && $user['reach_api_rt'] == 1; } /** - * + * * @global type $pearDB * @param type $div_name * @return int @@ -124,7 +133,7 @@ class CentreonUser } /** - * + * * @param type $pearDB * @return int */ @@ -140,10 +149,10 @@ class CentreonUser $DBRESULT->free(); return $lcaTopo; } - + /** * Check if user is admin or had ACL - * + * * @param type $sid * @param type $pearDB */ @@ -175,7 +184,7 @@ class CentreonUser } /** - * + * * @return type */ function get_name() @@ -184,7 +193,7 @@ class CentreonUser } /** - * + * * @return type */ function get_email() @@ -193,7 +202,7 @@ class CentreonUser } /** - * + * * @return type */ function get_alias() @@ -202,7 +211,7 @@ class CentreonUser } /** - * + * * @return type */ function get_version() @@ -211,7 +220,7 @@ class CentreonUser } /** - * + * * @return type */ function get_lang() @@ -220,7 +229,7 @@ class CentreonUser } /** - * + * * @return type */ function get_passwd() @@ -229,7 +238,7 @@ class CentreonUser } /** - * + * * @return type */ function get_admin() @@ -238,7 +247,7 @@ class CentreonUser } /** - * + * * @return type */ function is_admin() @@ -247,7 +256,7 @@ class CentreonUser } /** - * + * * @global type $pearDB * @return type */ @@ -264,11 +273,11 @@ class CentreonUser return $this->js_effects; } - + // Set /** - * + * * @param type $id */ function set_id($id) @@ -277,7 +286,7 @@ class CentreonUser } /** - * + * * @param type $name */ function set_name($name) @@ -286,7 +295,7 @@ class CentreonUser } /** - * + * * @param type $email */ function set_email($email) @@ -295,7 +304,7 @@ class CentreonUser } /** - * + * * @param type $lang */ function set_lang($lang) @@ -304,7 +313,7 @@ class CentreonUser } /** - * + * * @param type $alias */ function set_alias($alias) @@ -313,7 +322,7 @@ class CentreonUser } /** - * + * * @param type $version */ function set_version($version) @@ -322,7 +331,7 @@ class CentreonUser } /** - * + * * @param type $js_effects */ function set_js_effects($js_effects) @@ -331,7 +340,7 @@ class CentreonUser } /** - * + * * @return type */ function getMyGMT() @@ -437,7 +446,7 @@ class CentreonUser . 'WHERE cp_contact_id = ? ' . 'AND cp_key IN( '; $queryValues[] = $this->user_id; - + $queryKey =''; foreach ($keys as $key) { $queryKey .=' ?,'; @@ -445,14 +454,14 @@ class CentreonUser } $queryKey = rtrim($queryKey, ','); $deleteQuery .= $queryKey. ' )'; - + $stmt = $db->prepare($deleteQuery); $res = $db->execute($stmt, $queryValues); - + if (PEAR::isError($res)) { throw new Exception('Bad Request'); } - + $insertQuery = 'INSERT INTO contact_param (cp_key, cp_value, cp_contact_id) VALUES (?, ?, ?)'; $stmt = $db->prepare($insertQuery); foreach ($parameters as $key => $value) { @@ -460,20 +469,20 @@ class CentreonUser $db->execute($stmt, $sqlParams); } } - + /** * Get token - * + * * @return string */ public function getToken() { return $this->token; } - + /** * Set token - * + * * @param string $token * @return void */ @@ -481,4 +490,20 @@ class CentreonUser { $this->token = $token; } + + /** + * If the user has access to Rest API Configuration + */ + public function hasAccessRestApiConfiguration() + { + return $this->restApi; + } + + /** + * If the user has access to Rest API Realtime + */ + public function hasAccessRestApiRealtime() + { + return $this->restApiRt; + } } diff --git a/www/include/common/javascript/jquery/plugins/pagination/jquery.pagination.js b/www/include/common/javascript/jquery/plugins/pagination/jquery.pagination.js index a20d0ed5d312d439f4a656a99d120e9ba461e34e..9ab06ea5db3e642bce47d6b4dfe028e869b0c4be 100644 --- a/www/include/common/javascript/jquery/plugins/pagination/jquery.pagination.js +++ b/www/include/common/javascript/jquery/plugins/pagination/jquery.pagination.js @@ -242,6 +242,8 @@ if (opts.load_first_page) { opts.callback(current_page, containers); } + + return this; }; // End of jQuery.fn.pagination block })(jQuery); \ No newline at end of file diff --git a/www/include/common/javascript/jquery/plugins/toggleClick/jquery.toggleClick.js b/www/include/common/javascript/jquery/plugins/toggleClick/jquery.toggleClick.js new file mode 100644 index 0000000000000000000000000000000000000000..57bccd8f1d84f0076b21c9e87000df9c140010ec --- /dev/null +++ b/www/include/common/javascript/jquery/plugins/toggleClick/jquery.toggleClick.js @@ -0,0 +1,13 @@ +(function($) { + $.fn.toggleClick = function(){ + + var functions = arguments ; + + return this.click(function(){ + var iteration = $(this).data('iteration') || 0; + functions[iteration].apply(this, arguments); + iteration = (iteration + 1) % functions.length ; + $(this).data('iteration', iteration); + }); + }; +})(jQuery); \ No newline at end of file diff --git a/www/include/configuration/configObject/contact/DB-Func.php b/www/include/configuration/configObject/contact/DB-Func.php index 9d059544b3128e41fcdcf43d2f628e042453f8a8..29caaf98c9b95ae629ab3e3266abeb5e0f3bedc6 100644 --- a/www/include/configuration/configObject/contact/DB-Func.php +++ b/www/include/configuration/configObject/contact/DB-Func.php @@ -56,7 +56,7 @@ function testContactExistence($name = null) $DBRESULT = $pearDB->query("SELECT contact_name, contact_id FROM contact WHERE contact_name = '" . htmlentities($centreon->checkIllegalChar($name), ENT_QUOTES, "UTF-8") . "'"); $contact = $DBRESULT->fetchRow(); - + if ($DBRESULT->numRows() >= 1 && $contact["contact_id"] == $id) { return true; } elseif ($DBRESULT->numRows() >= 1 && $contact["contact_id"] != $id) { @@ -81,7 +81,7 @@ function testAliasExistence($alias = null) $DBRESULT = $pearDB->query("SELECT contact_alias, contact_id FROM contact WHERE contact_alias = '" . htmlentities($alias, ENT_QUOTES, "UTF-8") . "'"); $contact = $DBRESULT->fetchRow(); - + if ($DBRESULT->numRows() >= 1 && $contact["contact_id"] == $id) { return true; } elseif ($DBRESULT->numRows() >= 1 && $contact["contact_id"] != $id) { @@ -123,10 +123,10 @@ function keepOneContactAtLeast($ct_id = null) /* * Get activated contacts */ - $DBRESULT = $pearDB->query("SELECT COUNT(*) AS nbr_valid - FROM contact - WHERE contact_activate = '1' - AND contact_oreon = '1' + $DBRESULT = $pearDB->query("SELECT COUNT(*) AS nbr_valid + FROM contact + WHERE contact_activate = '1' + AND contact_oreon = '1' AND contact_id <> '" . $pearDB->escape($contact_id) . "'"); $contacts = $DBRESULT->fetchRow(); @@ -157,7 +157,7 @@ function enableContactInDB($contact_id = null, $contact_arr = array()) foreach ($contact_arr as $key => $value) { $DBRESULT = $pearDB->query("UPDATE contact SET contact_activate = '1' WHERE contact_id = '" . intval($key). "'"); - + $DBRESULT2 = $pearDB->query("SELECT contact_name FROM `contact` WHERE `contact_id` = '" . intval($key) . "' LIMIT 1"); $row = $DBRESULT2->fetchRow(); @@ -185,10 +185,10 @@ function disableContactInDB($contact_id = null, $contact_arr = array()) foreach ($contact_arr as $key => $value) { if (keepOneContactAtLeast($key)) { $pearDB->query("UPDATE contact SET contact_activate = '0' WHERE contact_id = '" . intval($key) . "'"); - + $DBRESULT2 = $pearDB->query("SELECT contact_name FROM `contact` WHERE `contact_id` = '" . intval($key) . "' LIMIT 1"); $row = $DBRESULT2->fetchRow(); - + $centreon->CentreonLogAction->insertLog("contact", $key, $row['contact_name'], "disable"); } } @@ -311,11 +311,11 @@ function multipleContactInDB($contacts = array(), $nbrDup = array()) function updateContactInDB($contact_id = null, $from_MC = false) { global $form; - + if (!$contact_id) { return; } - + $ret = $form->getSubmitValues(); # Global function to use if ($from_MC) { @@ -395,7 +395,8 @@ function insertContact($ret = array()) "`contact_id` , `timeperiod_tp_id` , `timeperiod_tp_id2` , `contact_name` , " . "`contact_alias` , `contact_autologin_key` , `contact_passwd` , `contact_lang` , `contact_template_id`, " . "`contact_host_notification_options` , `contact_service_notification_options` , " . - "`contact_email` , `contact_pager` , `contact_comment` , `contact_oreon`, `reach_api`, `contact_register`, `contact_enable_notifications` , " . + "`contact_email` , `contact_pager` , `contact_comment` , `contact_oreon`, `reach_api`, `reach_api_rt`, " . + "`contact_register`, `contact_enable_notifications` , " . "`contact_admin` , `contact_type_msg`, `contact_activate`, `contact_auth_type`, " . "`contact_ldap_dn`, `contact_location`, `contact_address1`, `contact_address2`, " . "`contact_address3`, `contact_address4`, `contact_address5`, `contact_address6`)" . @@ -439,6 +440,9 @@ function insertContact($ret = array()) isset($ret["contact_oreon"]["contact_oreon"]) && $ret["contact_oreon"]["contact_oreon"] != null ? $rq .= "'" . $ret["contact_oreon"]["contact_oreon"] . "', " : $rq .= " '1', "; } isset($ret["reach_api"]["reach_api"]) && $ret["reach_api"]["reach_api"] != null ? $rq .= $ret["reach_api"]["reach_api"] . ", " : $rq .= " 0, "; + isset($ret["reach_api_rt"]["reach_api_rt"]) && $ret["reach_api_rt"]["reach_api_rt"] != null + ? $rq .= $ret["reach_api_rt"]["reach_api_rt"] . ", " + : $rq .= " 0, "; isset($ret["contact_register"]) && $ret["contact_register"] != null ? $rq .= "'" . $ret["contact_register"] . "', " : $rq .= " '1', "; isset($ret["contact_enable_notifications"]["contact_enable_notifications"]) && $ret["contact_enable_notifications"]["contact_enable_notifications"] != null ? $rq .= "'" . $ret["contact_enable_notifications"]["contact_enable_notifications"] . "', " : $rq .= "NULL, "; isset($ret["contact_admin"]["contact_admin"]) && $ret["contact_admin"]["contact_admin"] != null ? $rq .= "'" . $ret["contact_admin"]["contact_admin"] . "', " : $rq .= "'0', "; @@ -468,7 +472,7 @@ function insertContact($ret = array()) $ret["contact_passwd"] = $ret["contact_passwd2"] = $utilsObject->encodePass($ret["contact_passwd"], 'md5'); } } - + /* Prepare value for changelog */ $fields = CentreonLogAction::prepareChanges($ret); $centreon->CentreonLogAction->insertLog("contact", $contact_id["MAX(contact_id)"], $ret["contact_name"], "a", $fields); @@ -495,7 +499,7 @@ function updateContact($contact_id = null, $from_MC = false) $rq .= "timeperiod_tp_id2 = "; isset($ret["timeperiod_tp_id2"]) && $ret["timeperiod_tp_id2"] != null ? $rq .= "'" . $ret["timeperiod_tp_id2"] . "', " : $rq .= "NULL, "; # If we are doing a MC, we don't have to set name and alias field - + if (!$from_MC) { $rq .= "contact_name = "; isset($ret["contact_name"]) && $ret["contact_name"] != null ? $rq .= "'" . $ret["contact_name"] . "', " : $rq .= "NULL, "; @@ -531,6 +535,10 @@ function updateContact($contact_id = null, $from_MC = false) isset($ret["contact_oreon"]["contact_oreon"]) && $ret["contact_oreon"]["contact_oreon"] != null ? $rq .= "'" . $ret["contact_oreon"]["contact_oreon"] . "', " : $rq .= "NULL, "; $rq .= "reach_api = "; isset($ret["reach_api"]["reach_api"]) && $ret["reach_api"]["reach_api"] != null ? $rq .= "'" . $ret["reach_api"]["reach_api"] . "', " : $rq .= "NULL, "; + $rq .= "reach_api_rt = "; + isset($ret["reach_api_rt"]["reach_api_rt"]) && $ret["reach_api_rt"]["reach_api_rt"] != null + ? $rq .= "'" . $ret["reach_api_rt"]["reach_api_rt"] . "', " + : $rq .= "NULL, "; $rq .= "contact_enable_notifications = "; isset($ret["contact_enable_notifications"]["contact_enable_notifications"]) && $ret["contact_enable_notifications"]["contact_enable_notifications"] != null ? $rq .= "'" . $ret["contact_enable_notifications"]["contact_enable_notifications"] . "', " : $rq .= "NULL, "; $rq .= "contact_admin = "; @@ -566,7 +574,7 @@ function updateContact($contact_id = null, $from_MC = false) if (isset($ret["contact_lang"]) && $ret["contact_lang"] != null && $contact_id == $centreon->user->get_id()) { $centreon->user->set_lang($ret["contact_lang"]); } - + if (isset($ret["contact_passwd"])) { if ($encryptType == 1) { $ret["contact_passwd"] = $ret["contact_passwd2"] = $utilsObject->encodePass($ret["contact_passwd"], 'md5'); @@ -585,13 +593,13 @@ function updateContact($contact_id = null, $from_MC = false) function updateContact_MC($contact_id = null) { global $form, $pearDB, $centreon, $encryptType; - + if (!$contact_id) { return; } $utilsObject = new CentreonUtils(); - + $ret = array(); $ret = $form->getSubmitValues(); $rq = "UPDATE contact SET "; @@ -684,7 +692,7 @@ function updateContact_MC($contact_id = null) $DBRESULT2 = $pearDB->query("SELECT contact_name FROM `contact` WHERE contact_id='" . intval($contact_id) . "' LIMIT 1"); $row = $DBRESULT2->fetchRow(); - + /* Prepare value for changelog */ $fields = CentreonLogAction::prepareChanges($ret); $centreon->CentreonLogAction->insertLog("contact", $contact_id, $row["contact_name"], "mc", $fields); @@ -694,7 +702,7 @@ function updateContact_MC($contact_id = null) function updateContactHostCommands($contact_id = null, $ret = array()) { global $form, $pearDB; - + if (!$contact_id) { return; } @@ -869,7 +877,7 @@ function insertLdapContactInDB($tmpContacts = array()) } $tmpContacts["contact_name"][$select_key] = str_replace(array(" ", ","), array("_", "_"), $tmpContacts["contact_name"][$select_key]); $arId = $tmpContacts["ar_id"][$select_key]; - + if (isset($tmpContacts["contact_name"][$select_key]) && testContactExistence($tmpContacts["contact_name"][$select_key])) { $tmpConf["contact_name"] = $tmpContacts["contact_name"][$select_key]; $tmpConf["contact_alias"] = $tmpContacts["contact_alias"][$select_key]; diff --git a/www/include/configuration/configObject/contact/formContact.ihtml b/www/include/configuration/configObject/contact/formContact.ihtml index 9fc82171495f3e5005f7aafb9dad24a400a222d6..6291be53ddcd6f73e81a536290a28d8a8b9cd04e 100644 --- a/www/include/configuration/configObject/contact/formContact.ihtml +++ b/www/include/configuration/configObject/contact/formContact.ihtml @@ -33,7 +33,7 @@ <tr class="list_two"><td class="FormRowField"><img class="helpTooltip" name="email"> {$form.contact_email.label}</td><td class="FormRowValue">{$form.contact_email.html}</td></tr> <tr class="list_one"><td class="FormRowField"><img class="helpTooltip" name="pager"> {$form.contact_pager.label}</td><td class="FormRowValue">{$form.contact_pager.html}</td></tr> <tr class="list_two"><td class="FormRowField"><img class="helpTooltip" name="pager"> {$form.contact_template_id.label}</td><td class="FormRowValue">{$form.contact_template_id.html}</td></tr> - + <tr class="list_lvl_1"> <td class="ListColLvl1_name" colspan="2"> <h4>{$form.header.groupLinks}</h4> @@ -118,6 +118,13 @@ {if $displayAdminFlag == 1} <tr class="list_two"><td class="FormRowField"><img class="helpTooltip" name="admin"> {$form.contact_admin.label}</td><td class="FormRowValue">{$form.contact_admin.html}</td></tr> <tr class="list_one"><td class="FormRowField"><img class="helpTooltip" name="reach_api"> {$form.reach_api.label}</td><td class="FormRowValue">{$form.reach_api.html}</td></tr> + <tr class="list_two"> + <td class="FormRowField"> + <img class="helpTooltip" name="reach_api_rt"> + {$form.reach_api_rt.label} + </td> + <td class="FormRowValue">{$form.reach_api_rt.html}</td> + </tr> {/if} <tr class="list_lvl_1"> <td class="ListColLvl1_name" colspan="2"> @@ -125,7 +132,7 @@ </td> </tr> {if $o == "mc"} - <tr class="list_two"><td class="FormRowField"><img class="helpTooltip" name="mc_update"> {$form.mc_mod_acl.label}</td><td class="FormRowValue">{$form.mc_mod_acl.html}</td></tr> + <tr class="list_one"><td class="FormRowField"><img class="helpTooltip" name="mc_update"> {$form.mc_mod_acl.label}</td><td class="FormRowValue">{$form.mc_mod_acl.html}</td></tr> {/if} <tr class="list_one"><td class="FormRowField"><img class="helpTooltip" name="aclgroups"> {$form.contact_acl_groups.label}</td><td class="FormRowValue">{$form.contact_acl_groups.html}</td></tr> {if $o == "a" || $o == "c"} @@ -175,4 +182,4 @@ </div> {$form.hidden} </form> -{$helptext} \ No newline at end of file +{$helptext} diff --git a/www/include/configuration/configObject/contact/formContact.php b/www/include/configuration/configObject/contact/formContact.php index 3a609b0082eb1623ea1d550fd05caca02994dbb4..3081b7b5c794d7e25c382cd82f591043645a844d 100644 --- a/www/include/configuration/configObject/contact/formContact.php +++ b/www/include/configuration/configObject/contact/formContact.php @@ -205,8 +205,8 @@ $aclCond = ""; if (!$centreon->user->admin) { $aclCond = " WHERE acl_group_id IN (" . $acl->getAccessGroupsString() . ") "; } -$sql = "SELECT acl_group_id, acl_group_name - FROM acl_groups +$sql = "SELECT acl_group_id, acl_group_name + FROM acl_groups {$aclCond} ORDER BY acl_group_name"; $DBRESULT = $pearDB->query($sql); @@ -357,7 +357,12 @@ if ($centreon->user->admin) { $tab = array(); $tab[] = HTML_QuickForm::createElement('radio', 'reach_api', null, _("Yes"), '1'); $tab[] = HTML_QuickForm::createElement('radio', 'reach_api', null, _("No"), '0'); - $form->addGroup($tab, 'reach_api', _("Reach API"), ' '); + $form->addGroup($tab, 'reach_api', _("Reach API Configuration"), ' '); + + $tab = array(); + $tab[] = HTML_QuickForm::createElement('radio', 'reach_api_rt', null, _("Yes"), '1'); + $tab[] = HTML_QuickForm::createElement('radio', 'reach_api_rt', null, _("No"), '0'); + $form->addGroup($tab, 'reach_api_rt', _("Reach API Realtime"), ' '); } /** @@ -408,7 +413,12 @@ if ($centreon->optGen['ldap_auth_enable'] == 1) { } } if ($o != "mc") { - $form->setDefaults(array('contact_oreon' => '1', "contact_admin" => '0', "reach_api" => '0')); + $form->setDefaults(array( + 'contact_oreon' => '1', + 'contact_admin' => '0', + 'reach_api' => '0', + 'reach_api_rt' => '0' + )); } $form->addElement('select', 'contact_auth_type', _("Authentication Source"), $auth_type); @@ -706,4 +716,4 @@ function uncheckAllS(object) document.getElementById('sNone').checked = false; } } -</script> \ No newline at end of file +</script> diff --git a/www/include/configuration/configObject/contact/help.php b/www/include/configuration/configObject/contact/help.php index 2898d5ffaa1fe89d5d4a9067284f15ce31057df5..cc45a099629acf8ec914b11f4befa2cc05d814f5 100644 --- a/www/include/configuration/configObject/contact/help.php +++ b/www/include/configuration/configObject/contact/help.php @@ -36,7 +36,8 @@ $help["admin"] = dgettext("help", "Specify if the user has administrative permis $help["autologin_key"] = dgettext("help", "Token used for autologin. Refer to the Centreon documentation to know more about its usage."); $help["auth_type"] = dgettext("help", "Specify the source for user credentials. Choose between Centreon and LDAP, whereas LDAP is only available when configured in Administration Options."); $help["location"] = dgettext("help", "Select the timezone, in which the user resides, from the list. The timezones are listed as time difference to Greenwich Mean Time (GMT) in hours."); -$help["reach_api"] = dgettext("help", "Allow this user to access to Centreon Rest API with its account."); +$help["reach_api"] = dgettext("help", "Allow this user to access to Centreon Rest API Configuration with its account."); +$help["reach_api_rt"] = dgettext("help", "Allow this user to access to Centreon Rest API Realtime with its account."); /* * Additional Information diff --git a/www/include/core/header/htmlHeader.php b/www/include/core/header/htmlHeader.php index 9f007d194602ea7bfddd37ad9f2095f7d8014239..b8838054f5e8a066900e9b38ed6fa9ab22e6b6c2 100644 --- a/www/include/core/header/htmlHeader.php +++ b/www/include/core/header/htmlHeader.php @@ -83,6 +83,7 @@ print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; <script type="text/javascript" src="./include/common/javascript/scriptaculous/scriptaculous.js?load=effects,dragdrop"></script> <script type="text/javascript" src="./include/common/javascript/modalbox.js"></script> <script type="text/javascript" src="./include/common/javascript/jquery/jquery.min.js"></script> + <script type="text/javascript" src="./include/common/javascript/jquery/plugins/toggleClick/jquery.toggleClick.js"></script> <script type="text/javascript" src="./include/common/javascript/jquery/plugins/select2/js/select2.full.min.js"></script> <script type="text/javascript" src="./include/common/javascript/centreon/centreon-select2.js"></script> <script type="text/javascript" src="./include/common/javascript/jquery/jquery-ui.js"></script> diff --git a/www/include/options/accessLists/actionsACL/formActionsAccess.ihtml b/www/include/options/accessLists/actionsACL/formActionsAccess.ihtml index e9263ae42983fb07ac6853d7fdb17a91ae51eb45..586c1c5e500f6dcb5a66798ed37742ad64a90997 100644 --- a/www/include/options/accessLists/actionsACL/formActionsAccess.ihtml +++ b/www/include/options/accessLists/actionsACL/formActionsAccess.ihtml @@ -140,29 +140,29 @@ {literal} <script> - jQuery('input[name=all_service]').change(function(){ - if(jQuery(this).attr('checked')){ - jQuery('.serviceCheckbox input').attr('checked',true); - }else{ - jQuery('.serviceCheckbox input').attr('checked',false); - } - }); + jQuery('input[name=all_service]').change(function(){ + if (jQuery(this).prop('checked')) { + jQuery('.serviceCheckbox input').attr('checked',true); + } else { + jQuery('.serviceCheckbox input').attr('checked',false); + } + }); - jQuery('input[name=all_host]').change(function(){ - if(jQuery(this).attr('checked')){ - jQuery('.hostCheckbox input').attr('checked',true); - }else{ - jQuery('.hostCheckbox input').attr('checked',false); - } - }); + jQuery('input[name=all_host]').change(function(){ + if (jQuery(this).prop('checked')) { + jQuery('.hostCheckbox input').attr('checked',true); + } else { + jQuery('.hostCheckbox input').attr('checked',false); + } + }); - jQuery('input[name=all_engine]').change(function(){ - if(jQuery(this).attr('checked')){ - jQuery('.engineCheckbox input').attr('checked',true); - }else{ - jQuery('.engineCheckbox input').attr('checked',false); - } - }); + jQuery('input[name=all_engine]').change(function(){ + if (jQuery(this).prop('checked')) { + jQuery('.engineCheckbox input').attr('checked',true); + } else { + jQuery('.engineCheckbox input').attr('checked',false); + } + }); </script> {/literal} diff --git a/www/include/reporting/dashboard/template/viewHostGroupLog.ihtml b/www/include/reporting/dashboard/template/viewHostGroupLog.ihtml index 8d7911b5a493d9972b8a834aaffabfd28ef1f956..558a83c0abab5f41169468a4ef2e578996d97b35 100644 --- a/www/include/reporting/dashboard/template/viewHostGroupLog.ihtml +++ b/www/include/reporting/dashboard/template/viewHostGroupLog.ihtml @@ -152,7 +152,7 @@ maxDate: '-1', onSelect : function(data){ jQuery( "select[name='period'] > option[value='']" ).prop('selected', true); - jQuery( "input:[value='custom'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='custom'][name='period_choice'][type='radio']").prop('checked', true); } }); @@ -160,12 +160,12 @@ maxDate: '-1', onSelect : function(data){ jQuery( "select[name='period'] > option[value='']" ).prop('selected', true); - jQuery( "input:[value='custom'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='custom'][name='period_choice'][type='radio']").prop('checked', true); } }); jQuery( "select[name='period']" ).click(function() { - jQuery( "input:[value='preset'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='preset'][name='period_choice'][type='radio']").prop('checked', true); }); jQuery(function () { diff --git a/www/include/reporting/dashboard/template/viewHostLog.ihtml b/www/include/reporting/dashboard/template/viewHostLog.ihtml index a5d20af15dffe4c9d022728b0ba35b0f35f8f5cc..96e21a8645021b4681a025c75ce5de1ba8b316b4 100644 --- a/www/include/reporting/dashboard/template/viewHostLog.ihtml +++ b/www/include/reporting/dashboard/template/viewHostLog.ihtml @@ -175,7 +175,7 @@ maxDate: '-1', onSelect : function(data){ jQuery( "select[name='period'] > option[value='']" ).prop('selected', true); - jQuery( "input:[value='custom'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='custom'][name='period_choice'][type='radio']").prop('checked', true); } }); @@ -183,12 +183,12 @@ maxDate: '-1', onSelect : function(data){ jQuery( "select[name='period'] > option[value='']" ).prop('selected', true); - jQuery( "input:[value='custom'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='custom'][name='period_choice'][type='radio']").prop('checked', true); } }); jQuery( "select[name='period']" ).click(function() { - jQuery( "input:[value='preset'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='preset'][name='period_choice'][type='radio']").prop('checked', true); }); jQuery(function () { diff --git a/www/include/reporting/dashboard/template/viewServicesGroupLog.ihtml b/www/include/reporting/dashboard/template/viewServicesGroupLog.ihtml index 88867fd8ed8aa67ecf64b0dc5790e13270f2560e..4d5b02f13bfca3f50a3f4b092cca8a442aeec6fb 100644 --- a/www/include/reporting/dashboard/template/viewServicesGroupLog.ihtml +++ b/www/include/reporting/dashboard/template/viewServicesGroupLog.ihtml @@ -165,7 +165,7 @@ maxDate: '-1', onSelect : function(data){ jQuery( "select[name='period'] > option[value='']" ).prop('selected', true); - jQuery( "input:[value='custom'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='custom'][name='period_choice'][type='radio']").prop('checked', true); } }); @@ -173,12 +173,12 @@ maxDate: '-1', onSelect : function(data){ jQuery( "select[name='period'] > option[value='']" ).prop('selected', true); - jQuery( "input:[value='custom'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='custom'][name='period_choice'][type='radio']").prop('checked', true); } }); jQuery( "select[name='period']" ).click(function() { - jQuery( "input:[value='preset'][name='period_choice'][type='radio']").prop('checked', true); + jQuery( "input[value='preset'][name='period_choice'][type='radio']").prop('checked', true); }); jQuery(function () { diff --git a/www/install/createTables.sql b/www/install/createTables.sql index 07c4aed9574a389c06e674966359af41f962668c..a9dcc2dc62cfa68ecb6144a6d6cbbd974edf1e8a 100644 --- a/www/install/createTables.sql +++ b/www/install/createTables.sql @@ -749,6 +749,7 @@ CREATE TABLE `contact` ( `contact_location` int(11) DEFAULT '0', `contact_oreon` enum('0','1') DEFAULT NULL, `reach_api` int(11) DEFAULT '0', + `reach_api_rt` int(1) DEFAULT 0, `contact_enable_notifications` enum('0','1','2') DEFAULT '2', `contact_template_id` int(11) DEFAULT NULL, `contact_admin` enum('0','1') DEFAULT '0', diff --git a/www/install/insertBaseConf.sql b/www/install/insertBaseConf.sql index d49f1aab6800da0cc9d422b8f4962e6dd8a6bba4..486d9315eed29dd32afcff580aa39c55c505f360 100644 --- a/www/install/insertBaseConf.sql +++ b/www/install/insertBaseConf.sql @@ -2,7 +2,7 @@ -- Insert version -- -INSERT INTO `informations` (`key` ,`value`) VALUES ('version', '2.8.21'); +INSERT INTO `informations` (`key` ,`value`) VALUES ('version', '2.8.23'); -- -- Contenu de la table `contact` diff --git a/www/install/sql/centreon/Update-DB-2.8.20_to_2.8.21.sql b/www/install/sql/centreon/Update-DB-2.8.20_to_2.8.21.sql index dccabf1b4ac5fbc690593ba6d9adb56b6f74d87e..01f7e7ff91480a587a72c1e73c5850d3919c5d11 100644 --- a/www/install/sql/centreon/Update-DB-2.8.20_to_2.8.21.sql +++ b/www/install/sql/centreon/Update-DB-2.8.20_to_2.8.21.sql @@ -1,2 +1,7 @@ -- Change version of Centreon UPDATE `informations` SET `value` = '2.8.21' WHERE CONVERT( `informations`.`key` USING utf8 ) = 'version' AND CONVERT ( `informations`.`value` USING utf8 ) = '2.8.20' LIMIT 1; + +-- Temporary fix for limit realtime and configuration Rest API +ALTER TABLE `contact` ADD COLUMN `reach_api_rt` int(1) DEFAULT 0 AFTER `reach_api`; +-- Update users with right to reach api +UPDATE contact SET reach_api_rt = "1" WHERE reach_api = "1"; diff --git a/www/install/sql/centreon/Update-DB-2.8.21_to_2.8.22.sql b/www/install/sql/centreon/Update-DB-2.8.21_to_2.8.22.sql new file mode 100644 index 0000000000000000000000000000000000000000..98ca1b3d98e95c2a7222f491a3a91505a733c5df --- /dev/null +++ b/www/install/sql/centreon/Update-DB-2.8.21_to_2.8.22.sql @@ -0,0 +1,2 @@ +-- Change version of Centreon +UPDATE `informations` SET `value` = '2.8.22' WHERE CONVERT( `informations`.`key` USING utf8 ) = 'version' AND CONVERT ( `informations`.`value` USING utf8 ) = '2.8.21' LIMIT 1; diff --git a/www/install/sql/centreon/Update-DB-2.8.22_to_2.8.23.sql b/www/install/sql/centreon/Update-DB-2.8.22_to_2.8.23.sql new file mode 100644 index 0000000000000000000000000000000000000000..488524c7261abd695bc99aaf44e44c258ce25d45 --- /dev/null +++ b/www/install/sql/centreon/Update-DB-2.8.22_to_2.8.23.sql @@ -0,0 +1,2 @@ +-- Change version of Centreon +UPDATE `informations` SET `value` = '2.8.23' WHERE CONVERT( `informations`.`key` USING utf8 ) = 'version' AND CONVERT ( `informations`.`value` USING utf8 ) = '2.8.22' LIMIT 1;