Merge pull request #5606 from Icinga/feature/remove-bottom-up

Remove bottom-up client mode
This commit is contained in:
Michael Friedrich 2017-09-22 14:48:51 +02:00 committed by GitHub
commit 92a399289a
39 changed files with 490 additions and 3913 deletions

View File

@ -159,21 +159,6 @@ This `include` directive takes care of including the configuration files for all
the features which have been enabled with `icinga2 feature enable`. See
[Enabling/Disabling Features](11-cli-commands.md#enable-features) for more details.
/**
* The repository.d directory contains all configuration objects
* managed by the 'icinga2 repository' CLI commands.
*/
include_recursive "repository.d"
This `include_recursive` directive is used for discovery of services on remote clients
and their generated configuration described in
[this chapter](06-distributed-monitoring.md#distributed-monitoring-bottom-up).
**Note**: This has been DEPRECATED in Icinga 2 v2.6 and is **not** required for
satellites and clients using the [top down approach](#06-distributed-monitoring.md#distributed-monitoring-top-down).
You can safely disable/remove it.
/**
* Although in theory you could define all your objects in this file
* the preferred way is to create separate directories and files in the conf.d
@ -265,7 +250,6 @@ Available configuration files which are installed by default:
* [templates.conf](04-configuring-icinga-2.md#templates-conf)
* [downtimes.conf](04-configuring-icinga-2.md#downtimes-conf)
* [timeperiods.conf](04-configuring-icinga-2.md#timeperiods-conf)
* [satellite.conf](04-configuring-icinga-2.md#satellite-conf)
* [api-users.conf](04-configuring-icinga-2.md#api-users-conf)
* [app.conf](04-configuring-icinga-2.md#app-conf)
@ -697,18 +681,6 @@ and `never`. TimePeriod objects are referenced by `*period`
objects such as hosts, services or notifications.
#### satellite.conf <a id="satellite-conf"></a>
Includes default templates and dependencies for
[monitoring remote clients](06-distributed-monitoring.md#distributed-monitoring)
using service discovery and
[config generation](06-distributed-monitoring.md#distributed-monitoring-bottom-up)
on the master. Can be ignored/removed on setups not using this feature.
Further details on the monitoring configuration can be found in the
[monitoring basics](03-monitoring-basics.md#monitoring-basics) chapter.
#### api-users.conf <a id="api-users-conf"></a>
Provides the default [ApiUser](09-object-types.md#objecttype-apiuser) object

View File

@ -732,21 +732,25 @@ the [detailed configuration modes](06-distributed-monitoring.md#distributed-moni
There are different ways to ensure that the Icinga 2 cluster nodes execute
checks, send notifications, etc.
Two different modes are available for synchronizing the host/service object's configuration between nodes and for executing checks:
The preferred method is to configure monitoring objects on the master
and distribute the configuration to satellites and clients.
The preferred mode is the [top down](06-distributed-monitoring.md#distributed-monitoring-top-down) approach.
This mode sends the configuration and commands from the master to the child zones.
The following chapters will explain this in detail with hands-on manual configuration
examples. You should test and implement this once to fully understand how it works.
The [bottom up](06-distributed-monitoring.md#distributed-monitoring-bottom-up) has been **deprecated in v2.6 and will be removed in future releases**.
This mode leaves the configuration files on the child nodes and requires an import on the parent nodes.
Once you are familiar with Icinga 2 and distributed monitoring, you
can start with additional integrations to manage and deploy your
configuration:
**Note**: Check results are always sent from the child nodes to the parent nodes.
This happens automatically and is ensured by the cluster protocol.
* [Icinga Director](https://github.com/icinga/icingaweb2-module-director) provides a web interface to manage configuration and also allows to sync imported resouces (CMDB, PuppetDB, etc.)
* [Ansible Roles](https://github.com/Icinga/icinga2-ansible)
* [Puppet Module](https://github.com/Icinga/puppet-icinga2)
* [Chef Cookbook](https://github.com/Icinga/chef-icinga2)
More details can be found [here](#configuration-tools).
### Top Down <a id="distributed-monitoring-top-down"></a>
According to feedback that we've received from the community, this is the most commonly used mode.
There are two different behaviors with check execution:
* Send a command execution event remotely: The scheduler still runs on the parent node.
@ -1087,324 +1091,6 @@ the [scenarios](06-distributed-monitoring.md#distributed-monitoring-scenarios)
section where you can find detailed information on extending the setup.
### Bottom Up Import <a id="distributed-monitoring-bottom-up"></a>
> **Warning**
>
> This mode has been deprecated in v2.6. You are strongly advised to
> migrate your existing configuration files to the [top down mode](06-distributed-monitoring.md#distributed-monitoring-top-down).
>
> Make sure to follow the release announcements on the [Icinga website](https://www.icinga.com).
This mode requires that you manage the configuration on the client itself.
Edit the configuration files in `/etc/icinga2/conf.d` or any other
directory included in the `icinga2.conf` file.
The client will send information about the configured objects to
the parent zone members where they can generate configuration
objects.
![Icinga 2 Distributed Bottom Up](images/distributed-monitoring/icinga2_distributed_bottom_up.png)
Advantages:
* Each child node comes configured with the most common local checks in the `conf.d` directory.
* Central repository for zones, endpoints, hosts, and services with configuration repository import.
Disadvantages:
* No object attribute sync. Parent nodes cannot filter specific attributes in assign expressions.
* Does not reliably work with a HA parent zone (single master preferred).
* Configuration management of many client nodes is hard or impossible if you don't have access to them.
On the master node, you can list and import the configuration sent from all the clients.
This example shows all client services on the master node `icinga2-master1.localdomain`:
[root@icinga2-master1.localdomain /]# icinga2 node list
Warning: CLI command 'node list' is DEPRECATED! Please read the Changelog.
Node 'icinga2-client3.localdomain' (last seen: Sun Aug 14 11:19:14 2016)
* Host 'icinga2-client2.localdomain'
* Service 'disk'
* Service 'disk C:'
* Service 'icinga'
* Service 'load'
* Service 'ping4'
* Service 'ping6'
* Service 'procs'
* Service 'swap'
* Service 'users'
The object configuration must exist on the master node as well
in order to receive check results from the clients. Therefore
you need to invoke the `node update-config` command:
[root@icinga2-master1.localdomain /]# icinga2 node update-config
Warning: CLI command 'node update-config' is DEPRECATED! Please read the Changelog.
information/cli: Updating node configuration for
...
The generated configuration objects are located in `/etc/icinga2/repository.d`.
If you have accidentally added specific hosts or services, you can safely purge
them from this directory and restart Icinga 2.
The generated host object uses the `cluster-zone` check command as
[health check](06-distributed-monitoring.md#distributed-monitoring-health-checks).
**Tip**: In case you want to blacklist or whitelist certain hosts and/or services
on the master, use the `icinga2 node {black,white}list`
commands.
In this example we're first putting all `ping*` services on all hosts on the blacklist.
With the next command we allow the host `probe` to run the service `ping4`:
# icinga2 node blacklist add --zone "*" --host "*" --service "ping*"
# icinga2 node whitelist add --zone "*" --host "probe" --service "ping4"
You can `list` and `remove` existing blacklists:
# icinga2 node blacklist list
Listing all blacklist entries:
blacklist filter for Node: '*' Host: '*' Service: 'ping*'.
# icinga2 node whitelist list
Listing all whitelist entries:
whitelist filter for Node: '*' Host: 'probe' Service: 'ping4'.
There are certain limitations with this mode. Currently the repository
does not sync object attributes (custom attributes, group memberships)
from the client to the master.
You can manually edit the configuration in `/etc/icinga2/repository.d`
and fix it. This will help with additional notification apply rules
or group memberships required for Icinga Web 2 and addons.
#### Bottom Up Migration to Top Down <a id="distributed-monitoring-bottom-up-migration-top-down"></a>
The bottom up mode has been deprecated and you should be prepared to migrate
your clients to the existing [top down mode](06-distributed-monitoring.md#distributed-monitoring-top-down).
The bottom up mode generates configuration files on the master node underneath
the `/etc/icinga2/repository.d` directory. This is achieved by running the
`node update-config` CLI command and depends on manual user interaction.
Unless you are changing something on the client which requires to run the
CLI command the configuration files in `repository.d` remain untouched.
The Icinga 2 client generates an object repository from local configuration
(usually included in `conf.d` by default) and syncs that to the master. The
`node update-config` CLI command parses these repository files from the `/var/lib/icinga2/api/repository`
directory and generates the `repository.d` configuration files. In addition to
that blacklist and whitelist settings are evaluated.
Those CLI commands also hide the fact that each client needs its own `Zone`
and `Endpoint` object as described [here](06-distributed-monitoring.md#distributed-monitoring-roles).
If you are certain that the master node has an up-to-date `repository.d`
ensure that all your clients **do not include conf.d in their icinga2.conf**
configuration file.
**Steps on each client**:
Add a [global zone](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync)
for syncing check commands later:
[root@icinga2-client3.localdomain /]# vim /etc/icinga2/zones.conf
object Zone "global-templates" {
global = true
}
Note: Packages >= 2.7 provide this configuration by default.
You don't need any local configuration on the client except for
CheckCommand definitions which can be synced using the global zone
above. Therefore disable the inclusion of the `conf.d` directory
in `/etc/icinga2/icinga2.conf`.
[root@icinga2-client3.localdomain /]# vim /etc/icinga2/icinga2.conf
// Commented out, not required on a client as command endpoint
//include_recursive "conf.d"
Edit the `api` feature on the client `icinga2-client2.localdomain` in
the `/etc/icinga2/features-enabled/api.conf` file and make sure to set
`accept_commands` and `accept_config` to `true`:
[root@icinga2-client3.localdomain /]# vim /etc/icinga2/features-enabled/api.conf
object ApiListener "api" {
//...
accept_commands = true
accept_config = true
}
Now it is time to validate the configuration and to restart the Icinga 2 daemon
on the client host `icinga2-client3.localdomain`.
Example on CentOS 7:
[root@icinga2-client3.localdomain /]# icinga2 daemon -C
[root@icinga2-client3.localdomain /]# systemctl restart icinga2
**Steps on the configuration master node**:
The migration strategy will guide you to use the client(s) as
[top down command endpoint](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint).
The `repository.d` directory is organised as a tree of object type directories.
[root@icinga2-master1.localdomain /]# tree /etc/icinga2/repository.d
repository.d/
├── README
├── endpoints
│   └── icinga2-client3.localdomain.conf
├── hosts
│   ├── icinga2-client3.localdomain
│   │   ├── disk\ C%3A.conf
│   │   ├── disk.conf
│   │   ├── icinga.conf
│   │   ├── load.conf
│   │   ├── ping4.conf
│   │   ├── ping6.conf
│   │   ├── procs.conf
│   │   ├── swap.conf
│   │   └── users.conf
│   └── icinga2-client3.localdomain.conf
└── zones
└── icinga2-client3.localdomain.conf
The `endpoints` and `zones` directories contain the required connection
information for your client. Decide whether to add the configuration
objects to your `zones.conf` file or keep them as is.
The `hosts` directory contains a `.conf` file for the host object
and a directory with the same name as the host with service configuration
files, in this example `hosts/icinga2-client3.localdomain`.
The simplest migration path is to merge the Zone, Endpoint, Host and Service
object configuration into one new file called `<FQDN>.conf`, for example
`icinga2-client2.localdomain.conf`.
Therefore create a new file in your master's zone directory in `zones.d`.
In this example we assume that the zone is called `master`.
Use the information provided by the configuration files `/etc/icinga2/repository.d/zones/icinga2-client3.localdomain.conf`
and `/etc/icinga2/repository.d/endpoints/icinga2-client3.localdomain.conf`.
[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-client3.localdomain.conf
object Zone "icinga2-client3.localdomain" {
endpoints = [ "icinga2-client3.localdomain" ]
parent = "master" //defined in zones.conf
}
object Endpoint "icinga2-client3.localdomain" {
//set the host attribute if the master should connect to the client endpoint
}
Now fetch the Host object information located in the `/etc/icinga2/repository.d/hosts/icinga2-client3.localdomain.conf`
file and adopt it for your needs. One common disadvantage of the bottom up mode is that
the host object attributes are not synced from the client to the master. Log onto your client
and manually copy the required attributes into a new host object.
Change the `check_command` attribute to `hostalive` to just ping the host. If you want to keep the
client connection check `cluster-zone`, you need to add the `cluster_zone` custom attribute.
In addition to that add a new custom attribute called `client_endpoint` which stores
the command endpoint information. In case you need to learn more details please refer to
the [top down command endpoint](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)
chapter.
[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-client3.localdomain.conf
object Host "icinga2-client3.localdomain.conf" {
//check_command = "hostalive"
check_command = "cluster-zone"
vars.cluster_zone = name //host name must be the same as the client's zone name
vars.client_endpoint = name //host name must be the same as the client's endpoint name
vars.os = "Linux" //added for group membership
}
Extract the service objects from the configuration files in the
`/etc/icinga2/repository.d/hosts/icinga2-client3.localdomain` directory
and add them into the `/etc/icinga2/zones.d/master/icinga2-client3.localdomain.conf`
file.
Best practice is to use a generic [service apply rule](03-monitoring-basics.md#using-apply)
for each service. Identify common services on your hosts and modify the apply rules for
your own needs.
Add the services to the generic `services.conf` file in `/etc/icinga2/zones.d/master`.
Change the `check_command` attribute to the actual `CheckCommand` object which should
be executed. This information is available on the client in the `conf.d` directory.
Make sure to also extract all required custom attributes from the client and add them
to the service object e.g. check command thresholds for [disk](10-icinga-template-library.md#plugin-check-command-disk).
Remove the `zone` attribute from all services. Specify the `command_endpoint` attribute
for all service checks which should be run locally on the client, for example `disk`.
[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master
[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/services.conf
apply Service "disk" {
check_command = "disk" //modified
vars.disk_wfree = "10%" //copied from client
vars.disk_cfree = "5%" //copied from client
command_endpoint = host.vars.client_endpoint
assign where host.vars.client_endpoint //create service objects for all clients with command endpoint
}
The `ping4` service should be executed on the master node itself. That is why
you must not add the `command_endpoint` attribute here.
[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/services.conf
apply Service "ping4" {
check_command = "ping4" //modified
vars.ping_wrta = 200 //copied from client
vars.ping_crta = 500 //copied from client
assign where host.address
}
In case you have been using custom CheckCommand definitions on your client
you must sync them again using a global zone.
Ensure that the global zone definition is already there in your `zones.conf`
file.
[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf
object Zone "global-templates" {
global = true
}
Note: Packages >= 2.7 provide this configuration by default.
Put existing CheckCommand definitions into `/etc/icinga2/zones.d/global-templates/commands.conf`.
[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/global-templates
[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/global-templates/commands.conf
object CheckCommand "my-check" {
//...
}
Now validate the configuration and restart Icinga 2.
[root@icinga2-master1.localdomain /]# icinga2 daemon -C
[root@icinga2-master1.localdomain /]# systemctl restart icinga2
In case you have additional apply rules in place and have trouble with duplicated objects please
adopt and merge them accordingly.
If you are eager to start fresh instead you might take a look into the
[Icinga Director](https://github.com/icinga/icingaweb2-module-director).

File diff suppressed because it is too large Load Diff

View File

@ -488,10 +488,6 @@ Clients which have the `checker` feature enabled will attempt
to execute checks for local services and send their results
back to the master.
> **Note**
>
> This is part of the deprecated client bottom up mode.
If you now have the same host and service objects on the
master you will receive wrong check results from the client.

View File

@ -5,6 +5,8 @@ are scheme updates for the IDO database.
## Upgrading to v2.8 <a id="upgrading-to-2-8"></a>
### Changed Certificate Paths <a id="upgrading-to-2-8-certificate-paths"></a>
The default certificate path was changed from `/etc/icinga2/pki` to
`/var/lib/icinga2/certs`.
@ -15,6 +17,22 @@ The [setup CLI commands](06-distributed-monitoring.md#distributed-monitoring-set
default [ApiListener configuration](06-distributed-monitoring.md#distributed-monitoring-apilistener)
have been adjusted to these paths too.
### Removed Bottom Up Client Mode <a id="upgrading-to-2-8-removed-bottom-up-client-mode"></a>
This client mode was deprecated in 2.6 and was removed in 2.8.
The node CLI command does not provide `list` or `update-config` anymore.
The clients don't need to have a local `conf.d` directory included.
The setup wizards for Linux and Windows attempt to disable this by default.
Icinga 2 continues to run with the generated and imported configuration.
You are advised to [migrate](https://github.com/Icinga/icinga2/issues/4798)
any existing configuration to the "top down" mode with the help of the
Icinga Director or config management tools such as Puppet, Ansible, etc.
### Removed Classic UI Config Package <a id="upgrading-to-2-8-removed-classicui-config-package"></a>
The config meta package `classicui-config` and the configuration files
have been removed. You need to manually configure
this legacy interface. Create a backup of the configuration

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -50,7 +50,6 @@ else()
endif()
install_if_not_exists(icinga2/conf.d/notifications.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/satellite.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/templates.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/timeperiods.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/users.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
@ -63,14 +62,12 @@ endif()
install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
install_if_not_exists(icinga2/zones.d/README ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/zones.d)
install_if_not_exists(icinga2/repository.d/README ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/repository.d)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/logrotate.d/icinga2 ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d)
endif()
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/pki\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/repository.d\")")
if(NOT WIN32)
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled\")")

View File

@ -1,33 +0,0 @@
/*
* Host and Service templates for the Agent Setup.
*/
/**
* Provides settings for satellite hosts managed by 'icinga2 repository'.
* Define your global attributes here, for example custom
* attributes used for notifications, etc.
*/
template Host "satellite-host" {
vars.notification["mail"] = {
groups = [ "icingaadmins" ]
}
}
/**
* Provides settings for satellite services managed by 'icinga2 repository'.
* Define your global satellite attributes here, for example custom
* attributes used for notifications, etc.
*/
template Service "satellite-service" {
vars.notification["mail"] = {
groups = [ "icingaadmins" ]
}
}
apply Dependency "satellite-host" to Host {
parent_host_name = host.zone
assign where host.zone != "" && "satellite-host" in host.templates
}

View File

@ -49,12 +49,6 @@ include <nscp>
*/
include "features-enabled/*.conf"
/**
* The repository.d directory contains all configuration objects
* managed by the 'icinga2 repository' CLI commands.
*/
include_recursive "repository.d"
/**
* Although in theory you could define all your objects in this file
* the preferred way is to create separate directories and files in the conf.d

View File

@ -1,3 +0,0 @@
This directory contains configuration files managed by 'icinga2 repository'
cli commands. Please run 'icinga2 repository --help' to see all available
options.

View File

@ -47,12 +47,6 @@ include <nscp>
*/
include "features-enabled/*.conf"
/**
* The repository.d directory contains all configuration objects
* managed by the 'icinga2 repository' CLI commands.
*/
include_recursive "repository.d"
/**
* Although in theory you could define all your objects in this file
* the preferred way is to create separate directories and files in the conf.d

View File

@ -250,7 +250,6 @@ static int InstallIcinga(void)
MkDirP(dataDir + "/var/lib/icinga2/agent/inventory");
MkDirP(dataDir + "/var/lib/icinga2/api/config");
MkDirP(dataDir + "/var/lib/icinga2/api/log");
MkDirP(dataDir + "/var/lib/icinga2/api/repository");
MkDirP(dataDir + "/var/lib/icinga2/api/zones");
MkDirP(dataDir + "/var/log/icinga2/compat/archive");
MkDirP(dataDir + "/var/log/icinga2/crash");

View File

@ -657,9 +657,7 @@ fi
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_sysconfdir}/%{name}/features-available
%exclude %{_sysconfdir}/%{name}/features-available/ido-*.conf
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_sysconfdir}/%{name}/features-enabled
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_sysconfdir}/%{name}/repository.d
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_sysconfdir}/%{name}/scripts
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_sysconfdir}/%{name}/repository.d
%attr(0750,%{icinga_user},%{icinga_group}) %dir %{_sysconfdir}/%{name}/zones.d
%config(noreplace) %attr(0640,%{icinga_user},%{icinga_group}) %{_sysconfdir}/%{name}/%{name}.conf
%config(noreplace) %attr(0640,root,%{icinga_group}) %{_sysconfdir}/%{name}/init.conf
@ -667,7 +665,6 @@ fi
%config(noreplace) %attr(0640,%{icinga_user},%{icinga_group}) %{_sysconfdir}/%{name}/zones.conf
%config(noreplace) %attr(0640,%{icinga_user},%{icinga_group}) %{_sysconfdir}/%{name}/conf.d/*.conf
%config(noreplace) %attr(0640,%{icinga_user},%{icinga_group}) %{_sysconfdir}/%{name}/features-available/*.conf
%config(noreplace) %attr(0640,%{icinga_user},%{icinga_group}) %{_sysconfdir}/%{name}/repository.d/*
%config(noreplace) %attr(0640,%{icinga_user},%{icinga_group}) %{_sysconfdir}/%{name}/zones.d/*
%config(noreplace) %{_sysconfdir}/%{name}/scripts/*
%dir %{_libexecdir}/%{name}
@ -731,5 +728,3 @@ fi
%{_datadir}/nano/%{name}.nanorc
%changelog
* Tue Jun 20 2017 Markus Frosch <markus.frosch@icinga.com> 2.7.0-1
- Update to 2.7.0

View File

@ -18,15 +18,13 @@
set(cli_SOURCES
apisetupcommand.cpp apisetuputility.cpp
calistcommand.cpp casigncommand.cpp
nodeaddcommand.cpp nodeblackandwhitelistcommand.cpp nodelistcommand.cpp noderemovecommand.cpp
nodesetcommand.cpp nodesetupcommand.cpp nodeupdateconfigcommand.cpp nodewizardcommand.cpp nodeutility.cpp
nodesetupcommand.cpp nodewizardcommand.cpp nodeutility.cpp
clicommand.cpp
consolecommand.cpp
daemoncommand.cpp daemonutility.cpp
featureenablecommand.cpp featuredisablecommand.cpp featurelistcommand.cpp featureutility.cpp
objectlistcommand.cpp objectlistutility.cpp
pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp pkirequestcommand.cpp pkisavecertcommand.cpp pkiticketcommand.cpp
repositoryclearchangescommand.cpp repositorycommitcommand.cpp repositoryobjectcommand.cpp repositoryutility.cpp
variablegetcommand.cpp variablelistcommand.cpp variableutility.cpp
troubleshootcommand.cpp
)

View File

@ -1,65 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/nodeaddcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/add", NodeAddCommand);
String NodeAddCommand::GetDescription(void) const
{
return "Add Icinga 2 node.";
}
String NodeAddCommand::GetShortDescription(void) const
{
return "add node";
}
int NodeAddCommand::GetMinArguments(void) const
{
return 1;
}
bool NodeAddCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node add" CLI command.
*
* @returns An exit status.
*/
int NodeAddCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
NodeUtility::AddNode(ap[0]);
return 0;
}

View File

@ -1,47 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef NODEADDCOMMAND_H
#define NODEADDCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node add" command.
*
* @ingroup cli
*/
class NodeAddCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeAddCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual int GetMinArguments(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODEADDCOMMAND_H */

View File

@ -1,153 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/nodeblackandwhitelistcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/objectlock.hpp"
#include "base/json.hpp"
#include <iostream>
#include <fstream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_BLACKANDWHITELIST_CLICOMMAND(whitelist);
REGISTER_BLACKANDWHITELIST_CLICOMMAND(blacklist);
BlackAndWhitelistCommand::BlackAndWhitelistCommand(const String& type, BlackAndWhitelistCommandType command)
: m_Type(type), m_Command(command)
{ }
String BlackAndWhitelistCommand::GetDescription(void) const
{
String description;
switch (m_Command) {
case BlackAndWhitelistCommandAdd:
description = "Adds a new";
break;
case BlackAndWhitelistCommandRemove:
description = "Removes a";
break;
case BlackAndWhitelistCommandList:
description = "Lists all";
break;
}
description += " " + m_Type + " filter";
if (m_Command == BlackAndWhitelistCommandList)
description += "s";
return description;
}
String BlackAndWhitelistCommand::GetShortDescription(void) const
{
String description;
switch (m_Command) {
case BlackAndWhitelistCommandAdd:
description = "adds a new";
break;
case BlackAndWhitelistCommandRemove:
description = "removes a";
break;
case BlackAndWhitelistCommandList:
description = "lists all";
break;
}
description += " " + m_Type + " filter";
if (m_Command == BlackAndWhitelistCommandList)
description += "s";
return description;
}
bool BlackAndWhitelistCommand::IsDeprecated(void) const
{
return true;
}
void BlackAndWhitelistCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
if (m_Command == BlackAndWhitelistCommandAdd || m_Command == BlackAndWhitelistCommandRemove) {
visibleDesc.add_options()
("zone", po::value<std::string>(), "The name of the zone")
("host", po::value<std::string>(), "The name of the host")
("service", po::value<std::string>(), "The name of the service");
}
}
/**
* The entry point for the "node <whitelist/blacklist> <add/remove/list>" CLI command.
*
* @returns An exit status.
*/
int BlackAndWhitelistCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (m_Command == BlackAndWhitelistCommandAdd) {
if (!vm.count("zone")) {
Log(LogCritical, "cli", "At least the zone name filter is required!");
return 1;
}
if (!vm.count("host")) {
Log(LogCritical, "cli", "At least the host name filter is required!");
return 1;
}
String service_filter;
if (vm.count("service"))
service_filter = vm["service"].as<std::string>();
return NodeUtility::UpdateBlackAndWhiteList(m_Type, vm["zone"].as<std::string>(), vm["host"].as<std::string>(), service_filter);
} else if (m_Command == BlackAndWhitelistCommandList) {
return NodeUtility::PrintBlackAndWhiteList(std::cout, m_Type);
} else if (m_Command == BlackAndWhitelistCommandRemove) {
if (!vm.count("zone")) {
Log(LogCritical, "cli", "The zone name filter is required!");
return 1;
}
if (!vm.count("host")) {
Log(LogCritical, "cli", "The host name filter is required!");
return 1;
}
String zone_filter = vm["zone"].as<std::string>();
String host_filter = vm["host"].as<std::string>();
String service_filter;
if (vm.count("service")) {
service_filter = vm["service"].as<std::string>();
}
return NodeUtility::RemoveBlackAndWhiteList(m_Type, vm["zone"].as<std::string>(), vm["host"].as<std::string>(), service_filter);
}
return 0;
}

View File

@ -1,80 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef BLACKANDWHITELISTCOMMAND_H
#define BLACKANDWHITELISTCOMMAND_H
#include "cli/clicommand.hpp"
#include <boost/algorithm/string/case_conv.hpp>
namespace icinga
{
enum BlackAndWhitelistCommandType
{
BlackAndWhitelistCommandAdd,
BlackAndWhitelistCommandRemove,
BlackAndWhitelistCommandList
};
/**
* The "repository <type> <add/remove/list>" command.
*
* @ingroup cli
*/
class I2_CLI_API BlackAndWhitelistCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(BlackAndWhitelistCommand);
BlackAndWhitelistCommand(const String& type, BlackAndWhitelistCommandType command);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
private:
String m_Type;
BlackAndWhitelistCommandType m_Command;
};
#define REGISTER_BLACKANDWHITELIST_CLICOMMAND(type) \
INITIALIZE_ONCE([]() { \
String ltype = #type; \
boost::algorithm::to_lower(ltype); \
\
std::vector<String> name; \
name.push_back("node"); \
name.push_back(ltype); \
name.push_back("add"); \
CLICommand::Register(name, new BlackAndWhitelistCommand(#type, BlackAndWhitelistCommandAdd)); \
\
name[2] = "remove"; \
CLICommand::Register(name, new BlackAndWhitelistCommand(#type, BlackAndWhitelistCommandRemove)); \
\
name[2] = "list"; \
CLICommand::Register(name, new BlackAndWhitelistCommand(#type, BlackAndWhitelistCommandList)); \
})
}
#endif /* BLACKANDWHITELISTCOMMAND_H */

View File

@ -1,76 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/nodelistcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/console.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/list", NodeListCommand);
String NodeListCommand::GetDescription(void) const
{
return "Lists all Icinga 2 nodes.";
}
String NodeListCommand::GetShortDescription(void) const
{
return "lists all nodes";
}
bool NodeListCommand::IsDeprecated(void) const
{
return true;
}
void NodeListCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("batch", "list nodes in json");
}
/**
* The entry point for the "node list" CLI command.
*
* @returns An exit status.
*/
int NodeListCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (!ap.empty()) {
Log(LogWarning, "cli")
<< "Ignoring parameters: " << boost::algorithm::join(ap, " ");
}
if (vm.count("batch"))
NodeUtility::PrintNodesJson(std::cout);
else
NodeUtility::PrintNodes(std::cout);
return 0;
}

View File

@ -1,48 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef NODELISTCOMMAND_H
#define NODELISTCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node list" command.
*
* @ingroup cli
*/
class NodeListCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeListCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODELISTCOMMAND_H */

View File

@ -1,77 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/noderemovecommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/remove", NodeRemoveCommand);
String NodeRemoveCommand::GetDescription(void) const
{
return "Removes Icinga 2 node.";
}
String NodeRemoveCommand::GetShortDescription(void) const
{
return "removes node";
}
std::vector<String> NodeRemoveCommand::GetPositionalSuggestions(const String& word) const
{
return NodeUtility::GetNodeCompletionSuggestions(word);
}
int NodeRemoveCommand::GetMinArguments(void) const
{
return 1;
}
int NodeRemoveCommand::GetMaxArguments(void) const
{
return -1;
}
bool NodeRemoveCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node remove" CLI command.
*
* @returns An exit status.
*/
int NodeRemoveCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
for (const String& node : ap) {
NodeUtility::RemoveNode(node);
}
return 0;
}

View File

@ -1,49 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef NODEREMOVECOMMAND_H
#define NODEREMOVECOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node remove" command.
*
* @ingroup cli
*/
class NodeRemoveCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeRemoveCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual int GetMinArguments(void) const override;
virtual int GetMaxArguments(void) const override;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODEREMOVECOMMAND_H */

View File

@ -1,94 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/nodesetcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/set", NodeSetCommand);
String NodeSetCommand::GetDescription(void) const
{
return "Set node attribute(s).";
}
String NodeSetCommand::GetShortDescription(void) const
{
return "set node attributes";
}
void NodeSetCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("host", po::value<std::string>(), "Icinga 2 host")
("port", po::value<std::string>(), "Icinga 2 port")
("log_duration", po::value<double>(), "Log duration (in seconds)");
}
int NodeSetCommand::GetMinArguments(void) const
{
return 1;
}
bool NodeSetCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node set" CLI command.
*
* @returns An exit status.
*/
int NodeSetCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
String repoFile = NodeUtility::GetNodeRepositoryFile(ap[0]);
if (!Utility::PathExists(repoFile)) {
Log(LogCritical, "cli")
<< "Node '" << ap[0] << "' does not exist.";
return 1;
}
String host, port = "5665";
double log_duration = 24 * 60 * 60;
if (vm.count("host"))
host = vm["host"].as<std::string>();
if (vm.count("port"))
port = vm["port"].as<std::string>();
if (vm.count("log_duration"))
log_duration = vm["log_duration"].as<double>();
NodeUtility::AddNodeSettings(ap[0], host, port, log_duration);
return 0;
}

View File

@ -1,49 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef NODESETCOMMAND_H
#define NODESETCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node set" command.
*
* @ingroup cli
*/
class NodeSetCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeSetCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual int GetMinArguments(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODESETCOMMAND_H */

View File

@ -1,427 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/nodeupdateconfigcommand.hpp"
#include "cli/nodeutility.hpp"
#include "cli/repositoryutility.hpp"
#include "cli/variableutility.hpp"
#include "base/logger.hpp"
#include "base/console.hpp"
#include "base/application.hpp"
#include "base/tlsutility.hpp"
#include "base/json.hpp"
#include "base/objectlock.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/update-config", NodeUpdateConfigCommand);
String NodeUpdateConfigCommand::GetDescription(void) const
{
return "Update Icinga 2 node config.";
}
String NodeUpdateConfigCommand::GetShortDescription(void) const
{
return "update node config";
}
ImpersonationLevel NodeUpdateConfigCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
bool NodeUpdateConfigCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node update-config" CLI command.
*
* @returns An exit status.
*/
int NodeUpdateConfigCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
//If there are changes pending, abort the current operation
if (RepositoryUtility::ChangeLogHasPendingChanges()) {
Log(LogWarning, "cli")
<< "There are pending changes for commit.\n"
<< "Please review and commit them using 'icinga2 repository commit [--simulate]'\n"
<< "or drop them using 'icinga2 repository clear-changes' before proceeding.";
return 1;
}
String inventory_path = NodeUtility::GetRepositoryPath() + "/inventory.index";
Dictionary::Ptr old_inventory = new Dictionary();
if (Utility::PathExists(inventory_path)) {
old_inventory = Utility::LoadJsonFile(inventory_path);
}
Dictionary::Ptr inventory = new Dictionary();
Log(LogInformation, "cli")
<< "Updating node configuration for ";
NodeUtility::PrintNodes(std::cout);
/* cache all existing object configs only once and pass it to AddObject() */
std::vector<String> object_paths = RepositoryUtility::GetObjects();
/* cache all existing changes only once and pass it to AddObject() */
Array::Ptr changes = new Array();
RepositoryUtility::GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changes));
std::vector<Dictionary::Ptr> nodes = NodeUtility::GetNodes();
/* first make sure that all nodes are valid and should not be removed */
for (const Dictionary::Ptr& node : nodes) {
Dictionary::Ptr repository = node->Get("repository");
String zone = node->Get("zone");
String endpoint = node->Get("endpoint");
String node_name = endpoint;
/* store existing structure in index */
inventory->Set(endpoint, node);
}
if (old_inventory) {
/* check if there are objects inside the old_inventory which do not exist anymore */
ObjectLock ulock(old_inventory);
for (const Dictionary::Pair& old_node_objs : old_inventory) {
String old_node_name = old_node_objs.first;
/* check if the node was dropped */
if (!inventory->Contains(old_node_name)) {
Log(LogInformation, "cli")
<< "Node update found old node '" << old_node_name << "'. Removing it and all of its hosts/services.";
//TODO Remove an node and all of his hosts
Dictionary::Ptr old_node = old_node_objs.second;
Dictionary::Ptr old_node_repository = old_node->Get("repository");
if (old_node_repository) {
ObjectLock olock(old_node_repository);
for (const Dictionary::Pair& kv : old_node_repository) {
String host = kv.first;
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("name", host);
RepositoryUtility::RemoveObject(host, "Host", host_attrs, changes); //this removes all services for this host as well
}
}
String zone = old_node->Get("zone");
String endpoint = old_node->Get("endpoint");
Dictionary::Ptr zone_attrs = new Dictionary();
zone_attrs->Set("name", zone);
RepositoryUtility::RemoveObject(zone, "Zone", zone_attrs, changes);
Dictionary::Ptr endpoint_attrs = new Dictionary();
endpoint_attrs->Set("name", endpoint);
RepositoryUtility::RemoveObject(endpoint, "Endpoint", endpoint_attrs, changes);
} else {
/* get the current node */
Dictionary::Ptr new_node = inventory->Get(old_node_name);
Dictionary::Ptr new_node_repository = new_node->Get("repository");
Dictionary::Ptr old_node = old_node_objs.second;
Dictionary::Ptr old_node_repository = old_node->Get("repository");
if (old_node_repository) {
ObjectLock xlock(old_node_repository);
for (const Dictionary::Pair& kv : old_node_repository) {
String old_host = kv.first;
if (old_host == "localhost") {
Log(LogWarning, "cli")
<< "Ignoring host '" << old_host << "'. Please make sure to configure a unique name on your node '" << old_node_name << "'.";
continue;
}
/* check against black/whitelist before trying to remove host */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", old_node_name, old_host, Empty) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", old_node_name, old_host, Empty)) {
Log(LogWarning, "cli")
<< "Host '" << old_node_name << "' on node '" << old_node_name << "' is blacklisted, but not whitelisted. Skipping.";
continue;
}
if (!new_node_repository->Contains(old_host)) {
Log(LogInformation, "cli")
<< "Node update found old host '" << old_host << "' on node '" << old_node_name << "'. Removing it.";
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("name", old_host);
RepositoryUtility::RemoveObject(old_host, "Host", host_attrs, changes); //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_node_repository->Get(old_host);
ObjectLock ylock(old_services);
for (const String& old_service : old_services) {
/* check against black/whitelist before trying to remove service */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", old_node_name, old_host, old_service) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", old_node_name, old_host, old_service)) {
Log(LogWarning, "cli")
<< "Service '" << old_service << "' on host '" << old_host << "' on node '"
<< old_node_name << "' is blacklisted, but not whitelisted. Skipping.";
continue;
}
if (!new_services->Contains(old_service)) {
Log(LogInformation, "cli")
<< "Node update found old service '" << old_service << "' on host '" << old_host
<< "' on node '" << old_node_name << "'. Removing it.";
Dictionary::Ptr service_attrs = new Dictionary();
service_attrs->Set("name", old_service);
service_attrs->Set("host_name", old_host);
RepositoryUtility::RemoveObject(old_service, "Service", service_attrs, changes);
}
}
}
}
}
}
}
}
/* next iterate over all nodes and add hosts/services */
for (const Dictionary::Ptr& node : nodes) {
Dictionary::Ptr repository = node->Get("repository");
String zone = node->Get("zone");
String endpoint = node->Get("endpoint");
String node_name = endpoint;
Dictionary::Ptr host_services = new Dictionary();
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", node_name, "*", Empty) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", node_name, "*", Empty)) {
Log(LogWarning, "cli")
<< "Skipping node '" << node_name << "' on blacklist.";
continue;
}
Log(LogInformation, "cli")
<< "Adding host '" << zone << "' to the repository.";
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("__name", zone);
host_attrs->Set("name", zone);
host_attrs->Set("check_command", "cluster-zone");
Array::Ptr host_imports = new Array();
host_imports->Add("satellite-host"); //default host node template
host_attrs->Set("import", host_imports);
if (!RepositoryUtility::AddObject(object_paths, zone, "Host", host_attrs, changes, false)) {
Log(LogWarning, "cli")
<< "Cannot add node host '" << zone << "' to the config repository!\n";
}
if (repository) {
ObjectLock olock(repository);
for (const Dictionary::Pair& kv : repository) {
String host = kv.first;
String host_pattern = host + ".conf";
bool skip_host = false;
if (host == "localhost") {
Log(LogWarning, "cli")
<< "Ignoring host '" << host << "'. Please make sure to configure a unique name on your node '" << endpoint << "'.";
continue;
}
for (const String& object_path : object_paths) {
if (object_path.Contains(host_pattern)) {
Log(LogNotice, "cli")
<< "Host '" << host << "' already exists.";
skip_host = true;
break;
}
}
/* host has already been created above */
if (host == zone)
skip_host = true;
bool host_was_blacklisted = false;
/* check against black/whitelist before trying to add host */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", node_name, host, Empty) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", node_name, host, Empty)) {
Log(LogWarning, "cli")
<< "Host '" << host << "' on node '" << node_name << "' is blacklisted, but not whitelisted. Not creating host object.";
skip_host = true;
host_was_blacklisted = true; //check this for services on this blacklisted host
}
if (!skip_host) {
/* add a new host to the config repository */
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("__name", host);
host_attrs->Set("name", host);
if (host == zone)
host_attrs->Set("check_command", "cluster-zone");
else {
host_attrs->Set("check_command", "dummy");
host_attrs->Set("zone", zone);
}
Array::Ptr host_imports = new Array();
host_imports->Add("satellite-host"); //default host node template
host_attrs->Set("import", host_imports);
RepositoryUtility::AddObject(object_paths, host, "Host", host_attrs, changes, false);
}
/* special condition: what if the host was blacklisted before, but the services should be generated? */
if (host_was_blacklisted) {
Log(LogNotice, "cli")
<< "Host '" << host << "' was blacklisted. Won't generate any services.";
continue;
}
Array::Ptr services = kv.second;
if (services->GetLength() == 0) {
Log(LogNotice, "cli")
<< "Host '" << host << "' without services.";
continue;
}
ObjectLock xlock(services);
for (const String& service : services) {
bool skip_service = false;
String service_pattern = host + "/" + service + ".conf";
for (const String& object_path : object_paths) {
if (object_path.Contains(service_pattern)) {
Log(LogNotice, "cli")
<< "Service '" << service << "' on Host '" << host << "' already exists.";
skip_service = true;
break;
}
}
/* check against black/whitelist before trying to add service */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", endpoint, host, service) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", endpoint, host, service)) {
Log(LogWarning, "cli")
<< "Service '" << service << "' on host '" << host << "' on node '"
<< node_name << "' is blacklisted, but not whitelisted. Not creating service object.";
skip_service = true;
}
if (skip_service)
continue;
/* add a new service for this host to the config repository */
Dictionary::Ptr service_attrs = new Dictionary();
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");
service_attrs->Set("zone", zone);
Array::Ptr service_imports = new Array();
service_imports->Add("satellite-service"); //default service node template
service_attrs->Set("import", service_imports);
if (!RepositoryUtility::AddObject(object_paths, service, "Service", service_attrs, changes, false))
continue;
}
}
}
/* write a new zone and endpoint for the node */
Dictionary::Ptr endpoint_attrs = new Dictionary();
endpoint_attrs->Set("__name", endpoint);
endpoint_attrs->Set("name", endpoint);
Dictionary::Ptr settings = node->Get("settings");
if (settings) {
if (settings->Contains("host"))
endpoint_attrs->Set("host", settings->Get("host"));
if (settings->Contains("port"))
endpoint_attrs->Set("port", settings->Get("port"));
}
Log(LogInformation, "cli")
<< "Adding endpoint '" << endpoint << "' to the repository.";
if (!RepositoryUtility::AddObject(object_paths, endpoint, "Endpoint", endpoint_attrs, changes, false)) {
Log(LogWarning, "cli")
<< "Cannot add node endpoint '" << endpoint << "' to the config repository!\n";
}
Dictionary::Ptr zone_attrs = new Dictionary();
Array::Ptr zone_members = new Array();
zone_members->Add(endpoint);
zone_attrs->Set("__name", zone);
zone_attrs->Set("name", zone);
zone_attrs->Set("endpoints", zone_members);
String parent_zone = VariableUtility::GetVariable("ZoneName");
if (parent_zone.IsEmpty()) {
Log(LogWarning, "cli")
<< "Variable 'ZoneName' is not set. Falling back to using 'master' as default. Please verify the generated configuration.";
parent_zone = "master";
}
zone_attrs->Set("parent", parent_zone);
Log(LogInformation, "cli")
<< "Adding zone '" << zone << "' to the repository.";
if (!RepositoryUtility::AddObject(object_paths, zone, "Zone", zone_attrs, changes, false)) {
Log(LogWarning, "cli")
<< "Cannot add node zone '" << zone << "' to the config repository!\n";
}
}
Log(LogInformation, "cli", "Committing node configuration.");
RepositoryUtility::PrintChangeLog(std::cout);
std::cout << "\n";
RepositoryUtility::CommitChangeLog();
/* store the new inventory for next run */
NodeUtility::CreateRepositoryPath();
Utility::SaveJsonFile(inventory_path, 0600, inventory);
std::cout << "Make sure to reload Icinga 2 for these changes to take effect." << std::endl;
return 0;
}

View File

@ -1,47 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef NODEUPDATECONFIGCOMMAND_H
#define NODEUPDATECONFIGCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "agent update-config" command.
*
* @ingroup cli
*/
class NodeUpdateConfigCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeUpdateConfigCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
};
}
#endif /* NODEUPDATECONFIGCOMMAND_H */

View File

@ -43,219 +43,6 @@
using namespace icinga;
String NodeUtility::GetRepositoryPath(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/api/repository";
}
String NodeUtility::GetNodeRepositoryFile(const String& name)
{
return GetRepositoryPath() + "/" + SHA256(name) + ".repo";
}
String NodeUtility::GetNodeSettingsFile(const String& name)
{
return GetRepositoryPath() + "/" + SHA256(name) + ".settings";
}
void NodeUtility::CreateRepositoryPath(const String& path)
{
if (!Utility::PathExists(path))
Utility::MkDirP(path, 0750);
String user = ScriptGlobal::Get("RunAsUser");
String group = ScriptGlobal::Get("RunAsGroup");
if (!Utility::SetFileOwnership(path, user, group)) {
Log(LogWarning, "cli")
<< "Cannot set ownership for user '" << user << "' group '" << group << "' on path '" << path << "'. Verify it yourself!";
}
}
std::vector<String> NodeUtility::GetNodeCompletionSuggestions(const String& word)
{
std::vector<String> suggestions;
for (const Dictionary::Ptr& node : GetNodes()) {
String node_name = node->Get("endpoint");
if (node_name.Find(word) == 0)
suggestions.push_back(node_name);
}
return suggestions;
}
void NodeUtility::PrintNodes(std::ostream& fp)
{
bool first = true;
for (const Dictionary::Ptr& node : GetNodes()) {
if (first)
first = false;
else
fp << "\n";
fp << "Node '"
<< ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << node->Get("endpoint") << ConsoleColorTag(Console_Normal)
<< "' (";
Dictionary::Ptr settings = node->Get("settings");
if (settings) {
String host = settings->Get("host");
String port = settings->Get("port");
double log_duration = settings->Get("log_duration");
if (!host.IsEmpty() && !port.IsEmpty())
fp << "host: " << host << ", port: " << port << ", ";
fp << "log duration: " << Utility::FormatDuration(log_duration) << ", ";
}
fp << "last seen: " << Utility::FormatDateTime("%c", node->Get("seen")) << ")\n";
PrintNodeRepository(fp, node->Get("repository"));
}
}
void NodeUtility::PrintNodeRepository(std::ostream& fp, const Dictionary::Ptr& repository)
{
if (!repository)
return;
ObjectLock olock(repository);
for (const Dictionary::Pair& kv : repository) {
fp << std::setw(4) << " "
<< "* Host '" << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << kv.first << ConsoleColorTag(Console_Normal) << "'\n";
Array::Ptr services = kv.second;
ObjectLock xlock(services);
for (const String& service : services) {
fp << std::setw(8) << " " << "* Service '" << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << service << ConsoleColorTag(Console_Normal) << "'\n";
}
}
}
void NodeUtility::PrintNodesJson(std::ostream& fp)
{
Dictionary::Ptr result = new Dictionary();
for (const Dictionary::Ptr& node : GetNodes()) {
result->Set(node->Get("endpoint"), node);
}
fp << JsonEncode(result);
}
void NodeUtility::AddNode(const String& name)
{
String path = GetNodeRepositoryFile(name);
if (Utility::PathExists(path) ) {
Log(LogInformation, "cli")
<< "Node '" << name << "' exists already.";
}
Dictionary::Ptr node = new Dictionary();
node->Set("seen", Utility::GetTime());
node->Set("endpoint", name);
node->Set("zone", name);
node->Set("repository", Empty);
CreateRepositoryPath();
Utility::SaveJsonFile(path, 0600, node);
}
void NodeUtility::AddNodeSettings(const String& name, const String& host,
const String& port, double log_duration)
{
Dictionary::Ptr settings = new Dictionary();
settings->Set("host", host);
settings->Set("port", port);
settings->Set("log_duration", log_duration);
CreateRepositoryPath();
Utility::SaveJsonFile(GetNodeSettingsFile(name), 0600, settings);
}
void NodeUtility::RemoveNode(const String& name)
{
String repoPath = GetNodeRepositoryFile(name);
if (!Utility::PathExists(repoPath))
return;
if (unlink(repoPath.CStr()) < 0) {
Log(LogCritical, "cli")
<< "Cannot remove file '" << repoPath
<< "'. Failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) + "\".";
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("unlink")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(repoPath));
}
String settingsPath = GetNodeSettingsFile(name);
if (Utility::PathExists(settingsPath)) {
if (unlink(settingsPath.CStr()) < 0) {
Log(LogCritical, "cli")
<< "Cannot remove file '" << settingsPath
<< "'. Failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) + "\".";
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("unlink")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(settingsPath));
}
}
}
std::vector<Dictionary::Ptr> NodeUtility::GetNodes(void)
{
std::vector<Dictionary::Ptr> nodes;
Utility::Glob(GetRepositoryPath() + "/*.repo",
boost::bind(&NodeUtility::CollectNodes, _1, boost::ref(nodes)), GlobFile);
return nodes;
}
Dictionary::Ptr NodeUtility::LoadNodeFile(const String& node_file)
{
Dictionary::Ptr node = Utility::LoadJsonFile(node_file);
if (!node)
return Dictionary::Ptr();
String settingsFile = GetNodeSettingsFile(node->Get("endpoint"));
if (Utility::PathExists(settingsFile))
node->Set("settings", Utility::LoadJsonFile(settingsFile));
else
node->Remove("settings");
return node;
}
void NodeUtility::CollectNodes(const String& node_file, std::vector<Dictionary::Ptr>& nodes)
{
Dictionary::Ptr node;
try {
node = LoadNodeFile(node_file);
} catch (const std::exception&) {
return;
}
if (!node)
return;
nodes.push_back(node);
}
/*
* Node Setup helpers
*/
@ -420,172 +207,6 @@ bool NodeUtility::WriteNodeConfigObjects(const String& filename, const Array::Pt
return true;
}
/*
* Black/Whitelist helpers
*/
String NodeUtility::GetBlackAndWhiteListPath(const String& type)
{
return NodeUtility::GetRepositoryPath() + "/" + type + ".list";
}
Array::Ptr NodeUtility::GetBlackAndWhiteList(const String& type)
{
String list_path = GetBlackAndWhiteListPath(type);
Array::Ptr lists = new Array();
if (Utility::PathExists(list_path)) {
lists = Utility::LoadJsonFile(list_path);
}
return lists;
}
int NodeUtility::UpdateBlackAndWhiteList(const String& type, const String& zone_filter, const String& host_filter, const String& service_filter)
{
Array::Ptr lists = GetBlackAndWhiteList(type);
{
ObjectLock olock(lists);
for (const Dictionary::Ptr& filter : lists) {
if (filter->Get("zone") == zone_filter) {
if (filter->Get("host") == host_filter && service_filter.IsEmpty()) {
Log(LogWarning, "cli")
<< "Found zone filter '" << zone_filter << "' with host filter '" << host_filter << "'. Bailing out.";
return 1;
} else if (filter->Get("host") == host_filter && filter->Get("service") == service_filter) {
Log(LogWarning, "cli")
<< "Found zone filter '" << zone_filter << "' with host filter '" << host_filter << "' and service filter '"
<< service_filter << "'. Bailing out.";
return 1;
}
}
}
}
Dictionary::Ptr new_filter = new Dictionary();
new_filter->Set("zone", zone_filter);
new_filter->Set("host", host_filter);
new_filter->Set("service", service_filter);
lists->Add(new_filter);
String list_path = GetBlackAndWhiteListPath(type);
CreateRepositoryPath();
Utility::SaveJsonFile(list_path, 0600, lists);
return 0;
}
int NodeUtility::RemoveBlackAndWhiteList(const String& type, const String& zone_filter, const String& host_filter, const String& service_filter)
{
Array::Ptr lists = GetBlackAndWhiteList(type);
std::vector<int> remove_filters;
int remove_idx = 0;
{
ObjectLock olock(lists);
for (const Dictionary::Ptr& filter : lists) {
if (filter->Get("zone") == zone_filter) {
if (filter->Get("host") == host_filter && service_filter.IsEmpty()) {
Log(LogInformation, "cli")
<< "Found zone filter '" << zone_filter << "' with host filter '" << host_filter << "'. Removing from " << type << ".";
remove_filters.push_back(remove_idx);
} else if (filter->Get("host") == host_filter && filter->Get("service") == service_filter) {
Log(LogInformation, "cli")
<< "Found zone filter '" << zone_filter << "' with host filter '" << host_filter << "' and service filter '"
<< service_filter << "'. Removing from " << type << ".";
remove_filters.push_back(remove_idx);
}
}
remove_idx++;
}
}
/* if there are no matches for reomval, throw an error */
if (remove_filters.empty()) {
Log(LogCritical, "cli", "Cannot remove filter!");
return 1;
}
for (int remove : remove_filters) {
lists->Remove(remove);
}
String list_path = GetBlackAndWhiteListPath(type);
CreateRepositoryPath();
Utility::SaveJsonFile(list_path, 0600, lists);
return 0;
}
int NodeUtility::PrintBlackAndWhiteList(std::ostream& fp, const String& type)
{
Array::Ptr lists = GetBlackAndWhiteList(type);
if (lists->GetLength() == 0)
return 0;
fp << "Listing all " << type << " entries:\n";
ObjectLock olock(lists);
for (const Dictionary::Ptr& filter : lists) {
fp << type << " filter for Node: '" << filter->Get("zone") << "' Host: '"
<< filter->Get("host") << "' Service: '" << filter->Get("service") << "'.\n";
}
return 0;
}
bool NodeUtility::CheckAgainstBlackAndWhiteList(const String& type, const String& zone, const String& host, const String& service)
{
Array::Ptr lists = GetBlackAndWhiteList(type);
Log(LogNotice, "cli")
<< "Checking object against " << type << ".";
ObjectLock olock(lists);
for (const Dictionary::Ptr& filter : lists) {
String zone_filter = filter->Get("zone");
String host_filter = filter->Get("host");
String service_filter;
if (filter->Contains("service"))
service_filter = filter->Get("service");
Log(LogNotice, "cli")
<< "Checking Node '" << zone << "' =~ '" << zone_filter << "', host '" << host << "' =~ '" << host_filter
<< "', service '" << service << "' =~ '" << service_filter << "'.";
if (Utility::Match(zone_filter, zone)) {
Log(LogNotice, "cli")
<< "Node '" << zone << "' matches filter '" << zone_filter << "'";
if (Utility::Match(host_filter, host)) {
Log(LogNotice, "cli")
<< "Host '" << host << "' matches filter '" << host_filter << "'";
/* no service filter means host match */
if (service_filter.IsEmpty())
return true;
if (Utility::Match(service_filter, service)) {
Log(LogNotice, "cli")
<< "Host '" << service << "' matches filter '" << service_filter << "'";
return true;
}
}
}
}
return false;
}
/*
* We generally don't overwrite files without backup before

View File

@ -37,21 +37,6 @@ namespace icinga
class I2_CLI_API NodeUtility
{
public:
static String GetRepositoryPath(void);
static String GetNodeRepositoryFile(const String& name);
static String GetNodeSettingsFile(const String& name);
static void CreateRepositoryPath(const String& path = GetRepositoryPath());
static std::vector<String> GetNodeCompletionSuggestions(const String& word);
static void PrintNodes(std::ostream& fp);
static void PrintNodesJson(std::ostream& fp);
static void PrintNodeRepository(std::ostream& fp, const Dictionary::Ptr& repository);
static void AddNode(const String& name);
static void AddNodeSettings(const String& name, const String& host, const String& port, double log_duration);
static void RemoveNode(const String& name);
static std::vector<Dictionary::Ptr> GetNodes(void);
static bool CreateBackupFile(const String& target, bool is_private = false);
static bool WriteNodeConfigObjects(const String& filename, const Array::Ptr& objects);
@ -62,22 +47,8 @@ public:
static int GenerateNodeIcingaConfig(const std::vector<std::string>& endpoints);
static int GenerateNodeMasterIcingaConfig(void);
/* black/whitelist */
static String GetBlackAndWhiteListPath(const String& type);
static Array::Ptr GetBlackAndWhiteList(const String& type);
static int UpdateBlackAndWhiteList(const String& type, const String& node_filter,
const String& host_filter, const String& service_filter);
static int RemoveBlackAndWhiteList(const String& type, const String& node_filter,
const String& host_filter, const String& service_filter);
static int PrintBlackAndWhiteList(std::ostream& fp, const String& type);
static bool CheckAgainstBlackAndWhiteList(const String& type, const String& node, const String& host, const String& service);
private:
NodeUtility(void);
static bool RemoveNodeFile(const String& path);
static Dictionary::Ptr LoadNodeFile(const String& node_file);
static void CollectNodes(const String& node_file, std::vector<Dictionary::Ptr>& nodes);
static void SerializeObject(std::ostream& fp, const Dictionary::Ptr& object);
};

View File

@ -1,69 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/repositoryclearchangescommand.hpp"
#include "cli/repositoryutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/utility.hpp"
#include <fstream>
#include <iostream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("repository/clear-changes", RepositoryClearChangesCommand);
String RepositoryClearChangesCommand::GetDescription(void) const
{
return "Clear uncommitted Icinga 2 repository changes";
}
String RepositoryClearChangesCommand::GetShortDescription(void) const
{
return "clear uncommitted repository changes";
}
ImpersonationLevel RepositoryClearChangesCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
bool RepositoryClearChangesCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "repository clear-changes" CLI command.
*
* @returns An exit status.
*/
int RepositoryClearChangesCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (!Utility::PathExists(RepositoryUtility::GetRepositoryChangeLogPath())) {
std::cout << "Repository Changelog path '" << RepositoryUtility::GetRepositoryChangeLogPath() << "' does not exist. Add objects first!\n";
return 1;
}
std::cout << "Clearing all remaining changes\n";
RepositoryUtility::ClearChangeLog();
return 0;
}

View File

@ -1,50 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef REPOSITORYCLEARCHANGESCOMMAND_H
#define REPOSITORYCLEARCHANGESCOMMAND_H
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "cli/clicommand.hpp"
#include <ostream>
namespace icinga
{
/**
* The "repository clear-changes" command.
*
* @ingroup cli
*/
class RepositoryClearChangesCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(RepositoryClearChangesCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* REPOSITORYCLEARCHANGESCOMMAND_H */

View File

@ -1,85 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/repositorycommitcommand.hpp"
#include "cli/repositoryutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/utility.hpp"
#include <fstream>
#include <iostream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("repository/commit", RepositoryCommitCommand);
String RepositoryCommitCommand::GetDescription(void) const
{
return "Commit Icinga 2 repository changes";
}
String RepositoryCommitCommand::GetShortDescription(void) const
{
return "commit repository changes";
}
void RepositoryCommitCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("simulate", "Simulate to-be-committed changes");
}
ImpersonationLevel RepositoryCommitCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
bool RepositoryCommitCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "repository commit" CLI command.
*
* @returns An exit status.
*/
int RepositoryCommitCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (!Utility::PathExists(RepositoryUtility::GetRepositoryChangeLogPath())) {
std::cout << "Repository Changelog path '" << RepositoryUtility::GetRepositoryChangeLogPath() << "' does not exist. Add objects first!\n";
return 1;
}
if (vm.count("simulate")) {
RepositoryUtility::PrintChangeLog(std::cout);
std::cout << "\n";
std::cout << "Simulation not yet implemented.\n";
//TODO
return 1;
} else {
RepositoryUtility::PrintChangeLog(std::cout);
std::cout << "\n";
RepositoryUtility::CommitChangeLog();
}
return 0;
}

View File

@ -1,52 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef REPOSITORYCOMMITCOMMAND_H
#define REPOSITORYCOMMITCOMMAND_H
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "cli/clicommand.hpp"
#include <ostream>
namespace icinga
{
/**
* The "repository commit" command.
*
* @ingroup cli
*/
class RepositoryCommitCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(RepositoryCommitCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* REPOSITORYCOMMITCOMMAND_H */

View File

@ -1,204 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/repositoryobjectcommand.hpp"
#include "cli/repositoryutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/utility.hpp"
#include <fstream>
#include <iostream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_REPOSITORY_CLICOMMAND(Host);
REGISTER_REPOSITORY_CLICOMMAND(Service);
REGISTER_REPOSITORY_CLICOMMAND(Zone);
REGISTER_REPOSITORY_CLICOMMAND(Endpoint);
RepositoryObjectCommand::RepositoryObjectCommand(const String& type, RepositoryCommandType command)
: m_Type(type), m_Command(command)
{ }
String RepositoryObjectCommand::GetDescription(void) const
{
String description;
switch (m_Command) {
case RepositoryCommandAdd:
description = "Adds a new";
break;
case RepositoryCommandRemove:
description = "Removes a";
break;
case RepositoryCommandList:
description = "Lists all";
break;
case RepositoryCommandSet:
description = "Set attributes for a";
break;
default:
break;
}
description += " " + m_Type + " object";
if (m_Command == RepositoryCommandList)
description += "s";
return description;
}
String RepositoryObjectCommand::GetShortDescription(void) const
{
String description;
switch (m_Command) {
case RepositoryCommandAdd:
description = "adds a new";
break;
case RepositoryCommandRemove:
description = "removes a";
break;
case RepositoryCommandList:
description = "lists all";
break;
case RepositoryCommandSet:
description = "set attributes for a";
break;
default:
break;
}
description += " " + m_Type + " object";
if (m_Command == RepositoryCommandList)
description += "s";
return description;
}
bool RepositoryObjectCommand::IsDeprecated(void) const
{
return true;
}
void RepositoryObjectCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
if (m_Command == RepositoryCommandAdd) {
visibleDesc.add_options()
("import", po::value<std::vector<std::string> >(), "Import the defined template into the object. Must be defined and included separately in Icinga 2");
}
}
std::vector<String> RepositoryObjectCommand::GetPositionalSuggestions(const String& word) const
{
if (m_Command == RepositoryCommandAdd) {
Type::Ptr ptype = Type::GetByName(m_Type);
ASSERT(ptype);
return GetFieldCompletionSuggestions(ptype, word);
} else if (m_Command == RepositoryCommandRemove) {
std::vector<String> suggestions;
String argName = "name=";
if (argName.Find(word) == 0)
suggestions.push_back(argName);
if (m_Type == "Service") {
String argHostName = "host_name=";
if (argHostName.Find(word) == 0)
suggestions.push_back(argHostName);
}
return suggestions;
} else
return CLICommand::GetPositionalSuggestions(word);
}
ImpersonationLevel RepositoryObjectCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
int RepositoryObjectCommand::GetMaxArguments(void) const
{
return -1;
}
/**
* The entry point for the "repository <type> <add/remove/list>" CLI command.
*
* @returns An exit status.
*/
int RepositoryObjectCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
Dictionary::Ptr attrs = RepositoryUtility::GetArgumentAttributes(ap);
/* shortcut for list command */
if (m_Command == RepositoryCommandList) {
RepositoryUtility::PrintObjects(std::cout, m_Type);
return 0;
}
if (!attrs->Contains("name")) {
Log(LogCritical, "cli", "Object requires a name (Hint: 'name=<objectname>')!");
return 1;
}
String name = attrs->Get("name");
if (vm.count("import")) {
Array::Ptr imports = new Array();
for (const String& import : vm["import"].as<std::vector<std::string> >()) {
imports->Add(import);
}
//Update object attributes
if (imports->GetLength() > 0)
attrs->Set("import", imports);
}
if (m_Command == RepositoryCommandAdd) {
std::vector<String> object_paths = RepositoryUtility::GetObjects();
Array::Ptr changes = new Array();
RepositoryUtility::GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changes));
RepositoryUtility::AddObject(object_paths, name, m_Type, attrs, changes);
} else if (m_Command == RepositoryCommandRemove) {
Array::Ptr changes = new Array();
RepositoryUtility::GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changes));
/* pass attrs for service->host_name requirement */
RepositoryUtility::RemoveObject(name, m_Type, attrs, changes);
} else if (m_Command == RepositoryCommandSet) {
Log(LogWarning, "cli")
<< "Not supported yet. Please check the roadmap at https://github.com/Icinga/icinga2\n";
return 1;
} else {
Log(LogCritical, "cli")
<< "Invalid command '" << m_Command << "'specified.\n";
return 1;
}
return 0;
}

View File

@ -1,84 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef REPOSITORYOBJECTCOMMAND_H
#define REPOSITORYOBJECTCOMMAND_H
#include "cli/clicommand.hpp"
#include <boost/algorithm/string/case_conv.hpp>
namespace icinga
{
enum RepositoryCommandType
{
RepositoryCommandAdd,
RepositoryCommandRemove,
RepositoryCommandList,
RepositoryCommandSet
};
/**
* The "repository <type> <add/remove/list>" command.
*
* @ingroup cli
*/
class I2_CLI_API RepositoryObjectCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(RepositoryObjectCommand);
RepositoryObjectCommand(const String& type, RepositoryCommandType command);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual int GetMaxArguments(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
private:
String m_Type;
RepositoryCommandType m_Command;
};
#define REGISTER_REPOSITORY_CLICOMMAND(type) \
INITIALIZE_ONCE([]() { \
String ltype = #type; \
boost::algorithm::to_lower(ltype); \
\
std::vector<String> name; \
name.push_back("repository"); \
name.push_back(ltype); \
name.push_back("add"); \
CLICommand::Register(name, new RepositoryObjectCommand(#type, RepositoryCommandAdd)); \
\
name[2] = "remove"; \
CLICommand::Register(name, new RepositoryObjectCommand(#type, RepositoryCommandRemove)); \
\
name[2] = "list"; \
CLICommand::Register(name, new RepositoryObjectCommand(#type, RepositoryCommandList)); \
})
}
#endif /* REPOSITORYOBJECTCOMMAND_H */

View File

@ -1,716 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/repositoryutility.hpp"
#include "cli/clicommand.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/convert.hpp"
#include "base/configwriter.hpp"
#include "base/scriptglobal.hpp"
#include "base/json.hpp"
#include "base/netstring.hpp"
#include "base/tlsutility.hpp"
#include "base/stdiostream.hpp"
#include "base/debug.hpp"
#include "base/objectlock.hpp"
#include "base/console.hpp"
#include "base/serializer.hpp"
#include "base/exception.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/regex.hpp>
#include <fstream>
#include <iostream>
using namespace icinga;
Dictionary::Ptr RepositoryUtility::GetArgumentAttributes(const std::vector<std::string>& arguments)
{
Dictionary::Ptr attrs = new Dictionary();
for (const String& kv : arguments) {
std::vector<String> tokens;
boost::algorithm::split(tokens, kv, boost::is_any_of("="));
if (tokens.size() != 2) {
Log(LogWarning, "cli")
<< "Cannot parse passed attributes: " << boost::algorithm::join(tokens, "=");
continue;
}
Value value;
try {
value = Convert::ToDouble(tokens[1]);
} catch (...) {
value = tokens[1];
}
attrs->Set(tokens[0], value);
}
return attrs;
}
String RepositoryUtility::GetRepositoryConfigPath(void)
{
return Application::GetSysconfDir() + "/icinga2/repository.d";
}
String RepositoryUtility::GetRepositoryObjectConfigPath(const String& type, const Dictionary::Ptr& object)
{
String path = GetRepositoryConfigPath() + "/";
if (type == "Host")
path += "hosts";
else if (type == "Service")
path += "hosts/" + EscapeName(object->Get("host_name"));
else if (type == "Zone")
path += "zones";
else if (type == "Endpoint")
path += "endpoints";
return path;
}
bool RepositoryUtility::FilterRepositoryObjects(const String& type, const String& path)
{
if (type == "Host") {
boost::regex expr("hosts/[^/]*.conf", boost::regex::icase);
boost::smatch what;
return boost::regex_search(path.GetData(), what, expr);
}
else if (type == "Service")
return Utility::Match("*hosts/*/*.conf", path);
else if (type == "Zone")
return Utility::Match("*zones/*.conf", path);
else if (type == "Endpoints")
return Utility::Match("*endpoints/*.conf", path);
return false;
}
String RepositoryUtility::GetRepositoryObjectConfigFilePath(const String& type, const Dictionary::Ptr& object)
{
String path = GetRepositoryObjectConfigPath(type, object);
path += "/" + EscapeName(object->Get("name")) + ".conf";
return path;
}
String RepositoryUtility::GetRepositoryChangeLogPath(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/repository/changes";
}
void RepositoryUtility::CreateRepositoryPath(const String& path)
{
if (!Utility::PathExists(path))
Utility::MkDirP(path, 0750);
String user = ScriptGlobal::Get("RunAsUser");
String group = ScriptGlobal::Get("RunAsGroup");
if (!Utility::SetFileOwnership(path, user, group)) {
Log(LogWarning, "cli")
<< "Cannot set ownership for user '" << user << "' group '" << group << "' on path '" << path << "'. Verify it yourself!";
}
}
/* printers */
void RepositoryUtility::PrintObjects(std::ostream& fp, const String& type)
{
std::vector<String> objects = GetObjects(); //full path
for (const String& object : objects) {
if (!FilterRepositoryObjects(type, object)) {
Log(LogDebug, "cli")
<< "Ignoring object '" << object << "'. Type '" << type << "' does not match.";
continue;
}
String file = Utility::BaseName(object);
boost::algorithm::replace_all(file, ".conf", "");
file = UnescapeName(file);
fp << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << type << ConsoleColorTag(Console_Normal)
<< " '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << file << ConsoleColorTag(Console_Normal) << "'";
String prefix = Utility::DirName(object);
if (type == "Service") {
std::vector<String> tokens;
boost::algorithm::split(tokens, prefix, boost::is_any_of("/"));
String host_name = UnescapeName(tokens[tokens.size()-1]);
fp << " (on " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << "Host" << ConsoleColorTag(Console_Normal)
<< " '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << host_name << ConsoleColorTag(Console_Normal) << "')";
}
fp << "\n";
}
}
void RepositoryUtility::PrintChangeLog(std::ostream& fp)
{
Array::Ptr changelog = new Array();
GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changelog));
ObjectLock olock(changelog);
std::cout << "Changes to be committed:\n\n";
for (const Value& entry : changelog) {
FormatChangelogEntry(std::cout, entry);
}
}
class RepositoryValidationUtils : public ValidationUtils
{
public:
virtual bool ValidateName(const String& type, const String& name) const
{
return true;
}
};
/* modify objects and write changelog */
bool RepositoryUtility::AddObject(const std::vector<String>& object_paths, const String& name, const String& type,
const Dictionary::Ptr& attrs, const Array::Ptr& changes, bool check_config)
{
String pattern;
if (type == "Service")
pattern = EscapeName(attrs->Get("host_name")) + "/" + EscapeName(name) + ".conf";
else
pattern = EscapeName(name) + ".conf";
for (const String& object_path : object_paths) {
if (object_path.Contains(pattern)) {
Log(LogWarning, "cli")
<< type << " '" << name << "' already exists. Skipping creation.";
return false;
}
}
/* add a new changelog entry by timestamp */
String path = GetRepositoryChangeLogPath() + "/" + Convert::ToString(Utility::GetTime()) + "-" + type + "-" + SHA256(name) + ".change";
Dictionary::Ptr change = new Dictionary();
change->Set("timestamp", Utility::GetTime());
change->Set("name", name);
change->Set("type", type);
change->Set("command", "add");
change->Set("attrs", attrs);
Type::Ptr utype = Type::GetByName(type);
ASSERT(utype);
if (check_config) {
try {
ConfigObject::Ptr object = static_pointer_cast<ConfigObject>(utype->Instantiate(std::vector<Value>()));
/* temporarly set the object type for validation */
attrs->Set("type", utype->GetName());
Deserialize(object, attrs, false, FAConfig);
object->SetName(name);
RepositoryValidationUtils utils;
static_pointer_cast<ConfigObject>(object)->Validate(FAConfig, utils);
attrs->Remove("type");
} catch (const ValidationError& ex) {
Log(LogCritical, "config", DiagnosticInformation(ex));
return false;
}
}
if (CheckChangeExists(change, changes)) {
Log(LogWarning, "cli")
<< "Change '" << change->Get("command") << "' for type '"
<< change->Get("type") << "' and name '" << change->Get("name")
<< "' already exists.";
return false;
}
/* store the cached change */
changes->Add(change);
return WriteObjectToRepositoryChangeLog(path, change);
}
bool RepositoryUtility::RemoveObject(const String& name, const String& type, const Dictionary::Ptr& attrs, const Array::Ptr& changes)
{
/* add a new changelog entry by timestamp */
String path = GetRepositoryChangeLogPath() + "/" + Convert::ToString(Utility::GetTime()) + "-" + type + "-" + SHA256(name) + ".change";
Dictionary::Ptr change = new Dictionary();
change->Set("timestamp", Utility::GetTime());
change->Set("name", name);
change->Set("type", type);
change->Set("command", "remove");
change->Set("attrs", attrs); //required for service->host_name
if (CheckChangeExists(change, changes)) {
Log(LogWarning, "cli")
<< "Change '" << change->Get("command") << "' for type '"
<< change->Get("type") << "' and name '" << change->Get("name")
<< "' already exists.";
return false;
}
/* store the cached change */
changes->Add(change);
return WriteObjectToRepositoryChangeLog(path, change);
}
bool RepositoryUtility::SetObjectAttribute(const String& name, const String& type, const String& attr, const Value& val)
{
//TODO: Implement modification commands
return true;
}
bool RepositoryUtility::CheckChangeExists(const Dictionary::Ptr& change, const Array::Ptr& changes)
{
Dictionary::Ptr attrs = change->Get("attrs");
ObjectLock olock(changes);
for (const Dictionary::Ptr& entry : changes) {
if (entry->Get("type") != change->Get("type"))
continue;
if (entry->Get("name") != change->Get("name"))
continue;
Dictionary::Ptr their_attrs = entry->Get("attrs");
if (entry->Get("type") == "Service" && attrs->Get("host_name") != their_attrs->Get("host_name"))
continue;
if (entry->Get("command") != change->Get("command"))
continue;
/* only works for add/remove commands (no set) */
if (change->Get("command") == "add" || change->Get("command") == "remove")
return true;
}
return false;
}
bool RepositoryUtility::ClearChangeLog(void)
{
GetChangeLog(boost::bind(RepositoryUtility::ClearChange, _1, _2));
return true;
}
bool RepositoryUtility::ChangeLogHasPendingChanges(void)
{
Array::Ptr changelog = new Array();
GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changelog));
return changelog->GetLength() > 0;
}
/* commit changelog */
bool RepositoryUtility::CommitChangeLog(void)
{
GetChangeLog(boost::bind(RepositoryUtility::CommitChange, _1, _2));
return true;
}
/* write/read from changelog repository */
bool RepositoryUtility::WriteObjectToRepositoryChangeLog(const String& path, const Dictionary::Ptr& item)
{
Log(LogInformation, "cli", "Dumping changelog items to file '" + path + "'");
CreateRepositoryPath(Utility::DirName(path));
std::fstream fp;
String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", 0600, fp);
fp << JsonEncode(item);
fp.close();
#ifdef _WIN32
_unlink(path.CStr());
#endif /* _WIN32 */
if (rename(tempFilename.CStr(), path.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("rename")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(tempFilename));
}
return true;
}
Dictionary::Ptr RepositoryUtility::GetObjectFromRepositoryChangeLog(const String& filename)
{
std::fstream fp;
fp.open(filename.CStr(), std::ifstream::in);
if (!fp)
return Dictionary::Ptr();
String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
fp.close();
return JsonDecode(content);
}
/* internal implementation when changes are committed */
bool RepositoryUtility::AddObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs)
{
String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf";
return WriteObjectToRepository(path, name, type, attrs);
}
bool RepositoryUtility::RemoveObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs)
{
String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf";
if (!Utility::PathExists(path)) {
Log(LogWarning, "cli")
<< type << " '" << name << "' does not exist.";
return true;
}
bool success = RemoveObjectFileInternal(path);
if (success)
Log(LogInformation, "cli")
<< "Removing config object '" << name << "' in file '" << path << "'";
/* special treatment for hosts -> remove the services too */
if (type == "Host") {
path = GetRepositoryObjectConfigPath(type, attrs) + "/" + name;
/* if path does not exist, this host does not have any services */
if (!Utility::PathExists(path)) {
Log(LogNotice, "cli")
<< type << " '" << name << "' does not have any services configured.";
return success;
}
std::vector<String> files;
Utility::GlobRecursive(path, "*.conf",
boost::bind(&RepositoryUtility::CollectObjects, _1, boost::ref(files)), GlobFile);
for (const String& file : files) {
RemoveObjectFileInternal(file);
}
#ifndef _WIN32
rmdir(path.CStr());
#else
_rmdir(path.CStr());
#endif /* _WIN32 */
}
return success;
}
bool RepositoryUtility::RemoveObjectFileInternal(const String& path)
{
if (!Utility::PathExists(path) ) {
Log(LogCritical, "cli", "Cannot remove '" + path + "'. Does not exist.");
return false;
}
if (unlink(path.CStr()) < 0) {
Log(LogCritical, "cli", "Cannot remove path '" + path +
"'. Failed with error code " + Convert::ToString(errno) + ", \"" + Utility::FormatErrorNumber(errno) + "\".");
return false;
}
return true;
}
bool RepositoryUtility::SetObjectAttributeInternal(const String& name, const String& type, const String& key, const Value& val, const Dictionary::Ptr& attrs)
{
//TODO
String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf";
Dictionary::Ptr obj = GetObjectFromRepository(path); //TODO
if (!obj) {
Log(LogCritical, "cli")
<< "Can't get object " << name << " from repository.\n";
return false;
}
obj->Set(key, val);
std::cout << "Writing object '" << name << "' to path '" << path << "'.\n";
//TODO: Create a patch file
if (!WriteObjectToRepository(path, name, type, obj)) {
Log(LogCritical, "cli")
<< "Can't write object " << name << " to repository.\n";
return false;
}
return true;
}
bool RepositoryUtility::WriteObjectToRepository(const String& path, const String& name, const String& type, const Dictionary::Ptr& item)
{
Log(LogInformation, "cli")
<< "Writing config object '" << name << "' to file '" << path << "'";
CreateRepositoryPath(Utility::DirName(path));
std::fstream fp;
String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", 0644, fp);
SerializeObject(fp, name, type, item);
fp << std::endl;
fp.close();
#ifdef _WIN32
_unlink(path.CStr());
#endif /* _WIN32 */
if (rename(tempFilename.CStr(), path.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("rename")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(tempFilename));
}
return true;
}
Dictionary::Ptr RepositoryUtility::GetObjectFromRepository(const String& filename)
{
//TODO: Parse existing configuration objects
return Dictionary::Ptr();
}
String RepositoryUtility::EscapeName(const String& name)
{
return Utility::EscapeString(name, "<>:\"/\\|?*", true);
}
String RepositoryUtility::UnescapeName(const String& name)
{
return Utility::UnescapeString(name);
}
/*
* collect functions
*/
std::vector<String> RepositoryUtility::GetObjects(void)
{
std::vector<String> objects;
String path = GetRepositoryConfigPath();
Utility::GlobRecursive(path, "*.conf",
boost::bind(&RepositoryUtility::CollectObjects, _1, boost::ref(objects)), GlobFile);
return objects;
}
void RepositoryUtility::CollectObjects(const String& object_file, std::vector<String>& objects)
{
Log(LogDebug, "cli")
<< "Adding object: '" << object_file << "'.";
objects.push_back(object_file);
}
bool RepositoryUtility::GetChangeLog(const boost::function<void (const Dictionary::Ptr&, const String&)>& callback)
{
std::vector<String> changelog;
String path = GetRepositoryChangeLogPath() + "/";
Utility::MkDirP(path, 0700);
Utility::Glob(path + "/*.change",
boost::bind(&RepositoryUtility::CollectChangeLog, _1, boost::ref(changelog)), GlobFile);
/* sort by timestamp ascending */
std::sort(changelog.begin(), changelog.end());
for (const String& entry : changelog) {
String file = path + entry + ".change";
Dictionary::Ptr change = GetObjectFromRepositoryChangeLog(file);
Log(LogDebug, "cli")
<< "Collecting entry " << entry << "\n";
if (change)
callback(change, file);
}
return true;
}
void RepositoryUtility::CollectChangeLog(const String& change_file, std::vector<String>& changelog)
{
String file = Utility::BaseName(change_file);
boost::algorithm::replace_all(file, ".change", "");
Log(LogDebug, "cli")
<< "Adding change file: '" << file << "'.";
changelog.push_back(file);
}
void RepositoryUtility::CollectChange(const Dictionary::Ptr& change, Array::Ptr& changes)
{
changes->Add(change);
}
/*
* Commit Changelog entry
*/
void RepositoryUtility::CommitChange(const Dictionary::Ptr& change, const String& path)
{
Log(LogDebug, "cli")
<< "Got change " << change->Get("name");
String name = change->Get("name");
String type = change->Get("type");
String command = change->Get("command");
Dictionary::Ptr attrs;
if (change->Contains("attrs"))
attrs = change->Get("attrs");
bool success = false;
if (command == "add")
success = AddObjectInternal(name, type, attrs);
else if (command == "remove")
success = RemoveObjectInternal(name, type, attrs);
if (success) {
Log(LogNotice, "cli")
<< "Removing changelog file '" << path << "'.";
RemoveObjectFileInternal(path);
}
}
/*
* Clear Changelog entry
*/
void RepositoryUtility::ClearChange(const Dictionary::Ptr& change, const String& path)
{
Log(LogDebug, "cli")
<< "Clearing change " << change->Get("name");
Log(LogInformation, "cli")
<< "Removing changelog file '" << path << "'.";
RemoveObjectFileInternal(path);
}
/*
* Print Changelog helpers
*/
void RepositoryUtility::FormatChangelogEntry(std::ostream& fp, const Dictionary::Ptr& change)
{
if (!change)
return;
if (change->Get("command") == "add")
fp << "Adding";
if (change->Get("command") == "remove")
fp << "Removing";
String type = change->Get("type");
boost::algorithm::to_lower(type);
Dictionary::Ptr attrs = change->Get("attrs");
fp << " " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << type << ConsoleColorTag(Console_Normal) << " '";
fp << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << change->Get("name") << ConsoleColorTag(Console_Normal) << "'\n";
ObjectLock olock(attrs);
for (const Dictionary::Pair& kv : attrs) {
/* skip the name */
if (kv.first == "name" || kv.first == "__name")
continue;
fp << std::setw(4) << " " << ConsoleColorTag(Console_ForegroundGreen) << kv.first << ConsoleColorTag(Console_Normal) << " = ";
ConfigWriter::EmitValue(fp, 0, kv.second);
fp << "\n";
}
}
/*
* print helpers for configuration
* TODO: Move into a separate class
*/
void RepositoryUtility::SerializeObject(std::ostream& fp, const String& name, const String& type, const Dictionary::Ptr& object)
{
fp << "object " << type << " ";
ConfigWriter::EmitString(fp, name);
fp << " {\n";
if (!object) {
fp << "}\n";
return;
}
if (object->Contains("import")) {
Array::Ptr imports = object->Get("import");
ObjectLock olock(imports);
for (const String& import : imports) {
fp << "\t" << "import ";
ConfigWriter::EmitString(fp, import);
fp << '\n';
}
}
ObjectLock xlock(object);
for (const Dictionary::Pair& kv : object) {
if (kv.first == "import" || kv.first == "name" || kv.first == "__name") {
continue;
} else {
fp << "\t";
ConfigWriter::EmitIdentifier(fp, kv.first, true);
fp << " = ";
ConfigWriter::EmitValue(fp, 1, kv.second);
}
fp << "\n";
}
fp << "}\n";
}

View File

@ -1,108 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef REPOSITORYUTILITY_H
#define REPOSITORYUTILITY_H
#include "base/i2-base.hpp"
#include "cli/i2-cli.hpp"
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "base/value.hpp"
#include "base/string.hpp"
#include <boost/function.hpp>
namespace icinga
{
/**
* @ingroup cli
*/
class I2_CLI_API RepositoryUtility
{
public:
static Dictionary::Ptr GetArgumentAttributes(const std::vector<std::string>& arguments);
static String GetRepositoryConfigPath(void);
static String GetRepositoryObjectConfigPath(const String& type, const Dictionary::Ptr& object);
static String GetRepositoryObjectConfigFilePath(const String& type, const Dictionary::Ptr& object);
static String GetRepositoryChangeLogPath(void);
static void CreateRepositoryPath(const String& path);
static bool FilterRepositoryObjects(const String& type, const String& path);
static void PrintObjects(std::ostream& fp, const String& type);
static void PrintChangeLog(std::ostream& fp);
static bool AddObject(const std::vector<String>& object_paths, const String& name, const String& type, const Dictionary::Ptr& attrs,
const Array::Ptr& changes, bool check_config = true);
static bool RemoveObject(const String& name, const String& type, const Dictionary::Ptr& attrs, const Array::Ptr& changes);
static bool CheckChangeExists(const Dictionary::Ptr& change, const Array::Ptr& changes);
static bool SetObjectAttribute(const String& name, const String& type, const String& attr, const Value& val);
static bool CommitChangeLog(void);
static bool ClearChangeLog(void);
static bool ChangeLogHasPendingChanges(void);
static bool GetChangeLog(const boost::function<void (const Dictionary::Ptr&, const String&)>& callback);
static void CollectChangeLog(const String& change_file, std::vector<String>& changelog);
static void CollectChange(const Dictionary::Ptr& change, Array::Ptr& changes);
static std::vector<String> GetObjects(void);
static void CollectObjects(const String& object_file, std::vector<String>& objects);
private:
RepositoryUtility(void);
static bool RemoveObjectFileInternal(const String& path);
static bool AddObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs);
static bool RemoveObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs);
static bool SetObjectAttributeInternal(const String& name, const String& type, const String& key,
const Value& val, const Dictionary::Ptr& attrs);
/* repository.d */
static bool WriteObjectToRepository(const String& path, const String& name, const String& type, const Dictionary::Ptr& item);
static Dictionary::Ptr GetObjectFromRepository(const String& filename);
/* changelog */
static bool WriteObjectToRepositoryChangeLog(const String& path, const Dictionary::Ptr& item);
static Dictionary::Ptr GetObjectFromRepositoryChangeLog(const String& filename);
static void CommitChange(const Dictionary::Ptr& change, const String& path);
static void ClearChange(const Dictionary::Ptr& change, const String& path);
static void FormatChangelogEntry(std::ostream& fp, const Dictionary::Ptr& change);
/* config print helpers */
static void SerializeObject(std::ostream& fp, const String& name, const String& type, const Dictionary::Ptr& object);
static void FormatValue(std::ostream& fp, const Value& val);
static void FormatArray(std::ostream& fp, const Array::Ptr& arr);
static String EscapeName(const String& name);
static String UnescapeName(const String& name);
};
}
#endif /* REPOSITORYUTILITY_H */

View File

@ -46,14 +46,11 @@ REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckCha
REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler);
REGISTER_APIFUNCTION(SetAcknowledgement, event, &ClusterEvents::AcknowledgementSetAPIHandler);
REGISTER_APIFUNCTION(ClearAcknowledgement, event, &ClusterEvents::AcknowledgementClearedAPIHandler);
REGISTER_APIFUNCTION(UpdateRepository, event, &ClusterEvents::UpdateRepositoryAPIHandler);
REGISTER_APIFUNCTION(ExecuteCommand, event, &ClusterEvents::ExecuteCommandAPIHandler);
REGISTER_APIFUNCTION(SendNotifications, event, &ClusterEvents::SendNotificationsAPIHandler);
REGISTER_APIFUNCTION(NotificationSentUser, event, &ClusterEvents::NotificationSentUserAPIHandler);
REGISTER_APIFUNCTION(NotificationSentToAllUsers, event, &ClusterEvents::NotificationSentToAllUsersAPIHandler);
static Timer::Ptr l_RepositoryTimer;
void ClusterEvents::StaticInitialize(void)
{
Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler);
@ -67,12 +64,6 @@ void ClusterEvents::StaticInitialize(void)
Checkable::OnAcknowledgementSet.connect(&ClusterEvents::AcknowledgementSetHandler);
Checkable::OnAcknowledgementCleared.connect(&ClusterEvents::AcknowledgementClearedHandler);
l_RepositoryTimer = new Timer();
l_RepositoryTimer->SetInterval(30);
l_RepositoryTimer->OnTimerExpired.connect(boost::bind(&ClusterEvents::RepositoryTimerHandler));
l_RepositoryTimer->Start();
l_RepositoryTimer->Reschedule(0);
}
Dictionary::Ptr ClusterEvents::MakeCheckResultMessage(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
@ -697,101 +688,6 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin,
return Empty;
}
void ClusterEvents::RepositoryTimerHandler(void)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Dictionary::Ptr repository = new Dictionary();
for (const Host::Ptr& host : ConfigType::GetObjectsByType<Host>()) {
Array::Ptr services = new Array();
for (const Service::Ptr& service : host->GetServices()) {
services->Add(service->GetShortName());
}
repository->Set(host->GetName(), services);
}
Endpoint::Ptr my_endpoint = Endpoint::GetLocalEndpoint();
if (!my_endpoint) {
Log(LogWarning, "ClusterEvents", "No local endpoint defined. Bailing out.");
return;
}
Zone::Ptr my_zone = my_endpoint->GetZone();
if (!my_zone)
return;
Dictionary::Ptr params = new Dictionary();
params->Set("seen", Utility::GetTime());
params->Set("endpoint", my_endpoint->GetName());
params->Set("zone", my_zone->GetName());
params->Set("repository", repository);
Dictionary::Ptr message = new Dictionary();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::UpdateRepository");
message->Set("params", params);
listener->RelayMessage(MessageOrigin::Ptr(), my_zone, message, false);
}
String ClusterEvents::GetRepositoryDir(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/api/repository/";
}
Value ClusterEvents::UpdateRepositoryAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Value vrepository = params->Get("repository");
if (vrepository.IsEmpty() || !vrepository.IsObjectType<Dictionary>())
return Empty;
Utility::MkDirP(GetRepositoryDir(), 0755);
String repositoryFile = GetRepositoryDir() + SHA256(params->Get("endpoint")) + ".repo";
std::fstream fp;
String tempRepositoryFile = Utility::CreateTempFile(repositoryFile + ".XXXXXX", 0644, fp);
fp << JsonEncode(params);
fp.close();
#ifdef _WIN32
_unlink(repositoryFile.CStr());
#endif /* _WIN32 */
if (rename(tempRepositoryFile.CStr(), repositoryFile.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("rename")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(tempRepositoryFile));
}
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return Empty;
Dictionary::Ptr message = new Dictionary();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::UpdateRepository");
message->Set("params", params);
listener->RelayMessage(origin, Zone::GetLocalZone(), message, true);
return Empty;
}
void ClusterEvents::SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type,
const CheckResult::Ptr& cr, const String& author, const String& text, const MessageOrigin::Ptr& origin)
{

View File

@ -61,10 +61,6 @@ public:
static Value ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static String GetRepositoryDir(void);
static void RepositoryTimerHandler(void);
static Value UpdateRepositoryAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static Dictionary::Ptr MakeCheckResultMessage(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr);
static void SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type,

View File

@ -66,7 +66,6 @@ endif()
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/zones\")")