From 8b9cfef5488868424faa3531aebc40fd3cbbf2f0 Mon Sep 17 00:00:00 2001 From: nook24 Date: Fri, 30 Jun 2023 16:38:54 +0200 Subject: [PATCH 1/4] ITC-3049 Add locking by semaphore of the containers table the avoid broken nested set when concurrent requests modify the tree https://github.com/cakephp/cakephp/issues/17174 https://github.com/it-novum/openITCOCKPIT/issues/1525 --- composer.json | 1 + src/Controller/ContactgroupsController.php | 17 +++++++- src/Controller/ContainersController.php | 11 ++++- src/Controller/HostgroupsController.php | 15 +++++++ src/Controller/LocationsController.php | 13 ++++++ src/Controller/ServicegroupsController.php | 15 +++++++ .../ServicetemplategroupsController.php | 20 +++++++-- src/Controller/TenantsController.php | 13 +++++- src/Model/Table/ContainersTable.php | 43 +++++++++++++++++++ 9 files changed, 141 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index bb4416ea17..946bd20b98 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "ext-sockets": "*", "ext-xmlrpc": "*", "ext-zip": "*", + "ext-sysvsem": "*", "azuyalabs/yasumi": "^2.2", "cakephp/authentication": "^2.0", "cakephp/authorization": "^2.0", diff --git a/src/Controller/ContactgroupsController.php b/src/Controller/ContactgroupsController.php index 2787795dd1..48c7b9f655 100644 --- a/src/Controller/ContactgroupsController.php +++ b/src/Controller/ContactgroupsController.php @@ -124,6 +124,11 @@ public function add() { $contactgroup->set('uuid', UUID::v4()); $contactgroup->get('container')->set('containertype_id', CT_CONTACTGROUP); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $ContactgroupsTable->save($contactgroup); if ($contactgroup->hasErrors()) { $this->response = $this->response->withStatus(400); @@ -197,6 +202,11 @@ public function edit($id = null) { //Update contact data $User = new User($this->getUser()); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $contactgroupEntity = $ContactgroupsTable->get($id, [ 'contain' => [ 'Containers' @@ -254,10 +264,15 @@ public function delete($id) { /** @var $ContactgroupsTable ContactgroupsTable */ $ContactgroupsTable = TableRegistry::getTableLocator()->get('Contactgroups'); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + if (!$ContactgroupsTable->existsById($id)) { throw new NotFoundException(__('Contact group not found')); } + $ContainersTable->acquireLock(); + $contactgroupEntity = $ContactgroupsTable->get($id, [ 'contain' => [ 'Containers' @@ -288,8 +303,6 @@ public function delete($id) { return; } - /** @var $ContainersTable ContainersTable */ - $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); $container = $ContainersTable->get($contactgroupEntity->get('container')->get('id'), [ 'contain' => [ 'Contactgroups' diff --git a/src/Controller/ContainersController.php b/src/Controller/ContainersController.php index 70afefcdb0..1afc979fe1 100644 --- a/src/Controller/ContainersController.php +++ b/src/Controller/ContainersController.php @@ -107,6 +107,8 @@ public function add() { return; } + $ContainersTable->acquireLock(); + $ContainersTable->save($container); if ($container->hasErrors()) { $this->response = $this->response->withStatus(400); @@ -154,6 +156,9 @@ public function edit() { if (!$ContainersTable->existsById($containerId)) { throw new NotFoundException(__('Invalid container')); } + + $ContainersTable->acquireLock(); + $container = $ContainersTable->get($containerId); $containerForChangelog = $container->toArray(); @@ -416,6 +421,9 @@ public function delete($id = null) { /** @var $ContainersTable ContainersTable */ $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $container = $ContainersTable->find() ->where([ 'Containers.id' => $id, @@ -424,7 +432,8 @@ public function delete($id = null) { ->first(); if (empty($container)) { - return $this->render403(); + $this->render403(); + return; } $containerForChangelog = $container->toArray(); diff --git a/src/Controller/HostgroupsController.php b/src/Controller/HostgroupsController.php index 6969934c12..7386ed2180 100644 --- a/src/Controller/HostgroupsController.php +++ b/src/Controller/HostgroupsController.php @@ -154,6 +154,11 @@ public function add() { $hostgroup->set('uuid', UUID::v4()); $hostgroup->get('container')->set('containertype_id', CT_HOSTGROUP); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $HostgroupsTable->save($hostgroup); if ($hostgroup->hasErrors()) { $this->response = $this->response->withStatus(400); @@ -231,6 +236,12 @@ public function edit($id = null) { if ($this->request->is('post') && $this->isAngularJsRequest()) { //Update contact data $User = new User($this->getUser()); + + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $hostgroupEntity = $HostgroupsTable->get($id, [ 'contain' => [ 'Containers' @@ -297,6 +308,8 @@ public function delete($id = null) { throw new NotFoundException(__('Invalid Hostgroup')); } + $ContainersTable->acquireLock(); + $hostgroup = $HostgroupsTable->getHostgroupById($id); $container = $ContainersTable->get($hostgroup->get('container')->get('id'), [ 'contain' => [ @@ -729,6 +742,8 @@ public function append() { throw new NotFoundException(__('Invalid Hostgroup')); } + $ContainersTable->acquireLock(); + $hostgroup = $HostgroupsTable->getHostgroupForEdit($id); $hostgroupForChangelog = $hostgroup; if (!$this->allowedByContainerId($hostgroup['Hostgroup']['container']['parent_id'])) { diff --git a/src/Controller/LocationsController.php b/src/Controller/LocationsController.php index 1c408f5cc5..5aaf7276f9 100644 --- a/src/Controller/LocationsController.php +++ b/src/Controller/LocationsController.php @@ -122,6 +122,11 @@ public function add() { $location->set('uuid', UUID::v4()); $location->container->containertype_id = CT_LOCATION; + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $LocationsTable->save($location); if ($location->hasErrors()) { $this->response = $this->response->withStatus(400); @@ -194,6 +199,12 @@ public function edit($id = null) { } if ($this->request->is('post') && $this->isAngularJsRequest()) { + + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $oldLocation = $LocationsTable->get($id, [ 'contain' => ['Containers'] ]); @@ -269,6 +280,8 @@ public function delete($containerId = null) { throw new NotFoundException(__('Invalid container')); } + $ContainersTable->acquireLock(); + $container = $ContainersTable->get($containerId); try { diff --git a/src/Controller/ServicegroupsController.php b/src/Controller/ServicegroupsController.php index 439354b5bd..e901879850 100644 --- a/src/Controller/ServicegroupsController.php +++ b/src/Controller/ServicegroupsController.php @@ -157,6 +157,11 @@ public function add() { $servicegroup->set('uuid', UUID::v4()); $servicegroup->get('container')->set('containertype_id', CT_SERVICEGROUP); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $ServicegroupsTable->save($servicegroup); if ($servicegroup->hasErrors()) { $this->response = $this->response->withStatus(400); @@ -234,6 +239,12 @@ public function edit($id = null) { if ($this->request->is('post') && $this->isAngularJsRequest()) { $User = new User($this->getUser()); + + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $servicegroupEntity = $ServicegroupsTable->get($id, [ 'contain' => [ 'Containers' @@ -301,6 +312,8 @@ public function delete($id = null) { throw new NotFoundException(__('Invalid Servicegroup')); } + $ContainersTable->acquireLock(); + $servicegroup = $ServicegroupsTable->getServicegroupById($id); $container = $ContainersTable->get($servicegroup->get('container')->get('id'), [ 'contain' => [ @@ -391,6 +404,8 @@ public function append() { throw new NotFoundException(__('Invalid Servicegroup')); } + $ContainersTable->acquireLock(); + $servicegroup = $ServicegroupsTable->getServicegroupForEdit($id); $servicegroupForChangelog = $servicegroup; if (!$this->allowedByContainerId($servicegroup['Servicegroup']['container']['parent_id'])) { diff --git a/src/Controller/ServicetemplategroupsController.php b/src/Controller/ServicetemplategroupsController.php index 194c0af2ba..ac8ec707d3 100644 --- a/src/Controller/ServicetemplategroupsController.php +++ b/src/Controller/ServicetemplategroupsController.php @@ -145,6 +145,11 @@ public function add() { $servicetemplategroup->set('uuid', UUID::v4()); $servicetemplategroup->get('container')->set('containertype_id', CT_SERVICETEMPLATEGROUP); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $ServicetemplategroupsTable->save($servicetemplategroup); if ($servicetemplategroup->hasErrors()) { $this->response = $this->response->withStatus(400); @@ -225,6 +230,11 @@ public function edit($id = null) { //Update service template group data $User = new User($this->getUser()); + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $servicetemplategroupEntity = $ServicetemplategroupsTable->get($id, [ 'contain' => [ 'Containers' @@ -304,6 +314,8 @@ public function append() { throw new NotFoundException(__('Invalid service template group')); } + $ContainersTable->acquireLock(); + $servicetemplategroup = $ServicetemplategroupsTable->getServicetemplategroupForEdit($id); $servicetemplategroupForChangelog = $servicetemplategroup; if (!$this->allowedByContainerId($servicetemplategroup['Servicetemplategroup']['container']['parent_id'])) { @@ -425,6 +437,11 @@ public function delete($id = null) { throw new NotFoundException(__('Service template group not found')); } + /** @var $ContainersTable ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $servicetemplategroupEntity = $ServicetemplategroupsTable->get($id, [ 'contain' => [ 'Containers' @@ -436,9 +453,6 @@ public function delete($id = null) { return; } - - /** @var $ContainersTable ContainersTable */ - $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); $container = $ContainersTable->get($servicetemplategroupEntity->get('container')->get('id'), [ 'contain' => [ 'Servicetemplategroups' diff --git a/src/Controller/TenantsController.php b/src/Controller/TenantsController.php index 41cc17833f..0d07a717c4 100644 --- a/src/Controller/TenantsController.php +++ b/src/Controller/TenantsController.php @@ -120,6 +120,10 @@ public function add() { $tenant->container->parent_id = ROOT_CONTAINER; $tenant->container->containertype_id = CT_TENANT; + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); $TenantsTable->save($tenant); if ($tenant->hasErrors()) { @@ -193,6 +197,12 @@ public function edit($id = null) { } if ($this->request->is('post') && $this->isAngularJsRequest()) { + + /** @var ContainersTable $ContainersTable */ + $ContainersTable = TableRegistry::getTableLocator()->get('Containers'); + + $ContainersTable->acquireLock(); + $oldTenant = $TenantsTable->get($id, [ 'contain' => ['Containers'] ]); @@ -271,10 +281,11 @@ public function delete($containerId = null) { throw new NotFoundException(__('Invalid container')); } + $ContainersTable->acquireLock(); try { $tenant = $TenantsTable->getTenantByContainerId($containerId); - }catch (RecordNotFoundException $e){ + } catch (RecordNotFoundException $e) { throw new NotFoundException(__('Invalid tenant')); } $tenantForChangelog = $tenant; diff --git a/src/Model/Table/ContainersTable.php b/src/Model/Table/ContainersTable.php index 137faae812..7517d98bc7 100644 --- a/src/Model/Table/ContainersTable.php +++ b/src/Model/Table/ContainersTable.php @@ -6,9 +6,12 @@ use Cake\Cache\Cache; use Cake\Core\Plugin; use Cake\Database\Expression\QueryExpression; +use Cake\Datasource\EntityInterface; use Cake\Datasource\Exception\RecordNotFoundException; +use Cake\Event\EventInterface; use Cake\Http\Exception\ForbiddenException; use Cake\Http\Exception\NotFoundException; +use Cake\Log\Log; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\ORM\TableRegistry; @@ -74,6 +77,20 @@ class ContainersTable extends Table { */ private $containerCache = null; + /** + * @var int|null + */ + private $semaphoreKey = null; + + /** + * @var \SysvSemaphore + * Provided by ext-sysvsem. + * Default on Ubuntu and Debian. + * For RHEL you have to install php-process + * + */ + private $semaphore; + /** * Initialize method * @@ -1229,4 +1246,30 @@ function ($v) { sort($visibleContainerIds); return $visibleContainerIds; } + + /** + * Will create a Semaphore with a limit of 1 to avoid multiple operations running concurrently on the containers table + * This method blocks (if necessary) until the semaphore can be acquired + * + * https://github.com/cakephp/cakephp/issues/14983#issuecomment-1613491168 + * @param bool $auto_release + * @return bool + */ + public function acquireLock(bool $auto_release = true) { + if ($this->semaphoreKey === null) { + $this->semaphoreKey = ftok(__FILE__, 'c'); + $this->semaphore = sem_get($this->semaphoreKey, 1, 0666, $auto_release); + } + + return sem_acquire($this->semaphore); + } + + /** + * @return bool + */ + public function releaseLock() { + if (!is_null($this->semaphore)) { + return sem_release($this->semaphore); + } + } } From 7a9c32711a5326a359cac0dee1c8043450fbab36 Mon Sep 17 00:00:00 2001 From: nook24 Date: Fri, 30 Jun 2023 17:23:04 +0200 Subject: [PATCH 2/4] ITC-3049 Fix style issue of progress bar in mass actions --- src/Template/Angular/acknowledge_host.php | 4 ++-- src/Template/Angular/acknowledge_service.php | 8 ++++---- src/Template/Angular/disable_host_flap_detection.php | 4 ++-- src/Template/Angular/disable_host_notifications.php | 6 +++--- .../Angular/disable_service_flap_detection.php | 6 +++--- src/Template/Angular/disable_service_notifications.php | 6 +++--- src/Template/Angular/downtime_host.php | 8 ++++---- src/Template/Angular/downtime_service.php | 8 ++++---- src/Template/Angular/enable_host_flap_detection.php | 6 +++--- src/Template/Angular/enable_host_notifications.php | 6 +++--- src/Template/Angular/enable_service_flap_detection.php | 6 +++--- src/Template/Angular/enable_service_notifications.php | 6 +++--- src/Template/Angular/executing.php | 6 +++--- src/Template/Angular/mass_activate.php | 8 ++++---- src/Template/Angular/mass_deactivate.php | 8 ++++---- src/Template/Angular/mass_delete.php | 8 ++++---- src/Template/Angular/mass_delete_acknowledgements.php | 8 ++++---- src/Template/Angular/mass_delete_host_downtimes.php | 8 ++++---- src/Template/Angular/mass_delete_service_downtimes.php | 8 ++++---- src/Template/Angular/reload_required.php | 10 +++++----- src/Template/Angular/reschedule_host.php | 6 +++--- src/Template/Angular/send_host_notification.php | 8 ++++---- src/Template/Angular/send_service_notification.php | 8 ++++---- src/Template/Angular/submit_host_result.php | 8 ++++---- src/Template/Angular/submit_service_result.php | 8 ++++---- 25 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/Template/Angular/acknowledge_host.php b/src/Template/Angular/acknowledge_host.php index 93f3522286..c275666bce 100644 --- a/src/Template/Angular/acknowledge_host.php +++ b/src/Template/Angular/acknowledge_host.php @@ -89,8 +89,8 @@ class="custom-control-input"

-
-
+
+
diff --git a/src/Template/Angular/acknowledge_service.php b/src/Template/Angular/acknowledge_service.php index 02dc387ec5..2c7ac9e3f6 100644 --- a/src/Template/Angular/acknowledge_service.php +++ b/src/Template/Angular/acknowledge_service.php @@ -66,12 +66,12 @@ class="custom-control-input" -
+

-
-
-
+
+
+
diff --git a/src/Template/Angular/disable_host_flap_detection.php b/src/Template/Angular/disable_host_flap_detection.php index 5a0067e90f..06651c1521 100644 --- a/src/Template/Angular/disable_host_flap_detection.php +++ b/src/Template/Angular/disable_host_flap_detection.php @@ -30,8 +30,8 @@
-
-
+
+
diff --git a/src/Template/Angular/disable_host_notifications.php b/src/Template/Angular/disable_host_notifications.php index ac79ec6ff5..3afc046d11 100644 --- a/src/Template/Angular/disable_host_notifications.php +++ b/src/Template/Angular/disable_host_notifications.php @@ -46,9 +46,9 @@ class="form-control"
-
-
-
+
+
+
diff --git a/src/Template/Angular/disable_service_flap_detection.php b/src/Template/Angular/disable_service_flap_detection.php index 1db65a86f7..c0942acffc 100644 --- a/src/Template/Angular/disable_service_flap_detection.php +++ b/src/Template/Angular/disable_service_flap_detection.php @@ -26,9 +26,9 @@
-
-
-
+
+
+
diff --git a/src/Template/Angular/disable_service_notifications.php b/src/Template/Angular/disable_service_notifications.php index 9299eb9cd8..7a8c29419d 100644 --- a/src/Template/Angular/disable_service_notifications.php +++ b/src/Template/Angular/disable_service_notifications.php @@ -29,9 +29,9 @@
-
-
-
+
+
+
diff --git a/src/Template/Angular/downtime_host.php b/src/Template/Angular/downtime_host.php index 5147bf197d..1bf5e1df41 100644 --- a/src/Template/Angular/downtime_host.php +++ b/src/Template/Angular/downtime_host.php @@ -112,12 +112,12 @@ class="col-md-offset-2 col-xs-12 col-md-10">
-
+

-
-
-
+
+
+
diff --git a/src/Template/Angular/downtime_service.php b/src/Template/Angular/downtime_service.php index 6574b3e82c..1f586aa4dd 100644 --- a/src/Template/Angular/downtime_service.php +++ b/src/Template/Angular/downtime_service.php @@ -86,12 +86,12 @@ class="col-md-offset-2 col-xs-12 col-md-10">
-
+

-
-
-
+
+
+
diff --git a/src/Template/Angular/enable_host_flap_detection.php b/src/Template/Angular/enable_host_flap_detection.php index aba7da8dde..7abfd98c4b 100644 --- a/src/Template/Angular/enable_host_flap_detection.php +++ b/src/Template/Angular/enable_host_flap_detection.php @@ -29,9 +29,9 @@
-
-
-
+
+
+
diff --git a/src/Template/Angular/enable_host_notifications.php b/src/Template/Angular/enable_host_notifications.php index 74831a1682..accd90fada 100644 --- a/src/Template/Angular/enable_host_notifications.php +++ b/src/Template/Angular/enable_host_notifications.php @@ -46,9 +46,9 @@ class="form-control"
-
-
-
+
+
+
diff --git a/src/Template/Angular/enable_service_flap_detection.php b/src/Template/Angular/enable_service_flap_detection.php index 41d83e2217..33c25b964e 100644 --- a/src/Template/Angular/enable_service_flap_detection.php +++ b/src/Template/Angular/enable_service_flap_detection.php @@ -26,9 +26,9 @@
-
-
-
+
+
+
diff --git a/src/Template/Angular/enable_service_notifications.php b/src/Template/Angular/enable_service_notifications.php index 141dc92bee..c2dcd2fb8d 100644 --- a/src/Template/Angular/enable_service_notifications.php +++ b/src/Template/Angular/enable_service_notifications.php @@ -29,9 +29,9 @@
-
-
-
+
+
+
diff --git a/src/Template/Angular/executing.php b/src/Template/Angular/executing.php index 3e002ea65b..3132d3d2fd 100644 --- a/src/Template/Angular/executing.php +++ b/src/Template/Angular/executing.php @@ -12,9 +12,9 @@