Passed
Push — develop ( 07b509...d5f888 )
by Портнов
05:34
created

WorkerModelsEvents::reloadNats()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\Workers;
21
22
require_once 'Globals.php';
23
24
use MikoPBX\Common\Models\{AsteriskManagerUsers,
25
    CallQueueMembers,
26
    CallQueues,
27
    Codecs,
28
    ConferenceRooms,
29
    CustomFiles,
30
    DialplanApplications,
31
    ExtensionForwardingRights,
32
    Extensions,
33
    ExternalPhones,
34
    Fail2BanRules,
35
    FirewallRules,
36
    Iax,
37
    IncomingRoutingTable,
38
    IvrMenu,
39
    IvrMenuActions,
40
    LanInterfaces,
41
    NetworkFilters,
42
    OutgoingRoutingTable,
43
    OutWorkTimes,
44
    PbxExtensionModules,
45
    PbxSettings,
46
    Sip,
47
    SoundFiles
48
};
49
use MikoPBX\Common\Providers\BeanstalkConnectionModelsProvider;
50
use MikoPBX\Core\Asterisk\Configs\QueueConf;
51
use MikoPBX\Core\System\{BeanstalkClient,
52
    Configs\CronConf,
53
    Configs\IptablesConf,
54
    Configs\NatsConf,
55
    Configs\NginxConf,
56
    Configs\NTPConf,
57
    Configs\PHPConf,
58
    Configs\SSHConf,
59
    Configs\SyslogConf,
60
    PBX,
61
    Processes,
62
    System,
63
    Util};
64
use MikoPBX\PBXCoreREST\Workers\WorkerApiCommands;
65
use Phalcon\Di;
66
use Pheanstalk\Contract\PheanstalkInterface;
67
use Throwable;
68
69
ini_set('error_reporting', E_ALL);
70
ini_set('display_startup_errors', 1);
71
72
class WorkerModelsEvents extends WorkerBase
73
{
74
    private const R_MANAGERS = 'reloadManager';
75
76
    private const R_QUEUES = 'reloadQueues';
77
78
    private const R_DIALPLAN = 'reloadDialplan';
79
80
    private const R_CUSTOM_F = 'updateCustomFiles';
81
82
    private const R_FIREWALL = 'reloadFirewall';
83
84
    private const R_NETWORK = 'networkReload';
85
86
    private const R_IAX = 'reloadIax';
87
88
    private const R_SIP = 'reloadSip';
89
90
    private const R_FEATURES = 'reloadFeatures';
91
92
    private const R_CRON = 'reloadCron';
93
94
    public const  R_NGINX = 'reloadNginx';
95
96
    private const R_PHP_FPM = 'reloadPHPFPM';
97
98
    private const R_TIMEZONE = 'updateTomeZone';
99
    private const R_SYSLOG   = 'restartSyslogD';
100
101
    private const R_SSH = 'reloadSSH';
102
103
    private const R_LICENSE = 'reloadLicense';
104
105
    private const R_NATS = 'reloadNats';
106
107
    private const R_VOICEMAIL = 'reloadVoicemail';
108
109
    private const R_REST_API_WORKER = 'reloadRestAPIWorker';
110
111
    private const R_CALL_EVENTS_WORKER = 'reloadWorkerCallEvents';
112
113
    private const R_PBX_EXTENSION_STATE = 'afterModuleStateChanged';
114
115
    private const R_MOH = 'reloadMoh';
116
117
    private const R_NTP = 'reloadNtp';
118
119
    private int $last_change;
120
    private array $modified_tables;
121
122
    private int $timeout = 2;
123
    private array $arrObject;
124
    private array $PRIORITY_R;
125
126
127
    /**
128
     * Entry point
129
     *
130
     * @param $argv
131
     */
132
    public function start($argv): void
133
    {
134
        $this->arrObject = $this->di->getShared('pbxConfModules');
135
136
        $this->PRIORITY_R = [
137
            self::R_TIMEZONE,
138
            self::R_SYSLOG,
139
            self::R_REST_API_WORKER,
140
            self::R_NETWORK,
141
            self::R_FIREWALL,
142
            self::R_SSH,
143
            self::R_LICENSE,
144
            self::R_NATS,
145
            self::R_NTP,
146
            self::R_PHP_FPM,
147
            self::R_NGINX,
148
            self::R_CRON,
149
            self::R_FEATURES,
150
            self::R_SIP,
151
            self::R_IAX,
152
            self::R_DIALPLAN,
153
            self::R_QUEUES,
154
            self::R_MANAGERS,
155
            self::R_CUSTOM_F,
156
            self::R_VOICEMAIL,
157
            self::R_MOH,
158
            self::R_CALL_EVENTS_WORKER,
159
            self::R_PBX_EXTENSION_STATE,
160
        ];
161
162
        $this->modified_tables = [];
163
164
        $client = $this->di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
165
        $client->subscribe(self::class, [$this, 'processModelChanges']);
166
        $client->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
167
        $client->setTimeoutHandler([$this, 'timeoutHandler']);
168
169
        while ($this->needRestart === false) {
170
            $client->wait();
171
        }
172
        // Execute all collected changes before exit
173
        $this->timeoutHandler();
174
    }
175
176
    /**
177
     * Parses for received Beanstalk message
178
     *
179
     * @param BeanstalkClient $message
180
     * @throws \JsonException
181
     */
182
    public function processModelChanges(BeanstalkClient $message): void
183
    {
184
        $data = $message->getBody();
185
        $receivedMessage = null;
186
        if(in_array($data, $this->PRIORITY_R, true)){
187
            $this->modified_tables[$data] = true;
188
        }else{
189
            $receivedMessage = json_decode($message->getBody(), true, 512, JSON_THROW_ON_ERROR);
190
            $this->fillModifiedTables($receivedMessage);
191
192
        }
193
        $this->startReload();
194
        if(!$receivedMessage){
195
            return;
196
        }
197
        // Send information about models changes to additional modules
198
        foreach ($this->arrObject as $appClass) {
199
            $appClass->modelsEventChangeData($receivedMessage);
200
        }
201
    }
202
203
    /**
204
     * Collects changes to determine which modules must be reloaded or reconfigured
205
     *
206
     * @param array $data
207
     */
208
    private function fillModifiedTables(array $data): void
209
    {
210
211
        $count_changes = count($this->modified_tables);
212
        $called_class  = $data['model'] ?? '';
213
        Util::sysLogMsg(__METHOD__, "New changes ".$called_class, LOG_DEBUG);
214
215
        // Clear all caches on any changed models on backend
216
        PbxSettings::clearCache($called_class, false);
217
218
        // Get new settings gor dependence modules
219
        foreach ($this->arrObject as $appClass) {
220
            $dependencies = $appClass->dependenceModels();
221
            if (in_array($called_class, $dependencies, true)) {
222
                $appClass->getSettings();
223
            }
224
        }
225
226
        switch ($called_class) {
227
            case AsteriskManagerUsers::class:
228
                $this->modified_tables[self::R_MANAGERS] = true;
229
                break;
230
            case CallQueueMembers::class:
231
                $this->modified_tables[self::R_QUEUES] = true;
232
                break;
233
            case CallQueues::class:
234
                $this->modified_tables[self::R_QUEUES]   = true;
235
                $this->modified_tables[self::R_DIALPLAN] = true;
236
                break;
237
            case ExternalPhones::class:
238
            case Extensions::class:
239
            case DialplanApplications::class:
240
            case IncomingRoutingTable::class:
241
            case IvrMenu::class:
242
            case IvrMenuActions::class:
243
            case OutgoingRoutingTable::class:
244
            case OutWorkTimes::class:
245
            case ConferenceRooms::class:
246
                $this->modified_tables[self::R_DIALPLAN] = true;
247
                break;
248
            case CustomFiles::class:
249
                $this->modified_tables[self::R_CUSTOM_F] = true;
250
                break;
251
            case Sip::class:
252
            case ExtensionForwardingRights::class:
253
                $this->modified_tables[self::R_SIP]      = true;
254
                $this->modified_tables[self::R_DIALPLAN] = true;
255
                break;
256
            case FirewallRules::class:
257
            case Fail2BanRules::class:
258
                $this->modified_tables[self::R_FIREWALL] = true;
259
                break;
260
            case Iax::class:
261
                $this->modified_tables[self::R_IAX]      = true;
262
                $this->modified_tables[self::R_DIALPLAN] = true;
263
                break;
264
            case Codecs::class:
265
                $this->modified_tables[self::R_IAX] = true;
266
                $this->modified_tables[self::R_SIP] = true;
267
                break;
268
            case SoundFiles::class:
269
                $this->modified_tables[self::R_MOH]      = true;
270
                $this->modified_tables[self::R_DIALPLAN] = true;
271
                break;
272
            case LanInterfaces::class:
273
                $this->modified_tables[self::R_NETWORK] = true;
274
                $this->modified_tables[self::R_IAX]     = true;
275
                $this->modified_tables[self::R_SIP]     = true;
276
                break;
277
            case NetworkFilters::class:
278
                $this->modified_tables[self::R_FIREWALL] = true;
279
                $this->modified_tables[self::R_SIP]      = true;
280
                $this->modified_tables[self::R_MANAGERS] = true;
281
                break;
282
            case PbxSettings::class:
283
                $pbxSettings = PbxSettings::findFirstByKey($data['recordId']);
284
                if ($pbxSettings === null) {
285
                    return;
286
                }
287
                if ($pbxSettings->itHasFeaturesSettingsChanges()) {
288
                    $this->modified_tables[self::R_FEATURES] = true;
289
                    $this->modified_tables[self::R_DIALPLAN] = true;
290
                }
291
                if ($pbxSettings->itHasAMIParametersChanges()) {
292
                    $this->modified_tables[self::R_MANAGERS] = true;
293
                }
294
                if ($pbxSettings->itHasIaxParametersChanges()) {
295
                    $this->modified_tables[self::R_IAX] = true;
296
                }
297
                if ($pbxSettings->itHasSipParametersChanges()) {
298
                    $this->modified_tables[self::R_SIP] = true;
299
                }
300
                if ($pbxSettings->itHasSSHParametersChanges()) {
301
                    $this->modified_tables[self::R_SSH] = true;
302
                }
303
                if ($pbxSettings->itHasFirewallParametersChanges()) {
304
                    $this->modified_tables[self::R_FIREWALL] = true;
305
                }
306
                if ($pbxSettings->itHasWebParametersChanges()) {
307
                    $this->modified_tables[self::R_NGINX] = true;
308
                }
309
                if ($pbxSettings->itHasCronParametersChanges()) {
310
                    $this->modified_tables[self::R_CRON] = true;
311
                }
312
                if ($pbxSettings->itHasDialplanParametersChanges()) {
313
                    $this->modified_tables[self::R_DIALPLAN] = true;
314
                }
315
                if ($pbxSettings->itHasVoiceMailParametersChanges()) {
316
                    $this->modified_tables[self::R_VOICEMAIL] = true;
317
                }
318
                if ($pbxSettings->itHasVisualLanguageSettings()) {
319
                    $this->modified_tables[self::R_REST_API_WORKER] = true;
320
                }
321
                if ($pbxSettings->itHasLicenseSettings()) {
322
                    $this->modified_tables[self::R_LICENSE] = true;
323
                    $this->modified_tables[self::R_NATS]    = true;
324
                }
325
                if ($pbxSettings->itHasTimeZoneSettings()) {
326
                    $this->modified_tables[self::R_TIMEZONE]        = true;
327
                    $this->modified_tables[self::R_NGINX]           = true;
328
                    $this->modified_tables[self::R_PHP_FPM]         = true;
329
                    $this->modified_tables[self::R_REST_API_WORKER] = true;
330
                    $this->modified_tables[self::R_SYSLOG]        = true;
331
                }
332
                if ($pbxSettings->itHasNTPSettings()) {
333
                    $this->modified_tables[self::R_NTP] = true;
334
                }
335
                if ($pbxSettings->itHasCallRecordSettings()) {
336
                    $this->modified_tables[self::R_CALL_EVENTS_WORKER] = true;
337
                    $this->modified_tables[self::R_DIALPLAN]           = true;
338
                }
339
                break;
340
            case PbxExtensionModules::class:
341
                $moduleSettings                                                   = PbxExtensionModules::findFirstById(
342
                    $data['recordId']
343
                );
344
                $this->modified_tables[self::R_PBX_EXTENSION_STATE]               = true;
345
                $this->modified_tables['parameters'][self::R_PBX_EXTENSION_STATE] = $moduleSettings;
346
                $this->modified_tables[self::R_CRON]                              = true;
347
                break;
348
            default:
349
        }
350
351
        if ($count_changes === 0 && count($this->modified_tables) > 0) {
352
            // Начинаем отсчет времени при получении первой задачи.
353
            $this->last_change = time();
354
        }
355
    }
356
357
    /**
358
     * Apply changes
359
     *
360
     * @return void
361
     */
362
    private function startReload(): void
363
    {
364
        if (count($this->modified_tables) === 0) {
365
            return;
366
        }
367
        $delta = time() - $this->last_change;
368
        if ($delta < $this->timeout) {
369
            return;
370
        }
371
372
        foreach ($this->PRIORITY_R as $method_name) {
373
            $action     = $this->modified_tables[$method_name] ?? null;
374
            $parameters = $this->modified_tables['parameters'][$method_name] ?? null;
375
            if ($action === null) {
376
                continue;
377
            }
378
            if (method_exists($this, $method_name)) {
379
                Util::sysLogMsg(__METHOD__, "Process changes by {$method_name}", LOG_DEBUG);
380
                if ($parameters === null) {
381
                    $this->$method_name();
382
                } else {
383
                    $this->$method_name($parameters);
384
                }
385
            }
386
        }
387
388
        foreach ($this->arrObject as $appClass) {
389
            $appClass->modelsEventNeedReload($this->modified_tables);
390
        }
391
        $this->modified_tables = [];
392
    }
393
394
395
    /**
396
     * Restarts gnats queue server daemon
397
     */
398
    public function reloadNats(): void
399
    {
400
        $natsConf = new NatsConf();
401
        $natsConf->reStart();
402
    }
403
404
405
    /**
406
     * Reloads Asterisk dialplan
407
     */
408
    public function reloadDialplan(): void
409
    {
410
        PBX::dialplanReload();
411
    }
412
413
    /**
414
     * Reloads Asterisk manager interface module
415
     */
416
    public function reloadManager(): void
417
    {
418
        PBX::managerReload();
419
    }
420
421
    /**
422
     * Generates queue.conf and restart asterisk queue module
423
     */
424
    public function reloadQueues(): void
425
    {
426
        QueueConf::queueReload();
427
    }
428
429
    /**
430
     * Updates custom changes in config files
431
     */
432
    public function updateCustomFiles(): void
433
    {
434
        System::updateCustomFiles();
435
    }
436
437
    /**
438
     * Apply iptable settings and restart firewall
439
     */
440
    public function reloadFirewall(): void
441
    {
442
        IptablesConf::reloadFirewall();
443
    }
444
445
    /**
446
     *  Refresh networks configs and restarts network daemon
447
     */
448
    public function networkReload(): void
449
    {
450
        System::networkReload();
451
    }
452
453
    /**
454
     * Refresh IAX configs and reload iax2 module
455
     */
456
    public function reloadIax(): void
457
    {
458
        PBX::iaxReload();
459
    }
460
461
    /**
462
     * Reload MOH file list in Asterisk.
463
     */
464
    public function reloadMoh(): void
465
    {
466
        PBX::mohReload();
467
    }
468
469
    /**
470
     * Refresh SIP configs and reload PJSIP module
471
     */
472
    public function reloadSip(): void
473
    {
474
        PBX::sipReload();
475
    }
476
477
    /**
478
     *  Refresh features configs and reload features module
479
     */
480
    public function reloadFeatures(): void
481
    {
482
        PBX::featuresReload();
483
    }
484
485
    /**
486
     * Restarts CROND daemon
487
     */
488
    public function reloadCron(): void
489
    {
490
        $cron = new CronConf();
491
        $cron->reStart();
492
    }
493
494
    /**
495
     * Restarts NTP daemon
496
     */
497
    public function reloadNtp(): void
498
    {
499
        NTPConf::configure();
500
    }
501
502
    /**
503
     * Restarts Nginx daemon
504
     */
505
    public function reloadNginx(): void
506
    {
507
        $nginxConf = new NginxConf();
508
        $nginxConf->generateConf();
509
        $nginxConf->reStart();
510
    }
511
512
    /**
513
     * Restarts PHP-FPM daemon
514
     */
515
    public function reloadPHPFPM(): void
516
    {
517
        PHPConf::reStart();
518
    }
519
520
    /**
521
     * Configure SSH settings
522
     */
523
    public function reloadSSH(): void
524
    {
525
        $sshConf = new SSHConf();
526
        $sshConf->configure();
527
    }
528
529
    /**
530
     * Reconfigure TomeZone settings
531
     */
532
    public function updateTomeZone(): void
533
    {
534
        System::timezoneConfigure();
535
    }
536
537
    /**
538
     * Перезапуск rsyslog.
539
     */
540
    public function restartSyslogD(): void
541
    {
542
        // Рестарт демона Syslog.
543
        $syslogConf = new SyslogConf();
544
        $syslogConf->reStart();
545
    }
546
547
    /**
548
     *  Reloads Asterisk voicemail module
549
     */
550
    public function reloadVoicemail(): void
551
    {
552
        PBX::voicemailReload();
553
    }
554
555
    /**
556
     *  Reloads WorkerApiCommands worker
557
     */
558
    public function reloadRestAPIWorker(): void
559
    {
560
        Processes::processPHPWorker(WorkerApiCommands::class);
561
    }
562
563
564
    /**
565
     *  Reloads WorkerCallEvents worker
566
     */
567
    public function reloadWorkerCallEvents(): void
568
    {
569
        Processes::processPHPWorker(WorkerCallEvents::class);
570
    }
571
572
573
    /**
574
     * Timeout handles
575
     */
576
    public function timeoutHandler(): void
577
    {
578
        $this->last_change = time() - $this->timeout;
579
        $this->startReload();
580
    }
581
582
583
    /**
584
     *  Process after PBXExtension state changes
585
     *
586
     * @param \MikoPBX\Common\Models\PbxExtensionModules $record
587
     */
588
    public function afterModuleStateChanged(PbxExtensionModules $record): void
589
    {
590
        $className       = str_replace('Module', '', $record->uniqid);
591
        $configClassName = "\\Modules\\{$record->uniqid}\\Lib\\{$className}Conf";
592
        if (class_exists($configClassName)) {
593
            $configClassName = new $configClassName();
594
            if ($record->disabled === '1' && method_exists($configClassName, 'onAfterModuleDisable')) {
595
                $configClassName->onAfterModuleDisable();
596
            } elseif ($record->disabled === '0' && method_exists($configClassName, 'onAfterModuleEnable')) {
597
                $configClassName->onAfterModuleEnable();
598
            }
599
        }
600
    }
601
602
    /**
603
     * Process async system signal
604
     *
605
     * @param int $signal
606
     */
607
    public function signalHandler(int $signal): void
608
    {
609
        Util::sysLogMsg(static::class, "Receive signal to restart  ".$signal, LOG_DEBUG);
610
        $this->needRestart = true;
611
        $this->timeoutHandler();
612
    }
613
614
    public static function invokeAction(string $action):void{
615
        $di = Di::getDefault();
616
        $queue = $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
617
        $queue->publish(
618
            $action,
619
            self::class,
620
            0,
621
            PheanstalkInterface::DEFAULT_DELAY,
622
            3600
623
        );
624
    }
625
}
626
627
/**
628
 * Start point
629
 */
630
$workerClassname = WorkerModelsEvents::class;
631
if (isset($argv) && count($argv) > 1 && $argv[1] === 'start') {
632
    cli_set_process_title($workerClassname);
633
    try {
634
        $worker = new $workerClassname();
635
        $worker->start($argv);
636
    } catch (Throwable $e) {
637
        global $errorLogger;
638
        $errorLogger->captureException($e);
639
        sleep(1);
640
    }
641
}