StatusManager::handleInstanceStatus()   A
last analyzed

Complexity

Conditions 5
Paths 16

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 15
c 1
b 0
f 0
nc 16
nop 1
dl 0
loc 32
rs 9.4555
ccs 0
cts 19
cp 0
crap 30
1
<?php
2
3
namespace Jalle19\StatusManager\Manager;
4
5
use Jalle19\StatusManager\Configuration\Instance;
6
use Jalle19\StatusManager\Event\ConnectionSeenEvent;
7
use Jalle19\StatusManager\Event\Events;
8
use Jalle19\StatusManager\Event\InputSeenEvent;
9
use Jalle19\StatusManager\Event\InstanceStatusCollectionRequestEvent;
10
use Jalle19\StatusManager\Event\InstanceSeenEvent;
11
use Jalle19\StatusManager\Event\InstanceStateEvent;
12
use Jalle19\StatusManager\Event\InstanceStatusUpdatesEvent;
13
use Jalle19\StatusManager\Event\SubscriptionSeenEvent;
14
use Jalle19\StatusManager\Event\SubscriptionStateChangeEvent;
15
use Jalle19\StatusManager\Instance\InstanceStatus;
16
use Jalle19\StatusManager\Instance\InstanceStatusCollection;
17
use Jalle19\StatusManager\Subscription\StateChangeParser;
18
use Jalle19\tvheadend\exception\RequestFailedException;
19
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
20
21
/**
22
 * Class StatusManager
23
 * @package   Jalle19\StatusManager\Manager
24
 * @copyright Copyright &copy; Sam Stenvall 2015-
25
 * @license   https://www.gnu.org/licenses/gpl.html The GNU General Public License v2.0
26
 */
27
class StatusManager extends AbstractManager implements EventSubscriberInterface
28
{
29
30
	/**
31
	 * @inheritdoc
32
	 */
33 1
	public static function getSubscribedEvents()
34
	{
35
		return [
36 1
			Events::MAIN_LOOP_STARTING => 'onMainLoopStarted',
37 1
			Events::MAIN_LOOP_TICK     => 'onMainLoopTick',
38 1
		];
39
	}
40
41
42
	/**
43
	 * Called right before the main loop is started
44
	 */
45
	public function onMainLoopStarted()
46
	{
47
		// Log information about the database
48
		$this->logger->notice('Using database at {databasePath}', [
49
			'databasePath' => $this->configuration->getDatabasePath(),
50
		]);
51
52
		// Log information about the configured instances
53
		$instances = $this->configuration->getInstances();
54
55
		$this->logger->notice('Managing {instances} instances:', [
56
			'instances' => count($instances),
57
		]);
58
59
		foreach ($instances as $instance)
60
		{
61
			$this->logger->notice('  {name} ({address}:{port})', [
62
				'name'    => $instance->getName(),
63
				'address' => $instance->getInstance()->getHostname(),
64
				'port'    => $instance->getInstance()->getPort(),
65
			]);
66
67
			$this->eventDispatcher->dispatch(Events::INSTANCE_SEEN, new InstanceSeenEvent($instance));
68
		}
69
	}
70
71
72
	/**
73
	 * Called periodically by the event loop
74
	 */
75
	public function onMainLoopTick()
76
	{
77
		/* @var InstanceStatusCollectionRequestEvent $event */
78
		$event = $this->eventDispatcher->dispatch(Events::INSTANCE_STATUS_COLLECTION_REQUEST,
79
			new InstanceStatusCollectionRequestEvent());
80
81
		$statusCollection = $this->getStatusMessages($event->getInstanceStatusCollection());
82
83
		foreach ($statusCollection->getInstanceStatuses() as $instanceStatus)
84
			$this->handleInstanceStatus($instanceStatus);
85
86
		$this->eventDispatcher->dispatch(Events::INSTANCE_STATUS_UPDATES,
87
			new InstanceStatusUpdatesEvent($statusCollection));
88
	}
89
90
91
	/**
92
	 * @param InstanceStatus $instanceStatus
93
	 */
94
	private function handleInstanceStatus(InstanceStatus $instanceStatus)
95
	{
96
		$instanceName = $instanceStatus->getInstanceName();
97
98
		$this->logger->debug('Got status updates from {instanceName}', [
99
			'instanceName' => $instanceName,
100
		]);
101
102
		// Persist connections
103
		foreach ($instanceStatus->getConnections() as $connection)
104
		{
105
			$this->eventDispatcher->dispatch(Events::CONNECTION_SEEN,
106
				new ConnectionSeenEvent($instanceName, $connection));
107
		}
108
109
		// Persist inputs
110
		foreach ($instanceStatus->getInputs() as $input)
111
			$this->eventDispatcher->dispatch(Events::INPUT_SEEN,
112
				new InputSeenEvent($instanceName, $instanceStatus, $input));
113
114
		// Persist running subscriptions
115
		foreach ($instanceStatus->getSubscriptions() as $subscription)
116
		{
117
			$this->eventDispatcher->dispatch(Events::SUBSCRIPTION_SEEN,
118
				new SubscriptionSeenEvent($instanceName, $instanceStatus, $subscription));
119
		}
120
121
		// Handle subscription state changes
122
		foreach ($instanceStatus->getSubscriptionStateChanges() as $subscriptionStateChange)
123
		{
124
			$this->eventDispatcher->dispatch(Events::SUBSCRIPTION_STATE_CHANGE,
125
				new SubscriptionStateChangeEvent($instanceName, $subscriptionStateChange));
126
		}
127
	}
128
129
130
	/**
131
	 * Retrieves and returns all status messages for the configured
132
	 * instances
133
	 *
134
	 * @param \SplObjectStorage $instances the instances and their state
135
	 *
136
	 * @return InstanceStatusCollection
137
	 */
138
	private function getStatusMessages($instances)
139
	{
140
		$collection = new InstanceStatusCollection();
141
142
		foreach ($instances as $instance)
143
		{
144
			/* @var Instance $instance */
145
			$tvheadend     = $instance->getInstance();
146
			$instanceName  = $instance->getName();
147
			$instanceState = $instances[$instance];
148
149
			// Collect statuses from currently reachable instances
150
			if ($instanceState->isReachable())
151
			{
152
				try
153
				{
154
					$collection->add(new InstanceStatus(
155
						$instanceName,
156
						$tvheadend->getNetworks(),
157
						$tvheadend->getMultiplexes(),
158
						$tvheadend->getInputStatus(),
159
						$tvheadend->getSubscriptionStatus(),
160
						$tvheadend->getConnectionStatus(),
161
						StateChangeParser::parseStateChanges($tvheadend->getLogMessages())));
162
163
					$this->eventDispatcher->dispatch(Events::INSTANCE_STATE_REACHABLE,
164
						new InstanceStateEvent($instance));
165
				}
166
				catch (\Exception $e)
167
				{
168
					if ($e instanceof RequestFailedException)
169
					{
170
						// Check for authentication errors
171
						$statusCode = $e->getResponse()->getStatusCode();
172
173
						if ($statusCode >= 400 && $statusCode < 500)
174
						{
175
							$this->logger->error('Authentication/authorization failed when connecting to {instanceName} (HTTP {statusCode})',
176
								[
177
									'instanceName' => $instanceName,
178
									'statusCode'   => $e->getResponse()->getStatusCode(),
179
								]);
180
						}
181
					}
182
183
					$this->eventDispatcher->dispatch(Events::INSTANCE_STATE_UNREACHABLE,
184
						new InstanceStateEvent($instance));
185
				}
186
			}
187
			else
188
			{
189
				$this->eventDispatcher->dispatch(Events::INSTANCE_STATE_MAYBE_REACHABLE,
190
					new InstanceStateEvent($instance));
191
			}
192
		}
193
194
		return $collection;
195
	}
196
197
}
198