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