ResourceRepositoryRDB.php 13.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?php

/*
 * Copyright 2005 - 2021 Centreon (https://www.centreon.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For more information : contact@centreon.com
 *
 */
declare(strict_types=1);

namespace Centreon\Infrastructure\Monitoring\Resource;

use Centreon\Domain\Contact\Interfaces\ContactInterface;
use Centreon\Domain\RequestParameters\RequestParameters;
use Centreon\Infrastructure\Repository\AbstractRepositoryDRB;
use Centreon\Domain\Security\AccessGroup;
use Centreon\Domain\Entity\EntityCreator;
use Centreon\Domain\Monitoring\Icon;
use Centreon\Domain\Monitoring\Resource as ResourceEntity;
use Centreon\Domain\Monitoring\ResourceFilter;
use Centreon\Domain\Monitoring\ResourceStatus;
use Centreon\Domain\Monitoring\Interfaces\ResourceRepositoryInterface;
use Centreon\Domain\Monitoring\Notes;
use Centreon\Infrastructure\Monitoring\Resource\Provider\ProviderInterface;
use Centreon\Infrastructure\DatabaseConnection;
use Centreon\Infrastructure\RequestParameters\SqlRequestParametersTranslator;
use Centreon\Infrastructure\CentreonLegacyDB\StatementCollector;
use Centreon\Domain\Repository\RepositoryException;
use Centreon\Infrastructure\RequestParameters\RequestParametersTranslatorException;

/**
 * Database repository for the real time monitoring of services and host.
 *
 * @package Centreon\Infrastructure\Monitoring\Resource
 */
final class ResourceRepositoryRDB extends AbstractRepositoryDRB implements ResourceRepositoryInterface
{
    /**
     * @var SqlRequestParametersTranslator
     */
    private $sqlRequestTranslator;

    /**
     * @var ProviderInterface[]
     */
    private $providers = [];

    /**
     * @var ContactInterface
     */
    private $contact;

    /**
     * @var AccessGroup[] List of access group used to filter the requests
     */
    private $accessGroups = [];

    /**
71
     * @var array<string, string> Association of resource search parameters
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
     */
    private $resourceConcordances = [
        'id' => 'resource.id',
        'name' => 'resource.name',
        'alias' => 'resource.alias',
        'fqdn' => 'resource.fqdn',
        'type' => 'resource.type',
        'status_code' => 'resource.status_code',
        'status' => 'resource.status_name',
        'status_severity_code' => 'resource.status_severity_code',
        'action_url' => 'resource.action_url',
        'parent_name' => 'resource.parent_name',
        'parent_alias' => 'resource.parent_alias',
        'parent_status' => 'resource.parent_status_name',
        'severity_level' => 'resource.severity_level',
        'in_downtime' => 'resource.in_downtime',
        'acknowledged' => 'resource.acknowledged',
        'last_status_change' => 'resource.last_status_change',
        'tries' => 'resource.tries',
        'last_check' => 'resource.last_check',
        'monitoring_server_name' => 'resource.monitoring_server_name',
        'information' => 'resource.information',
    ];

    /**
     * @param DatabaseConnection $pdo
     */
    public function __construct(DatabaseConnection $pdo)
    {
        $this->db = $pdo;
    }

    /**
     * Initialized by the dependency injector.
     *
     * @param SqlRequestParametersTranslator $sqlRequestTranslator
     */
    public function setSqlRequestTranslator(SqlRequestParametersTranslator $sqlRequestTranslator): void
    {
        $this->sqlRequestTranslator = $sqlRequestTranslator;
        $this->sqlRequestTranslator
            ->getRequestParameters()
            ->setConcordanceStrictMode(RequestParameters::CONCORDANCE_MODE_STRICT)
            ->setConcordanceErrorMode(RequestParameters::CONCORDANCE_ERRMODE_SILENT);
    }

    /**
     * @param \Traversable $providers
     * @return void
     */
    public function setProviders(\Traversable $providers): void
    {
        if (count($providers) === 0) {
            throw new \InvalidArgumentException(
                _('You must at least add one resource provider')
            );
        }

        $this->providers = iterator_to_array($providers);
    }

    /**
     * {@inheritDoc}
     */
    public function setContact(ContactInterface $contact): ResourceRepositoryInterface
    {
        $this->contact = $contact;

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function filterByAccessGroups(?array $accessGroups): ResourceRepositoryInterface
    {
        $this->accessGroups = $accessGroups;

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function findResources(ResourceFilter $filter): array
    {
        $resources = [];

        if ($this->hasNotEnoughRightsToContinue()) {
            return $resources;
        }

        $collector = new StatementCollector();
165
        $request = 'SELECT SQL_CALC_FOUND_ROWS '
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
            . 'resource.id, resource.type, resource.name, resource.alias, resource.fqdn, '
            . 'resource.host_id, resource.service_id, '
            . 'resource.status_code, resource.status_name, resource.status_severity_code, ' // status
            . 'resource.icon_name, resource.icon_url, ' // icon
            . 'resource.command_line, resource.timezone, '
            . 'resource.parent_id, resource.parent_name, resource.parent_type, ' // parent
            . 'resource.parent_alias, resource.parent_fqdn, ' // parent
            . 'resource.parent_icon_name, resource.parent_icon_url, ' // parent icon
            . 'resource.action_url, resource.notes_url, resource.notes_label, ' // external urls
            . 'resource.monitoring_server_name, resource.monitoring_server_id, ' // monitoring server
            // parent status
            . 'resource.parent_status_code, resource.parent_status_name, resource.parent_status_severity_code, '
            . 'resource.flapping, resource.percent_state_change, '
            . 'resource.severity_level, ' // severity
            . 'resource.in_downtime, resource.acknowledged, '
            . 'resource.active_checks, resource.passive_checks,'
            . 'resource.last_status_change, '
            . 'resource.last_notification, resource.notification_number, '
184
            . 'resource.state_type, resource.tries, resource.last_check, resource.next_check, '
185
186
            . 'resource.information, resource.performance_data, '
            . 'resource.execution_time, resource.latency, '
187
            . 'resource.notification_enabled, resource.last_time_with_no_issue '
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
            . 'FROM (';

        $subRequests = [];
        foreach ($this->providers as $provider) {
            if ($provider->shouldBeSearched($filter)) {
                if ($this->isAdmin()) {
                    $subRequest = $provider->prepareSubQueryWithoutAcl($filter, $collector);
                } else {
                    $accessGroupIds = array_map(
                        function ($accessGroup) {
                            return $accessGroup->getId();
                        },
                        $this->accessGroups
                    );
                    $subRequest = $provider->prepareSubQueryWithAcl($filter, $collector, $accessGroupIds);
                }
                $subRequests[] = '(' . $subRequest . ')';
            }
        }

        if (!$subRequests) {
            $this->sqlRequestTranslator->getRequestParameters()->setTotal(0);

            return [];
        }

214
        $request .= implode(' UNION ALL ', $subRequests);
215
216
217
218
        unset($subRequests);

        $request .= ') AS `resource`';

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
        $hasWhereCondition = false;

        // Search
        $this->sqlRequestTranslator->setConcordanceArray($this->resourceConcordances);
        try {
            $searchRequest = $this->sqlRequestTranslator->translateSearchParameterToSql();
        } catch (RequestParametersTranslatorException $ex) {
            throw new RepositoryException($ex->getMessage(), 0, $ex);
        }

        if ($searchRequest !== null) {
            $hasWhereCondition = true;
            $request .= $searchRequest;
        }

234
        // apply the host group filter to SQL query
235
        if ($filter->getHostgroupNames()) {
236
237
            $groupList = [];

238
239
            foreach ($filter->getHostgroupNames() as $index => $groupName) {
                $key = ":resourceHostgroupName_{$index}";
240
                $groupList[] = $key;
241
                $collector->addValue($key, $groupName, \PDO::PARAM_STR);
242
243
            }

244
245
246
247
248
249
250
251
252
253
254
            $request .= ($hasWhereCondition === false) ? ' WHERE ' : ' AND ';
            $hasWhereCondition = true;

            $request .= ' EXISTS (
                SELECT 1 FROM `:dbstg`.`hosts_hostgroups` AS hhg
                WHERE hhg.host_id = resource.host_id
                    AND EXISTS (
                        SELECT 1 FROM `:dbstg`.`hostgroups` AS hg
                        WHERE hg.hostgroup_id = hhg.hostgroup_id AND hg.name IN (' . implode(', ', $groupList) . ')
                        LIMIT 1)
                LIMIT 1) ';
255
256
257
258
259
260
261
        }

        /**
         * If we specify that user only wants resources with available performance datas.
         * Then only resources with existing metrics referencing index_data services will be returned.
         */
        if ($filter->getOnlyWithPerformanceData() === true) {
262
263
264
265
            $request .= $hasWhereCondition ? ' AND ' : ' WHERE ';
            $request .= ' EXISTS (
                SELECT 1 FROM `:dbstg`.index_data AS idata
                  WHERE idata.host_id = resource.parent_id
266
267
                  AND idata.service_id = resource.id
                  AND resource.type = "service"
268
269
270
                AND EXISTS (SELECT 1 FROM `:dbstg`.metrics AS m
                  WHERE m.index_id = idata.id
                  AND m.hidden = "0" LIMIT 1) LIMIT 1) ';
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
        }

        foreach ($this->sqlRequestTranslator->getSearchValues() as $key => $data) {
            $collector->addValue($key, current($data), key($data));
        }

        // Sort
        $request .= $this->sqlRequestTranslator->translateSortParameterToSql()
            ?: ' ORDER BY resource.status_name DESC, resource.name ASC';

        // Pagination
        $request .= $this->sqlRequestTranslator->translatePaginationToSql();

        $statement = $this->db->prepare(
            $this->translateDbName($request)
        );
        $collector->bind($statement);

        $statement->execute();

        $this->sqlRequestTranslator->getRequestParameters()->setTotal(
            (int)$this->db->query('SELECT FOUND_ROWS()')->fetchColumn()
        );

        while ($result = $statement->fetch()) {
            $resources[] = $this->parseResource($result);
        }

        return $resources;
    }

    /**
     * Parse array data from DB into Resource model
     *
     * @param array $data
     * @return ResourceEntity
     * @throws \Exception
     */
    protected function parseResource(array $data): ResourceEntity
    {
        $resource = EntityCreator::createEntityByArray(
            ResourceEntity::class,
            $data
        );

        $resource->setHostId((int)$data['host_id']);
        $resource->setServiceId((int)$data['service_id']);

        // parse ResourceStatus object
        $resource->setStatus(EntityCreator::createEntityByArray(
            ResourceStatus::class,
            $data,
            'status_'
        ));

        // parse Icon object
        $icon = EntityCreator::createEntityByArray(
            Icon::class,
            $data,
            'icon_'
        );

        if ($icon->getUrl()) {
            $resource->setIcon($icon);
        }

337
338
        $parent = null;
        if ($resource->getType() === ResourceEntity::TYPE_SERVICE) {
339
340
341
            // parse parent Resource object
            $parent = EntityCreator::createEntityByArray(
                ResourceEntity::class,
342
                $data,
343
                'parent_'
344
345
            );

346
347
348
349
350
351
            if ($parent->getId()) {
                $parentIcon = EntityCreator::createEntityByArray(
                    Icon::class,
                    $data,
                    'parent_icon_'
                );
352

353
354
355
                if ($parentIcon->getUrl()) {
                    $parent->setIcon($parentIcon);
                }
356

357
358
359
360
361
362
363
                $parentStatus = EntityCreator::createEntityByArray(
                    ResourceStatus::class,
                    $data,
                    'parent_status_'
                );
                $parent->setStatus($parentStatus);
            }
364
365
        }

366
367
        $resource->setParent($parent);

368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
        // Setting the External links
        $externalLinks = $resource->getLinks()->getExternals();
        $externalLinks->setActionUrl($data['action_url']);
        $notes = (new Notes())
            ->setUrl($data['notes_url'])
            ->setLabel($data['notes_label']);
        $externalLinks->setNotes($notes);

        return $resource;
    }

    /**
     * {@inheritDoc}
     */
    public function extractResourcesWithGraphData(array $resources): array
    {
        foreach ($this->providers as $provider) {
            $resources = $provider->excludeResourcesWithoutMetrics($resources);
        }

        return $resources;
    }

    /**
     * Check if the contact is admin
     *
     * @return bool
     */
    private function isAdmin(): bool
    {
        return ($this->contact !== null)
            ? $this->contact->isAdmin()
            : false;
    }

    /**
     * @return bool Return FALSE if the contact is an admin or has at least one access group.
     */
    private function hasNotEnoughRightsToContinue(): bool
    {
        return ($this->contact !== null)
            ? !($this->contact->isAdmin() || count($this->accessGroups) > 0)
            : count($this->accessGroups) == 0;
    }
}