From 16069a7368929f03fb7e3dab241cbd1bb1f9171b Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 28 Oct 2014 19:17:37 +0100 Subject: [PATCH] Cli: Don't overwrite existing objects; delete old agent objects on 'agent update-config' refs #7248 --- lib/cli/agentupdateconfigcommand.cpp | 144 +++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/lib/cli/agentupdateconfigcommand.cpp b/lib/cli/agentupdateconfigcommand.cpp index ff5963b46..018c7997c 100644 --- a/lib/cli/agentupdateconfigcommand.cpp +++ b/lib/cli/agentupdateconfigcommand.cpp @@ -23,6 +23,7 @@ #include "base/logger.hpp" #include "base/application.hpp" #include "base/objectlock.hpp" +#include "base/json.hpp" #include #include #include @@ -66,22 +67,57 @@ int AgentUpdateConfigCommand::Run(const boost::program_options::variables_map& v return 1; } + String inventory_path = AgentUtility::GetRepositoryPath() + "/inventory.index"; + + Dictionary::Ptr old_inventory = make_shared(); + if (Utility::PathExists(inventory_path)) { + old_inventory = Utility::LoadJsonFile(inventory_path); + } + + Dictionary::Ptr inventory = make_shared(); + Log(LogInformation, "cli") << "Updating agent configuration for "; AgentUtility::PrintAgents(std::cout); + Utility::LoadExtensionLibrary("icinga"); + + std::vector object_paths = RepositoryUtility::GetObjects(); + BOOST_FOREACH(const Dictionary::Ptr& agent, AgentUtility::GetAgents()) { + + /* store existing structure in index */ + inventory->Set(agent->Get("endpoint"), agent); + Dictionary::Ptr repository = agent->Get("repository"); String zone = agent->Get("zone"); String endpoint = agent->Get("endpoint"); + Dictionary::Ptr host_services = make_shared(); + ObjectLock olock(repository); BOOST_FOREACH(const Dictionary::Pair& kv, repository) { String host = kv.first; + String host_pattern = host + ".conf"; + bool skip = false; + + BOOST_FOREACH(const String& object_path, object_paths) { + if (object_path.Contains(host_pattern)) { + Log(LogWarning, "cli") + << "Host '" << host << "' already existing. Skipping its creation."; + skip = true; + break; + } + } + + if (skip) + continue; /* add a new host to the config repository */ Dictionary::Ptr host_attrs = make_shared(); + host_attrs->Set("__name", host); + host_attrs->Set("name", host); host_attrs->Set("check_command", "dummy"); //TODO: add a repository-host template host_attrs->Set("zone", zone); @@ -92,11 +128,36 @@ int AgentUpdateConfigCommand::Run(const boost::program_options::variables_map& v } Array::Ptr services = kv.second; + + if (services->GetLength() == 0) { + Log(LogNotice, "cli") + << "Host '" << host << "' without services."; + continue; + } + ObjectLock xlock(services); BOOST_FOREACH(const String& service, services) { + String service_pattern = host + "/" + service + ".conf"; + bool skip = false; + + BOOST_FOREACH(const String& object_path, object_paths) { + if (object_path.Contains(service_pattern)) { + Log(LogWarning, "cli") + << "Service '" << service << "' on Host '" << host << "' already existing. Skipping its creation."; + skip = true; + break; + } + } + + if (skip) + continue; + /* add a new service for this host to the config repository */ Dictionary::Ptr service_attrs = make_shared(); + String long_name = host + "!" + service; //use NameComposer? + service_attrs->Set("__name", long_name); + service_attrs->Set("name", service); service_attrs->Set("host_name", host); //Required for host-service relation service_attrs->Set("check_command", "dummy"); //TODO: add a repository-service template service_attrs->Set("zone", zone); @@ -111,6 +172,8 @@ int AgentUpdateConfigCommand::Run(const boost::program_options::variables_map& v /* write a new zone and endpoint for the agent */ Dictionary::Ptr endpoint_attrs = make_shared(); + endpoint_attrs->Set("__name", endpoint); + endpoint_attrs->Set("name", endpoint); Dictionary::Ptr settings = agent->Get("settings"); @@ -130,6 +193,8 @@ int AgentUpdateConfigCommand::Run(const boost::program_options::variables_map& v Array::Ptr zone_members = make_shared(); zone_members->Add(endpoint); + zone_attrs->Set("__name", zone); + zone_attrs->Set("name", zone); zone_attrs->Set("endpoints", zone_members); zone_attrs->Set("parent", agent->Get("parent_zone")); @@ -139,12 +204,91 @@ int AgentUpdateConfigCommand::Run(const boost::program_options::variables_map& v } } + /* check if there are objects inside the old_inventory which do not exist anymore */ + BOOST_FOREACH(const Dictionary::Pair& old_agent_objs, old_inventory) { + + String old_agent_name = old_agent_objs.first; + + /* check if the agent was dropped */ + if (!inventory->Contains(old_agent_name)) { + Log(LogInformation, "cli") + << "Agent update found old agent '" << old_agent_name << "'. Removing it and all of its hosts/services."; + + //TODO Remove an agent and all of his hosts + Dictionary::Ptr old_agent = old_inventory->Get(old_agent_name); + Dictionary::Ptr old_agent_repository = old_agent->Get("repository"); + + ObjectLock olock(old_agent_repository); + BOOST_FOREACH(const Dictionary::Pair& kv, old_agent_repository) { + String host = kv.first; + + Dictionary::Ptr host_attrs = make_shared(); + host_attrs->Set("name", host); + RepositoryUtility::RemoveObject(host, "Host", host_attrs); //this removes all services for this host as well + } + + //TODO: Remove zone/endpoint information as well + String zone = old_agent->Get("zone"); + String endpoint = old_agent->Get("endpoint"); + + Dictionary::Ptr zone_attrs = make_shared(); + zone_attrs->Set("name", zone); + RepositoryUtility::RemoveObject(zone, "Zone", zone_attrs); + + Dictionary::Ptr endpoint_attrs = make_shared(); + endpoint_attrs->Set("name", endpoint); + RepositoryUtility::RemoveObject(endpoint, "Endpoint", endpoint_attrs); + } else { + /* get the current agent */ + Dictionary::Ptr new_agent = inventory->Get(old_agent_name); + Dictionary::Ptr new_agent_repository = new_agent->Get("repository"); + + Dictionary::Ptr old_agent = old_inventory->Get(old_agent_name); + Dictionary::Ptr old_agent_repository = old_agent->Get("repository"); + + ObjectLock xlock(old_agent_repository); + BOOST_FOREACH(const Dictionary::Pair& kv, old_agent_repository) { + String old_host = kv.first; + + if (!new_agent_repository->Contains(old_host)) { + Log(LogInformation, "cli") + << "Agent update found old host '" << old_host << "' on agent '" << old_agent_name << "'. Removing it."; + + Dictionary::Ptr host_attrs = make_shared(); + host_attrs->Set("name", old_host); + RepositoryUtility::RemoveObject(old_host, "Host", host_attrs); //this will remove all services for this host too + } else { + /* host exists, now check all services for this host */ + Array::Ptr old_services = kv.second; + Array::Ptr new_services = new_agent_repository->Get(old_host); + + ObjectLock ylock(old_services); + BOOST_FOREACH(const String& old_service, old_services) { + if (!new_services->Contains(old_service)) { + Log(LogInformation, "cli") + << "Agent update found old service '" << old_service << "' on host '" << old_host + << "' on agent '" << old_agent_name << "'. Removing it."; + + Dictionary::Ptr service_attrs = make_shared(); + service_attrs->Set("name", old_service); + service_attrs->Set("host_name", old_host); + RepositoryUtility::RemoveObject(old_service, "Service", service_attrs); + } + } + } + } + } + } + Log(LogInformation, "cli", "Committing agent configuration."); RepositoryUtility::PrintChangeLog(std::cout); std::cout << "\n"; RepositoryUtility::CommitChangeLog(); + /* store the new inventory for next run */ + Utility::SaveJsonFile(inventory_path, inventory); + std::cout << "Make sure to reload Icinga 2 for these changes to take effect." << std::endl; return 0; -- 2.40.0