Esempio n. 1
0
void ApiListener::SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoint::Ptr& endpoint)
{
	try {
		{
			ObjectLock olock(endpoint);

			endpoint->SetSyncing(true);
		}

		Log(LogInformation, "ApiListener")
		    << "Sending updates for endpoint '" << endpoint->GetName() << "'.";

		/* sync zone file config */
		SendConfigUpdate(aclient);
		/* sync runtime config */
		SendRuntimeConfigObjects(aclient);

		Log(LogInformation, "ApiListener")
		    << "Finished sending updates for endpoint '" << endpoint->GetName() << "'.";

		ReplayLog(aclient);
	} catch (const std::exception& ex) {
		Log(LogCritical, "ApiListener")
		    << "Error while syncing endpoint '" << endpoint->GetName() << "': " << DiagnosticInformation(ex);
	}
}
	BOOST_FOREACH(const Endpoint::Ptr& endpoint, ConfigType::GetObjectsByType<Endpoint>()) {
		if (!endpoint->GetConnected())
			continue;

		double ts = endpoint->GetRemoteLogPosition();

		if (ts == 0)
			continue;

		Dictionary::Ptr lparams = new Dictionary();
		lparams->Set("log_position", ts);

		Dictionary::Ptr lmessage = new Dictionary();
		lmessage->Set("jsonrpc", "2.0");
		lmessage->Set("method", "log::SetLogPosition");
		lmessage->Set("params", lparams);

		double maxTs = 0;

		BOOST_FOREACH(const JsonRpcConnection::Ptr& client, endpoint->GetClients()) {
			if (client->GetTimestamp() > maxTs)
				maxTs = client->GetTimestamp();
		}

		BOOST_FOREACH(const JsonRpcConnection::Ptr& client, endpoint->GetClients()) {
			if (client->GetTimestamp() != maxTs)
				client->Disconnect();
			else
				client->SendMessage(lmessage);
		}

		Log(LogNotice, "ApiListener")
		    << "Setting log position for identity '" << endpoint->GetName() << "': "
		    << Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", ts);
	}
Esempio n. 3
0
void Zone::OnAllConfigLoaded()
{
	ObjectImpl<Zone>::OnAllConfigLoaded();

	m_Parent = Zone::GetByName(GetParentRaw());

	if (m_Parent && m_Parent->IsGlobal())
		BOOST_THROW_EXCEPTION(ScriptError("Zone '" + GetName() + "' can not have a global zone as parent.", GetDebugInfo()));

	Zone::Ptr zone = m_Parent;
	int levels = 0;

	Array::Ptr endpoints = GetEndpointsRaw();

	if (endpoints) {
		ObjectLock olock(endpoints);
		for (const String& endpoint : endpoints) {
			Endpoint::Ptr ep = Endpoint::GetByName(endpoint);

			if (ep)
				ep->SetCachedZone(this);
		}
	}

	while (zone) {
		m_AllParents.push_back(zone);

		zone = Zone::GetByName(zone->GetParentRaw());
		levels++;

		if (levels > 32)
			BOOST_THROW_EXCEPTION(ScriptError("Infinite recursion detected while resolving zone graph. Check your zone hierarchy.", GetDebugInfo()));
	}
}
Esempio n. 4
0
Value EndpointsTable::IdentityAccessor(const Value& row)
{
	Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row);

	if (!endpoint)
		return Empty;

	return endpoint->GetName();
}
Esempio n. 5
0
bool ApiListener::IsMaster(void) const
{
	Endpoint::Ptr master = GetMaster();

	if (!master)
		return false;

	return master->GetName() == GetIdentity();
}
Esempio n. 6
0
Zone::Ptr Zone::GetLocalZone()
{
	Endpoint::Ptr local = Endpoint::GetLocalEndpoint();

	if (!local)
		return nullptr;

	return local->GetZone();
}
Esempio n. 7
0
Zone::Ptr Zone::GetLocalZone(void)
{
	Endpoint::Ptr local = Endpoint::GetLocalEndpoint();

	if (!local)
		return Zone::Ptr();

	return local->GetZone();
}
Esempio n. 8
0
int EndpointDbObject::EndpointIsConnected(const Endpoint::Ptr& endpoint)
{
    unsigned int is_connected = endpoint->IsConnected() ? 1 : 0;

    /* if identity is equal to node, fake is_connected */
    if (endpoint->GetName() == IcingaApplication::GetInstance()->GetNodeName())
        is_connected = 1;

    return is_connected;
}
Esempio n. 9
0
double ApiListener::CalculateZoneLag(const Endpoint::Ptr& endpoint)
{
	double remoteLogPosition = endpoint->GetRemoteLogPosition();
	double eplag = Utility::GetTime() - remoteLogPosition;

	if ((endpoint->GetSyncing() || !endpoint->GetConnected()) && remoteLogPosition != 0)
		return eplag;

	return 0;
}
Esempio n. 10
0
Dictionary::Ptr EndpointDbObject::GetConfigFields(void) const
{
    Dictionary::Ptr fields = new Dictionary();
    Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(GetObject());

    fields->Set("identity", endpoint->GetName());
    fields->Set("node", IcingaApplication::GetInstance()->GetNodeName());

    return fields;
}
Esempio n. 11
0
Dictionary::Ptr EndpointDbObject::GetStatusFields(void) const
{
	Dictionary::Ptr fields = make_shared<Dictionary>();
	Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(GetObject());

	Log(LogDebug, "EndpointDbObject", "update status for endpoint '" + endpoint->GetName() + "'");

	fields->Set("identity", endpoint->GetName());
	fields->Set("node", IcingaApplication::GetInstance()->GetNodeName());
	fields->Set("is_connected", EndpointIsConnected(endpoint));

	return fields;
}
Esempio n. 12
0
Value EndpointsTable::ZoneAccessor(const Value& row)
{
	Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row);

	if (!endpoint)
		return Empty;

	Zone::Ptr zone = endpoint->GetZone();

	if (!zone)
		return Empty;

	return zone->GetName();
}
Esempio n. 13
0
int ClusterLink::GetMetric(void) const
{
	int metric = 0;

	Endpoint::Ptr fromEp = Endpoint::GetByName(From);
	if (fromEp)
		metric += fromEp->GetMetric();

	Endpoint::Ptr toEp = Endpoint::GetByName(To);
	if (toEp)
		metric += toEp->GetMetric();

	return metric;
}
Esempio n. 14
0
Value EndpointsTable::IsConnectedAccessor(const Value& row)
{
	Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row);

	if (!endpoint)
		return Empty;

	unsigned int is_connected = endpoint->IsConnected() ? 1 : 0;

	/* if identity is equal to node, fake is_connected */
	if (endpoint->GetName() == IcingaApplication::GetInstance()->GetNodeName())
		is_connected = 1;

	return is_connected;
}
Esempio n. 15
0
Value SetLogPositionHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
	if (!params)
		return Empty;

	double log_position = params->Get("log_position");
	Endpoint::Ptr endpoint = origin.FromClient->GetEndpoint();

	if (!endpoint)
		return Empty;

	if (log_position > endpoint->GetLocalLogPosition())
		endpoint->SetLocalLogPosition(log_position);

	return Empty;
}
Esempio n. 16
0
void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient)
{
	Endpoint::Ptr endpoint = aclient->GetEndpoint();
	ASSERT(endpoint);

	Zone::Ptr azone = endpoint->GetZone();
	Zone::Ptr lzone = Zone::GetLocalZone();

	/* don't try to send config updates to our master */
	if (!azone->IsChildOf(lzone))
		return;

	Dictionary::Ptr configUpdateV1 = new Dictionary();
	Dictionary::Ptr configUpdateV2 = new Dictionary();

	String zonesDir = Configuration::DataDir + "/api/zones";

	for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) {
		String zoneDir = zonesDir + "/" + zone->GetName();

		if (!zone->IsChildOf(azone) && !zone->IsGlobal())
			continue;

		if (!Utility::PathExists(zoneDir))
			continue;

		Log(LogInformation, "ApiListener")
			<< "Syncing configuration files for " << (zone->IsGlobal() ? "global " : "")
			<< "zone '" << zone->GetName() << "' to endpoint '" << endpoint->GetName() << "'.";

		ConfigDirInformation config = LoadConfigDir(zonesDir + "/" + zone->GetName());
		configUpdateV1->Set(zone->GetName(), config.UpdateV1);
		configUpdateV2->Set(zone->GetName(), config.UpdateV2);
	}

	Dictionary::Ptr message = new Dictionary({
		{ "jsonrpc", "2.0" },
		{ "method", "config::Update" },
		{ "params", new Dictionary({
			{ "update", configUpdateV1 },
			{ "update_v2", configUpdateV2 }
		}) }
	});

	aclient->SendMessage(message);
}
Esempio n. 17
0
void ApiListener::SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoint::Ptr& endpoint, bool needSync)
{
	try {
		{
			ObjectLock olock(endpoint);

			endpoint->SetSyncing(true);
		}

		/* Make sure that the config updates are synced
		 * before the logs are replayed.
		 */

		Log(LogInformation, "ApiListener")
		    << "Sending config updates for endpoint '" << endpoint->GetName() << "'.";

		/* sync zone file config */
		SendConfigUpdate(aclient);
		/* sync runtime config */
		SendRuntimeConfigObjects(aclient);

		Log(LogInformation, "ApiListener")
		    << "Finished sending config updates for endpoint '" << endpoint->GetName() << "'.";

		if (!needSync) {
			ObjectLock olock2(endpoint);
			endpoint->SetSyncing(false);
			return;
		}

		Log(LogInformation, "ApiListener")
		    << "Sending replay log for endpoint '" << endpoint->GetName() << "'.";

		ReplayLog(aclient);

		if (endpoint->GetZone() == Zone::GetLocalZone())
			UpdateObjectAuthority();

		Log(LogInformation, "ApiListener")
		    << "Finished sending replay log for endpoint '" << endpoint->GetName() << "'.";
	} catch (const std::exception& ex) {
		ObjectLock olock2(endpoint);
		endpoint->SetSyncing(false);

		Log(LogCritical, "ApiListener")
		    << "Error while syncing endpoint '" << endpoint->GetName() << "': " << DiagnosticInformation(ex);
	}
}
Esempio n. 18
0
void EndpointDbObject::OnConfigUpdate(void)
{
	/* update current status on config dump once */
	Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(GetObject());

	DbQuery query1;
	query1.Table = "endpointstatus";
	query1.Type = DbQueryInsert;

	Dictionary::Ptr fields1 = make_shared<Dictionary>();
	fields1->Set("identity", endpoint->GetName());
	fields1->Set("node", IcingaApplication::GetInstance()->GetNodeName());
	fields1->Set("is_connected", EndpointIsConnected(endpoint));
	fields1->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
	fields1->Set("endpoint_object_id", endpoint);
	fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
	query1.Fields = fields1;

	OnQuery(query1);
}
Esempio n. 19
0
void Checkable::OnAllConfigLoaded(void)
{
	ObjectImpl<Checkable>::OnAllConfigLoaded();

	Endpoint::Ptr endpoint = GetCommandEndpoint();

	if (endpoint) {
		Zone::Ptr checkableZone = static_pointer_cast<Zone>(GetZone());

		if (!checkableZone)
			checkableZone = Zone::GetLocalZone();

		Zone::Ptr cmdZone = endpoint->GetZone();

		if (checkableZone && cmdZone != checkableZone && cmdZone->GetParent() != checkableZone) {
			BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("command_endpoint"),
			    "Command endpoint must be in zone '" + checkableZone->GetName() + "' or in a direct child zone thereof."));
		}
	}
}
Esempio n. 20
0
void Checkable::OnAllConfigLoaded()
{
	ObjectImpl<Checkable>::OnAllConfigLoaded();

	Endpoint::Ptr endpoint = GetCommandEndpoint();

	if (endpoint) {
		Zone::Ptr checkableZone = static_pointer_cast<Zone>(GetZone());

		if (checkableZone) {
			Zone::Ptr cmdZone = endpoint->GetZone();

			if (cmdZone != checkableZone && cmdZone->GetParent() != checkableZone) {
				BOOST_THROW_EXCEPTION(ValidationError(this, { "command_endpoint" },
					"Command endpoint must be in zone '" + checkableZone->GetName() + "' or in a direct child zone thereof."));
			}
		} else {
			BOOST_THROW_EXCEPTION(ValidationError(this, { "command_endpoint" },
				"Command endpoint must not be set."));
		}
	}
}
Esempio n. 21
0
void ApiListener::SyncSendMessage(const Endpoint::Ptr& endpoint, const Dictionary::Ptr& message)
{
	ObjectLock olock(endpoint);

	if (!endpoint->GetSyncing()) {
		Log(LogNotice, "ApiListener")
		    << "Sending message '" << message->Get("method") << "' to '" << endpoint->GetName() << "'";

		double maxTs = 0;

		for (const JsonRpcConnection::Ptr& client : endpoint->GetClients()) {
			if (client->GetTimestamp() > maxTs)
				maxTs = client->GetTimestamp();
		}

		for (const JsonRpcConnection::Ptr& client : endpoint->GetClients()) {
			if (client->GetTimestamp() != maxTs)
				continue;

			client->SendMessage(message);
		}
	}
}
Esempio n. 22
0
void ApiListener::SendConfigUpdate(const ApiClient::Ptr& aclient)
{
	Endpoint::Ptr endpoint = aclient->GetEndpoint();
	ASSERT(endpoint);

	Zone::Ptr azone = endpoint->GetZone();
	Zone::Ptr lzone = Zone::GetLocalZone();

	/* don't try to send config updates to our master */
	if (!azone->IsChildOf(lzone))
		return;

	Dictionary::Ptr configUpdate = make_shared<Dictionary>();

	String zonesDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";

	BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjectsByType<Zone>()) {
		String zoneDir = zonesDir + "/" + zone->GetName();

		if (!zone->IsChildOf(azone) && !zone->IsGlobal()) {
			Log(LogNotice, "ApiListener")
			    << "Skipping sync for '" << zone->GetName() << "'. Not a child of zone '" << azone->GetName() << "'.";
			continue;
		}
		if (!Utility::PathExists(zoneDir)) {
			Log(LogNotice, "ApiListener")
			    << "Ignoring sync for '" << zone->GetName() << "'. Zone directory '" << zoneDir << "' does not exist.";
			continue;
		}

		if (zone->IsGlobal())
			Log(LogInformation, "ApiListener")
			    << "Syncing global zone '" << zone->GetName() << "'.";

		configUpdate->Set(zone->GetName(), LoadConfigDir(zonesDir + "/" + zone->GetName()));
	}
Esempio n. 23
0
/**
 * Creates a new JSON-RPC client and connects to the specified endpoint.
 *
 * @param endpoint The endpoint.
 */
void ApiListener::AddConnection(const Endpoint::Ptr& endpoint)
{
	{
		ObjectLock olock(this);

		std::shared_ptr<SSL_CTX> sslContext = m_SSLContext;

		if (!sslContext) {
			Log(LogCritical, "ApiListener", "SSL context is required for AddConnection()");
			return;
		}
	}

	String host = endpoint->GetHost();
	String port = endpoint->GetPort();

	Log(LogInformation, "ApiListener")
		<< "Reconnecting to endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'";

	TcpSocket::Ptr client = new TcpSocket();

	String serverName = endpoint->GetName();

	String env = ScriptGlobal::Get("Environment", &Empty);
	if (env != "" && env != "production")
		serverName += ":" + env;

	try {
		endpoint->SetConnecting(true);
		client->Connect(host, port);
		NewClientHandler(client, serverName, RoleClient);
		endpoint->SetConnecting(false);
	} catch (const std::exception& ex) {
		endpoint->SetConnecting(false);
		client->Close();

		std::ostringstream info;
		info << "Cannot connect to host '" << host << "' on port '" << port << "'";
		Log(LogCritical, "ApiListener", info.str());
		Log(LogDebug, "ApiListener")
			<< info.str() << "\n" << DiagnosticInformation(ex);
	}

	Log(LogInformation, "ApiListener")
		<< "Finished reconnecting to endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'";
}
Esempio n. 24
0
void EndpointDbObject::UpdateConnectedStatus(const Endpoint::Ptr& endpoint)
{
    bool connected = EndpointIsConnected(endpoint);

    Log(LogDebug, "EndpointDbObject")
            << "update is_connected=" << connected << " for endpoint '" << endpoint->GetName() << "'";

    DbQuery query1;
    query1.Table = "endpointstatus";
    query1.Type = DbQueryUpdate;
    query1.Category = DbCatState;

    Dictionary::Ptr fields1 = new Dictionary();
    fields1->Set("is_connected", (connected ? 1 : 0));
    fields1->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
    query1.Fields = fields1;

    query1.WhereCriteria = new Dictionary();
    query1.WhereCriteria->Set("endpoint_object_id", endpoint);
    query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */

    OnQuery(query1);
}
Esempio n. 25
0
/**
 * Creates a new JSON-RPC client and connects to the specified endpoint.
 *
 * @param endpoint The endpoint.
 */
void ApiListener::AddConnection(const Endpoint::Ptr& endpoint)
{
	{
		ObjectLock olock(this);

		boost::shared_ptr<SSL_CTX> sslContext = m_SSLContext;

		if (!sslContext) {
			Log(LogCritical, "ApiListener", "SSL context is required for AddConnection()");
			return;
		}
	}

	String host = endpoint->GetHost();
	String port = endpoint->GetPort();

	Log(LogInformation, "JsonRpcConnection")
	    << "Reconnecting to API endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'";

	TcpSocket::Ptr client = new TcpSocket();

	try {
		endpoint->SetConnecting(true);
		client->Connect(host, port);
		NewClientHandler(client, endpoint->GetName(), RoleClient);
		endpoint->SetConnecting(false);
	} catch (const std::exception& ex) {
		endpoint->SetConnecting(false);
		client->Close();

		std::ostringstream info;
		info << "Cannot connect to host '" << host << "' on port '" << port << "'";
		Log(LogCritical, "ApiListener", info.str());
		Log(LogDebug, "ApiListener")
		    << info.str() << "\n" << DiagnosticInformation(ex);
	}
}
Esempio n. 26
0
void ApiListener::ApiReconnectTimerHandler(void)
{
	Zone::Ptr my_zone = Zone::GetLocalZone();

	for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) {
		/* don't connect to global zones */
		if (zone->GetGlobal())
			continue;

		/* only connect to endpoints in a) the same zone b) our parent zone c) immediate child zones */
		if (my_zone != zone && my_zone != zone->GetParent() && zone != my_zone->GetParent()) {
			Log(LogDebug, "ApiListener")
			    << "Not connecting to Zone '" << zone->GetName()
			    << "' because it's not in the same zone, a parent or a child zone.";
			continue;
		}

		for (const Endpoint::Ptr& endpoint : zone->GetEndpoints()) {
			/* don't connect to ourselves */
			if (endpoint == GetLocalEndpoint()) {
				Log(LogDebug, "ApiListener")
				    << "Not connecting to Endpoint '" << endpoint->GetName() << "' because that's us.";
				continue;
			}

			/* don't try to connect to endpoints which don't have a host and port */
			if (endpoint->GetHost().IsEmpty() || endpoint->GetPort().IsEmpty()) {
				Log(LogDebug, "ApiListener")
				    << "Not connecting to Endpoint '" << endpoint->GetName()
				    << "' because the host/port attributes are missing.";
				continue;
			}

			/* don't try to connect if there's already a connection attempt */
			if (endpoint->GetConnecting()) {
				Log(LogDebug, "ApiListener")
				    << "Not connecting to Endpoint '" << endpoint->GetName()
				    << "' because we're already trying to connect to it.";
				continue;
			}

			/* don't try to connect if we're already connected */
			if (endpoint->GetConnected()) {
				Log(LogDebug, "ApiListener")
				    << "Not connecting to Endpoint '" << endpoint->GetName()
				    << "' because we're already connected to it.";
				continue;
			}

			boost::thread thread(boost::bind(&ApiListener::AddConnection, this, endpoint));
			thread.detach();
		}
	}

	Endpoint::Ptr master = GetMaster();

	if (master)
		Log(LogNotice, "ApiListener")
		    << "Current zone master: " << master->GetName();

	std::vector<String> names;
	for (const Endpoint::Ptr& endpoint : ConfigType::GetObjectsByType<Endpoint>())
		if (endpoint->GetConnected())
			names.push_back(endpoint->GetName() + " (" + Convert::ToString(endpoint->GetClients().size()) + ")");

	Log(LogNotice, "ApiListener")
	    << "Connected endpoints: " << Utility::NaturalJoin(names);
}
Esempio n. 27
0
/**
 * Processes a new client connection.
 *
 * @param client The new client.
 */
void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const String& hostname, ConnectionRole role)
{
	CONTEXT("Handling new API client connection");

	TlsStream::Ptr tlsStream;

	{
		ObjectLock olock(this);
		try {
			tlsStream = new TlsStream(client, hostname, role, m_SSLContext);
		} catch (const std::exception&) {
			Log(LogCritical, "ApiListener", "Cannot create TLS stream from client connection.");
			return;
		}
	}

	try {
		tlsStream->Handshake();
	} catch (const std::exception& ex) {
		Log(LogCritical, "ApiListener", "Client TLS handshake failed");
		return;
	}

	boost::shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
	String identity;
	Endpoint::Ptr endpoint;
	bool verify_ok = false;

	if (cert) {
		try {
			identity = GetCertificateCN(cert);
		} catch (const std::exception&) {
			Log(LogCritical, "ApiListener")
			    << "Cannot get certificate common name from cert path: '" << GetCertPath() << "'.";
			return;
		}

		verify_ok = tlsStream->IsVerifyOK();

		Log(LogInformation, "ApiListener")
		    << "New client connection for identity '" << identity << "'" << (verify_ok ? "" : " (unauthenticated)");


		if (verify_ok)
			endpoint = Endpoint::GetByName(identity);
	} else {
		Log(LogInformation, "ApiListener")
		    << "New client connection (no client certificate)";
	}

	bool need_sync = false;

	if (endpoint)
		need_sync = !endpoint->IsConnected();

	ClientType ctype;

	if (role == RoleClient) {
		Dictionary::Ptr message = new Dictionary();
		message->Set("jsonrpc", "2.0");
		message->Set("method", "icinga::Hello");
		message->Set("params", new Dictionary());
		JsonRpc::SendMessage(tlsStream, message);
		ctype = ClientJsonRpc;
	} else {
		tlsStream->WaitForData(5);

		if (!tlsStream->IsDataAvailable()) {
			Log(LogWarning, "ApiListener", "No data received on new API connection.");
			return;
		}

		char firstByte;
		tlsStream->Peek(&firstByte, 1, false);

		if (firstByte >= '0' && firstByte <= '9')
			ctype = ClientJsonRpc;
		else
			ctype = ClientHttp;
	}

	if (ctype == ClientJsonRpc) {
		Log(LogNotice, "ApiListener", "New JSON-RPC client");

		JsonRpcConnection::Ptr aclient = new JsonRpcConnection(identity, verify_ok, tlsStream, role);
		aclient->Start();

		if (endpoint) {
			endpoint->AddClient(aclient);

			if (need_sync) {
				{
					ObjectLock olock(endpoint);

					endpoint->SetSyncing(true);
				}

				Log(LogInformation, "ApiListener")
				    << "Sending updates for endpoint '" << endpoint->GetName() << "'.";

				/* sync zone file config */
				SendConfigUpdate(aclient);
				/* sync runtime config */
				SendRuntimeConfigObjects(aclient);

				Log(LogInformation, "ApiListener")
				    << "Finished sending updates for endpoint '" << endpoint->GetName() << "'.";

				ReplayLog(aclient);
			}
		} else
			AddAnonymousClient(aclient);
	} else {
		Log(LogNotice, "ApiListener", "New HTTP client");

		HttpServerConnection::Ptr aclient = new HttpServerConnection(identity, verify_ok, tlsStream);
		aclient->Start();
		AddHttpClient(aclient);
	}
}
/**
 * Processes a new client connection.
 *
 * @param client The new client.
 */
void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const String& hostname, ConnectionRole role)
{
	CONTEXT("Handling new API client connection");

	TlsStream::Ptr tlsStream;

	{
		ObjectLock olock(this);
		try {
			tlsStream = new TlsStream(client, hostname, role, m_SSLContext);
		} catch (const std::exception&) {
			Log(LogCritical, "ApiListener", "Cannot create TLS stream from client connection.");
			return;
		}
	}

	try {
		tlsStream->Handshake();
	} catch (const std::exception& ex) {
		Log(LogCritical, "ApiListener", "Client TLS handshake failed");
		return;
	}

	boost::shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
	String identity;
	Endpoint::Ptr endpoint;
	bool verify_ok = false;

	if (cert) {
		try {
			identity = GetCertificateCN(cert);
		} catch (const std::exception&) {
			Log(LogCritical, "ApiListener")
			    << "Cannot get certificate common name from cert path: '" << GetCertPath() << "'.";
			return;
		}

		verify_ok = tlsStream->IsVerifyOK();
		if (!hostname.IsEmpty()) {
			if (identity != hostname) {
				Log(LogWarning, "ApiListener")
					<< "Unexpected certificate common name while connecting to endpoint '"
				    << hostname << "': got '" << identity << "'";
				return;
			} else if (!verify_ok) {
				Log(LogWarning, "ApiListener")
					<< "Peer certificate for endpoint '" << hostname
					<< "' is not signed by the certificate authority.";
				return;
			}
		}

		Log(LogInformation, "ApiListener")
		    << "New client connection for identity '" << identity << "'"
		    << (verify_ok ? "" : " (client certificate not signed by CA)");

		if (verify_ok)
			endpoint = Endpoint::GetByName(identity);
	} else {
		Log(LogInformation, "ApiListener")
		    << "New client connection (no client certificate)";
	}

	ClientType ctype;

	if (role == RoleClient) {
		Dictionary::Ptr message = new Dictionary();
		message->Set("jsonrpc", "2.0");
		message->Set("method", "icinga::Hello");
		message->Set("params", new Dictionary());
		JsonRpc::SendMessage(tlsStream, message);
		ctype = ClientJsonRpc;
	} else {
		tlsStream->WaitForData(5);

		if (!tlsStream->IsDataAvailable()) {
			Log(LogWarning, "ApiListener", "No data received on new API connection.");
			return;
		}

		char firstByte;
		tlsStream->Peek(&firstByte, 1, false);

		if (firstByte >= '0' && firstByte <= '9')
			ctype = ClientJsonRpc;
		else
			ctype = ClientHttp;
	}

	if (ctype == ClientJsonRpc) {
		Log(LogNotice, "ApiListener", "New JSON-RPC client");

		JsonRpcConnection::Ptr aclient = new JsonRpcConnection(identity, verify_ok, tlsStream, role);
		aclient->Start();

		if (endpoint) {
			bool needSync = !endpoint->GetConnected();

			endpoint->AddClient(aclient);

			m_SyncQueue.Enqueue(boost::bind(&ApiListener::SyncClient, this, aclient, endpoint, needSync));
		} else
			AddAnonymousClient(aclient);
	} else {
		Log(LogNotice, "ApiListener", "New HTTP client");

		HttpServerConnection::Ptr aclient = new HttpServerConnection(identity, verify_ok, tlsStream);
		aclient->Start();
		AddHttpClient(aclient);
	}
}
Esempio n. 29
0
void Checkable::ExecuteCheck()
{
    CONTEXT("Executing check for object '" + GetName() + "'");

    ASSERT(!OwnsLock());

    UpdateNextCheck();

    bool reachable = IsReachable();

    {
        ObjectLock olock(this);

        /* don't run another check if there is one pending */
        if (m_CheckRunning)
            return;

        m_CheckRunning = true;

        SetLastStateRaw(GetStateRaw());
        SetLastStateType(GetLastStateType());
        SetLastReachable(reachable);
    }

    /* keep track of scheduling info in case the check type doesn't provide its own information */
    double scheduled_start = GetNextCheck();
    double before_check = Utility::GetTime();

    CheckResult::Ptr cr = new CheckResult();

    cr->SetScheduleStart(scheduled_start);
    cr->SetExecutionStart(before_check);

    Endpoint::Ptr endpoint = GetCommandEndpoint();
    bool local = !endpoint || endpoint == Endpoint::GetLocalEndpoint();

    if (local) {
        GetCheckCommand()->Execute(this, cr, NULL, false);
    } else {
        Dictionary::Ptr macros = new Dictionary();
        GetCheckCommand()->Execute(this, cr, macros, false);

        if (endpoint->GetConnected()) {
            /* perform check on remote endpoint */
            Dictionary::Ptr message = new Dictionary();
            message->Set("jsonrpc", "2.0");
            message->Set("method", "event::ExecuteCommand");

            Host::Ptr host;
            Service::Ptr service;
            tie(host, service) = GetHostService(this);

            Dictionary::Ptr params = new Dictionary();
            message->Set("params", params);
            params->Set("command_type", "check_command");
            params->Set("command", GetCheckCommand()->GetName());
            params->Set("host", host->GetName());

            if (service)
                params->Set("service", service->GetShortName());

            params->Set("macros", macros);

            ApiListener::Ptr listener = ApiListener::GetInstance();

            if (listener)
                listener->SyncSendMessage(endpoint, message);

        } else if (Application::GetInstance()->GetStartTime() < Utility::GetTime() - 30) {
            /* fail to perform check on unconnected endpoint */
            cr->SetState(ServiceUnknown);

            cr->SetOutput("Remote Icinga instance '" + endpoint->GetName() +
                          "' " + "is not connected to '" + Endpoint::GetLocalEndpoint()->GetName() + "'");

            ProcessCheckResult(cr);
        }

        {
            ObjectLock olock(this);
            m_CheckRunning = false;
        }
    }
}
Esempio n. 30
0
void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client)
{
	Endpoint::Ptr endpoint = client->GetEndpoint();

	if (endpoint->GetLogDuration() == 0) {
		ObjectLock olock2(endpoint);
		endpoint->SetSyncing(false);
		return;
	}

	CONTEXT("Replaying log for Endpoint '" + endpoint->GetName() + "'");

	int count = -1;
	double peer_ts = endpoint->GetLocalLogPosition();
	double logpos_ts = peer_ts;
	bool last_sync = false;

	Endpoint::Ptr target_endpoint = client->GetEndpoint();
	ASSERT(target_endpoint);

	Zone::Ptr target_zone = target_endpoint->GetZone();

	if (!target_zone) {
		ObjectLock olock2(endpoint);
		endpoint->SetSyncing(false);
		return;
	}

	for (;;) {
		boost::mutex::scoped_lock lock(m_LogLock);

		CloseLogFile();
		RotateLogFile();

		if (count == -1 || count > 50000) {
			OpenLogFile();
			lock.unlock();
		} else {
			last_sync = true;
		}

		count = 0;

		std::vector<int> files;
		Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
		std::sort(files.begin(), files.end());

		for (int ts : files) {
			String path = GetApiDir() + "log/" + Convert::ToString(ts);

			if (ts < peer_ts)
				continue;

			Log(LogNotice, "ApiListener")
			    << "Replaying log: " << path;

			std::fstream *fp = new std::fstream(path.CStr(), std::fstream::in | std::fstream::binary);
			StdioStream::Ptr logStream = new StdioStream(fp, true);

			String message;
			StreamReadContext src;
			while (true) {
				Dictionary::Ptr pmessage;

				try {
					StreamReadStatus srs = NetString::ReadStringFromStream(logStream, &message, src);

					if (srs == StatusEof)
						break;

					if (srs != StatusNewItem)
						continue;

					pmessage = JsonDecode(message);
				} catch (const std::exception&) {
					Log(LogWarning, "ApiListener")
					    << "Unexpected end-of-file for cluster log: " << path;

					/* Log files may be incomplete or corrupted. This is perfectly OK. */
					break;
				}

				if (pmessage->Get("timestamp") <= peer_ts)
					continue;

				Dictionary::Ptr secname = pmessage->Get("secobj");

				if (secname) {
					ConfigObject::Ptr secobj = ConfigObject::GetObject(secname->Get("type"), secname->Get("name"));

					if (!secobj)
						continue;

					if (!target_zone->CanAccessObject(secobj))
						continue;
				}

				try  {
					NetString::WriteStringToStream(client->GetStream(), pmessage->Get("message"));
					count++;
				} catch (const std::exception& ex) {
					Log(LogWarning, "ApiListener")
					    << "Error while replaying log for endpoint '" << endpoint->GetName() << "': " << DiagnosticInformation(ex);
					break;
				}

				peer_ts = pmessage->Get("timestamp");

				if (ts > logpos_ts + 10) {
					logpos_ts = ts;

					Dictionary::Ptr lparams = new Dictionary();
					lparams->Set("log_position", logpos_ts);

					Dictionary::Ptr lmessage = new Dictionary();
					lmessage->Set("jsonrpc", "2.0");
					lmessage->Set("method", "log::SetLogPosition");
					lmessage->Set("params", lparams);

					JsonRpc::SendMessage(client->GetStream(), lmessage);
				}
			}

			logStream->Close();
		}

		if (count > 0) {
			Log(LogInformation, "ApiListener")
			   << "Replayed " << count << " messages.";
		}

		Log(LogNotice, "ApiListener")
		   << "Replayed " << count << " messages.";

		if (last_sync) {
			{
				ObjectLock olock2(endpoint);
				endpoint->SetSyncing(false);
			}

			OpenLogFile();

			break;
		}
	}
}