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).

View File

@ -11,31 +11,23 @@ make sure to always run them with the `--help` parameter.
Run `icinga2` without any arguments to get a list of all available global
options.
```
# icinga2
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 <command> [<arguments>]
Supported commands:
* api setup (setup for api)
* ca list (lists all certificate signing requests)
* ca sign (signs an outstanding certificate request)
* console (Icinga console)
* daemon (starts Icinga 2)
* feature disable (disables specified feature)
* feature enable (enables specified feature)
* feature list (lists all enabled features)
* node add (add node) (DEPRECATED)
* node blacklist add (adds a new blacklist filter) (DEPRECATED)
* node blacklist list (lists all blacklist filters) (DEPRECATED)
* node blacklist remove (removes a blacklist filter) (DEPRECATED)
* node list (lists all nodes) (DEPRECATED)
* node remove (removes node) (DEPRECATED)
* node set (set node attributes) (DEPRECATED)
* feature list (lists all available features)
* node setup (set up node)
* node update-config (update node config) (DEPRECATED)
* node whitelist add (adds a new whitelist filter) (DEPRECATED)
* node whitelist list (lists all whitelist filters) (DEPRECATED)
* node whitelist remove (removes a whitelist filter) (DEPRECATED)
* node wizard (wizard for node setup)
* object list (lists all objects)
* pki new-ca (sets up a new CA)
@ -44,20 +36,6 @@ options.
* pki save-cert (saves another Icinga 2 instance's certificate)
* pki sign-csr (signs a CSR)
* pki ticket (generates a ticket)
* repository clear-changes (clear uncommitted repository changes) (DEPRECATED)
* repository commit (commit repository changes) (DEPRECATED)
* repository endpoint add (adds a new Endpoint object) (DEPRECATED)
* repository endpoint list (lists all Endpoint objects) (DEPRECATED)
* repository endpoint remove (removes a Endpoint object) (DEPRECATED)
* repository host add (adds a new Host object) (DEPRECATED)
* repository host list (lists all Host objects) (DEPRECATED)
* repository host remove (removes a Host object) (DEPRECATED)
* repository service add (adds a new Service object) (DEPRECATED)
* repository service list (lists all Service objects) (DEPRECATED)
* repository service remove (removes a Service object) (DEPRECATED)
* repository zone add (adds a new Zone object) (DEPRECATED)
* repository zone list (lists all Zone objects) (DEPRECATED)
* repository zone remove (removes a Zone object) (DEPRECATED)
* troubleshoot (collect information for troubleshooting)
* variable get (gets a variable)
* variable list (lists all variables)
@ -78,6 +56,7 @@ options.
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## Icinga 2 CLI Bash Autocompletion <a id="cli-commands-autocompletion"></a>
@ -94,21 +73,29 @@ You need to install the `bash-completion` package if not already installed.
RHEL/CentOS/Fedora:
```
# yum install bash-completion
```
SUSE:
```
# zypper install bash-completion
```
Debian/Ubuntu:
```
# apt-get install bash-completion
```
Ensure that the `bash-completion.d` directory is added to your shell
environment. You can manually source the icinga2 bash-completion file
into your current session and test it:
```
# source /etc/bash-completion.d/icinga2
```
## Icinga 2 CLI Global Options <a id="cli-commands-global-options"></a>
@ -135,7 +122,9 @@ When including files you can specify that the include search path should be
checked. You can do this by putting your configuration file name in angle
brackets like this:
```
include <test.conf>
```
This causes Icinga 2 to search its include path for the configuration file
`test.conf`. By default the installation path for the [Icinga Template Library](10-icinga-template-library.md#icinga-template-library)
@ -144,14 +133,82 @@ is the only search directory.
Using the `--include` command-line option additional search directories can be
added.
## CLI command: Api <a id="cli-command-api"></a>
Provides the setup CLI command to enable the REST API. More details
in the [Icinga 2 API](#icinga2-api-setup) chapter.
```
# icinga2 api --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 <command> [<arguments>]
Supported commands:
* api setup (setup for api)
Global options:
-h [ --help ] show this help message
-V [ --version ] show version information
--color use VT100 color codes even when stdout is not a
terminal
-D [ --define ] arg define a constant
-a [ --app ] arg application library name (default: icinga)
-l [ --library ] arg load a library
-I [ --include ] arg add include search directory
-x [ --log-level ] arg specify the log level for the console log.
The valid value is either debug, notice,
information (default), warning, or critical
-X [ --script-debugger ] whether to enable the script debugger
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## CLI command: Ca <a id="cli-command-ca"></a>
List and manage incoming certificate signing requests. More details
can be found in the [signing methods](#distributed-monitoring-setup-sign-certificates-master)
chapter. This CLI command is available since v2.8.
```
# icinga2 ca --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 <command> [<arguments>]
Supported commands:
* ca list (lists all certificate signing requests)
* ca sign (signs an outstanding certificate request)
Global options:
-h [ --help ] show this help message
-V [ --version ] show version information
--color use VT100 color codes even when stdout is not a
terminal
-D [ --define ] arg define a constant
-a [ --app ] arg application library name (default: icinga)
-l [ --library ] arg load a library
-I [ --include ] arg add include search directory
-x [ --log-level ] arg specify the log level for the console log.
The valid value is either debug, notice,
information (default), warning, or critical
-X [ --script-debugger ] whether to enable the script debugger
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## CLI command: Console <a id="cli-command-console"></a>
The CLI command `console` can be used to debug and evaluate Icinga 2 config expressions,
e.g. to test [functions](17-language-reference.md#functions) in your local sandbox.
```
$ icinga2 console
Icinga 2 (version: v2.6.0)
Icinga 2 (version: v2.8.0)
<1> => function test(name) {
<1> .. log("Hello " + name)
<1> .. }
@ -160,14 +217,52 @@ e.g. to test [functions](17-language-reference.md#functions) in your local sandb
information/config: Hello World
null
<3> =>
```
Further usage examples can be found in the [library reference](18-library-reference.md#library-reference) chapter.
```
# icinga2 console --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 console [<arguments>]
Interprets Icinga script expressions.
Global options:
-h [ --help ] show this help message
-V [ --version ] show version information
--color use VT100 color codes even when stdout is not a
terminal
-D [ --define ] arg define a constant
-a [ --app ] arg application library name (default: icinga)
-l [ --library ] arg load a library
-I [ --include ] arg add include search directory
-x [ --log-level ] arg specify the log level for the console log.
The valid value is either debug, notice,
information (default), warning, or critical
-X [ --script-debugger ] whether to enable the script debugger
Command options:
-c [ --connect ] arg connect to an Icinga 2 instance
-e [ --eval ] arg evaluate expression and terminate
-r [ --file ] arg evaluate a file and terminate
--syntax-only only validate syntax (requires --eval or --file)
--sandbox enable sandbox mode
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
On operating systems without the `libedit` library installed there is no
support for line-editing or a command history. However you can
use the `rlwrap` program if you require those features:
```
$ rlwrap icinga2 console
```
The debug console can be used to connect to a running Icinga 2 instance using
the [REST API](12-icinga2-api.md#icinga2-api). [API permissions](12-icinga2-api.md#icinga2-api-permissions)
@ -194,18 +289,21 @@ user credentials the debug console supports two environment variables:
Here's an example:
```
$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/'
Icinga 2 (version: v2.6.0)
Icinga 2 (version: v2.8.0)
<1> =>
```
Once connected you can inspect variables and execute other expressions by entering them at the prompt:
<1> => var h = get_host("example.localdomain")
```
<1> => var h = get_host("icinga2-client1.localdomain")
null
<2> => h.last_check_result
{
active = true
check_source = "example.localdomain"
check_source = "icinga2-client1.localdomain"
command = [ "/usr/local/sbin/check_ping", "-H", "127.0.0.1", "-c", "5000,100%", "-w", "3000,80%" ]
execution_end = 1446653527.174983
execution_start = 1446653523.152673
@ -230,7 +328,7 @@ Once connected you can inspect variables and execute other expressions by enteri
}
}
<3> =>
```
You can use the `--eval` parameter to evaluate a single expression in batch mode.
Using the `--file` option you can specify a file which should be evaluated.
@ -240,9 +338,10 @@ The `--syntax-only` option can be used in combination with `--eval` or `--file`
to check a script for syntax errors. In this mode the script is parsed to identify
syntax errors but not evaluated.
Here's an example that retrieves the command that was used by Icinga to check the `example.localdomain` host:
Here's an example that retrieves the command that was used by Icinga to check the `icinga2-client1.localdomain` host:
$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' --eval 'get_host("example.localdomain").last_check_result.command' | python -m json.tool
```
$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' --eval 'get_host("icinga2-client1.localdomain").last_check_result.command' | python -m json.tool
[
"/usr/local/sbin/check_ping",
"-H",
@ -252,14 +351,16 @@ Here's an example that retrieves the command that was used by Icinga to check th
"-w",
"3000,80%"
]
```
## CLI command: Daemon <a id="cli-command-daemon"></a>
The CLI command `daemon` provides the functionality to start/stop Icinga 2.
Furthermore it allows to run the [configuration validation](11-cli-commands.md#config-validation).
```
# icinga2 daemon --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 daemon [<arguments>]
@ -290,7 +391,7 @@ Furthermore it allows to run the [configuration validation](11-cli-commands.md#c
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
### Config Files
@ -311,53 +412,40 @@ is returned. More details in the [configuration validation](11-cli-commands.md#c
The `feature enable` and `feature disable` commands can be used to enable and disable features:
```
# icinga2 feature disable <tab>
--app --define --include --log-level --version checker graphite mainlog
--color --help --library --script-debugger api command ido-mysql notification
```
```
# icinga2 feature enable <tab>
--app --define --include --log-level --version debuglog ido-pgsql livestatus perfdata syslog
--color --help --library --script-debugger compatlog gelf influxdb opentsdb statusdata
```
The `feature list` command shows which features are currently enabled:
```
# icinga2 feature list
Disabled features: compatlog debuglog gelf ido-pgsql influxdb livestatus opentsdb perfdata statusdata syslog
Enabled features: api checker command graphite ido-mysql mainlog notification
```
## CLI command: Node <a id="cli-command-node"></a>
> **Warning**
>
> This CLI command and its sub commands except for `setup` and `wizard`
> have been deprecated in v2.6 and will be
> removed in future releases.
>
> Make sure to follow the release announcements on the [Icinga website](https://www.icinga.com).
Provides the functionality to install and manage master and client
Provides the functionality to setup master and client
nodes in a [distributed monitoring](06-distributed-monitoring.md#distributed-monitoring) scenario.
```
# icinga2 node --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 <command> [<arguments>]
Supported commands:
* node add (add node) (DEPRECATED)
* node blacklist add (adds a new blacklist filter) (DEPRECATED)
* node blacklist list (lists all blacklist filters) (DEPRECATED)
* node blacklist remove (removes a blacklist filter) (DEPRECATED)
* node list (lists all nodes) (DEPRECATED)
* node remove (removes node) (DEPRECATED)
* node set (set node attributes) (DEPRECATED)
* node setup (set up node)
* node update-config (update node config) (DEPRECATED)
* node whitelist add (adds a new whitelist filter) (DEPRECATED)
* node whitelist list (lists all whitelist filters) (DEPRECATED)
* node whitelist remove (removes a whitelist filter) (DEPRECATED)
* node wizard (wizard for node setup)
Global options:
@ -376,8 +464,7 @@ nodes in a [distributed monitoring](06-distributed-monitoring.md#distributed-mon
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## CLI command: Object <a id="cli-command-object"></a>
@ -393,8 +480,9 @@ You need to restart Icinga 2 in order to update the `icinga2.debug` cache file.
More information can be found in the [troubleshooting](15-troubleshooting.md#troubleshooting-list-configuration-objects) section.
```
# icinga2 object --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.7.1-196-g23e8a6253; debug)
Usage:
icinga2 <command> [<arguments>]
@ -418,6 +506,7 @@ More information can be found in the [troubleshooting](15-troubleshooting.md#tro
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## CLI command: Pki <a id="cli-command-pki"></a>
@ -433,8 +522,9 @@ Provides the CLI commands to
This functionality is used by the [node setup/wizard](11-cli-commands.md#cli-command-node) CLI commands.
You will need them in the [distributed monitoring chapter](06-distributed-monitoring.md#distributed-monitoring).
```
# icinga2 pki --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 <command> [<arguments>]
@ -463,18 +553,7 @@ You will need them in the [distributed monitoring chapter](06-distributed-monito
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
## CLI command: Repository <a id="cli-command-repository"></a>
> **Warning**
>
> This CLI command has been deprecated in v2.6 and will be
> removed in future releases.
>
> Make sure to follow the release announcements on the [Icinga website](https://www.icinga.com).
This command is experimental and not finished as public CLI command. Parts of its functionality
are used in the [node update-config](11-cli-commands.md#cli-command-node) cli command.
```
## CLI command: Troubleshoot <a id="cli-command-troubleshoot"></a>
@ -487,8 +566,9 @@ Keep in mind that this tool can not collect information from other icinga2 nodes
each of one of you instances.
This is only a tool to collect information to help others help you, it will not attempt to fix anything.
```
# icinga2 troubleshoot --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0)
Usage:
icinga2 troubleshoot [<arguments>]
@ -517,13 +597,15 @@ This is only a tool to collect information to help others help you, it will not
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## CLI command: Variable <a id="cli-command-variable"></a>
Lists all configured variables (constants) in a similar fashion like [object list](11-cli-commands.md#cli-command-object).
```
# icinga2 variable --help
icinga2 - The Icinga 2 network monitoring daemon (version: v2.6.0)
icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0; debug)
Usage:
icinga2 <command> [<arguments>]
@ -548,6 +630,7 @@ Lists all configured variables (constants) in a similar fashion like [object lis
Report bugs at <https://github.com/Icinga/icinga2>
Icinga home page: <https://www.icinga.com/>
```
## Enabling/Disabling Features <a id="enable-features"></a>
@ -562,22 +645,26 @@ in the example configuration file.
You can view a list of enabled and disabled features:
```
# icinga2 feature list
Disabled features: api command compatlog debuglog graphite icingastatus ido-mysql ido-pgsql livestatus notification perfdata statusdata syslog
Enabled features: checker mainlog notification
```
Using the `icinga2 feature enable` command you can enable features:
```
# icinga2 feature enable graphite
Enabling feature graphite. Make sure to restart Icinga 2 for these changes to take effect.
```
You can disable features using the `icinga2 feature disable` command:
```
# icinga2 feature disable ido-mysql livestatus
Disabling feature ido-mysql. Make sure to restart Icinga 2 for these changes to take effect.
Disabling feature livestatus. Make sure to restart Icinga 2 for these changes to take effect.
```
The `icinga2 feature enable` and `icinga2 feature disable` commands do not
restart Icinga 2. You will need to restart Icinga 2 using the init script
@ -593,32 +680,31 @@ a hint on the file, the line number and the affected configuration line itself.
The following example creates an apply rule without any `assign` condition.
apply Service "5872-ping4" {
```
apply Service "my-ping4" {
import "generic-service"
check_command = "ping4"
//assign where match("5872-*", host.name)
//assign where host.address
}
```
Validate the configuration with the init script option `checkconfig`:
# /etc/init.d/icinga2 checkconfig
**Note**: Using [systemd](02-getting-started.md#systemd-service) you need to manually validate the configuration using
the CLI command below.
Validate the configuration:
```
# icinga2 daemon -C
[2014-05-22 17:07:25 +0200] critical/ConfigItem: Location:
/etc/icinga2/conf.d/tests/5872.conf(5): }
/etc/icinga2/conf.d/tests/5872.conf(6):
/etc/icinga2/conf.d/tests/5872.conf(7): apply Service "5872-ping4" {
/etc/icinga2/conf.d/tests/my.conf(5): }
/etc/icinga2/conf.d/tests/my.conf(6):
/etc/icinga2/conf.d/tests/my.conf(7): apply Service "my-ping4" {
^^^^^^^^^^^^^
/etc/icinga2/conf.d/tests/5872.conf(8): import "test-generic-service"
/etc/icinga2/conf.d/tests/5872.conf(9): check_command = "ping4"
/etc/icinga2/conf.d/tests/my.conf(8): import "test-generic-service"
/etc/icinga2/conf.d/tests/my.conf(9): check_command = "ping4"
Config error: 'apply' is missing 'assign'
[2014-05-22 17:07:25 +0200] critical/ConfigItem: 1 errors, 0 warnings.
Icinga 2 detected configuration errors.
```
If you encounter errors during configuration validation, please make sure
to read the [troubleshooting](15-troubleshooting.md#troubleshooting) chapter.
@ -628,33 +714,6 @@ after validation passes to analyze object attributes, inheritance or created
objects by apply rules.
Find more on troubleshooting with `object list` in [this chapter](15-troubleshooting.md#troubleshooting-list-configuration-objects).
Example filtered by `Service` objects with the name `ping*`:
# icinga2 object list --type Service --name *ping*
Object 'icinga.com!ping4' of type 'Service':
* __name = 'icinga.com!ping4'
* check_command = 'ping4'
% = modified in '/etc/icinga2/conf.d/services.conf', lines 17:3-17:25
* check_interval = 60
% = modified in '/etc/icinga2/conf.d/templates.conf', lines 28:3-28:21
* host_name = 'icinga.com'
% = modified in '/etc/icinga2/conf.d/services.conf', lines 14:1-14:21
* max_check_attempts = 3
% = modified in '/etc/icinga2/conf.d/templates.conf', lines 27:3-27:24
* name = 'ping4'
% = modified in '/etc/icinga2/conf.d/services.conf', lines 14:1-14:21
* retry_interval = 30
% = modified in '/etc/icinga2/conf.d/templates.conf', lines 29:3-29:22
* templates = [ 'ping4', 'generic-service' ]
% += modified in '/etc/icinga2/conf.d/services.conf', lines 14:1-14:21
% += modified in '/etc/icinga2/conf.d/templates.conf', lines 26:1-30:1
* type = 'Service'
* vars
% += modified in '/etc/icinga2/conf.d/services.conf', lines 18:3-18:19
* sla = '24x7'
% = modified in '/etc/icinga2/conf.d/services.conf', lines 18:3-18:19
## Reload on Configuration Changes <a id="config-change-reload"></a>
@ -662,7 +721,9 @@ Every time you have changed your configuration you should first tell Icinga 2
to [validate](11-cli-commands.md#config-validation). If there are no validation errors, you can
safely reload the Icinga 2 daemon.
```
# systemctl reload icinga2
```
The `reload` action will send the `SIGHUP` signal to the Icinga 2 daemon
which will validate the configuration in a separate process and not stop

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\")")