Compare commits

..

3 Commits

Author SHA1 Message Date
vv01f 012cb9639a limit matching url forwarding to future.html 2022-04-26 12:19:09 +02:00
vv01f 56261a7bd7 vorschlag von astro, kA ob das geht 2022-04-25 21:31:40 +02:00
vv01f e5d2d03f44 idee: nicht existierende jahre auf datei umbiegen 2022-04-25 10:33:21 +02:00
445 changed files with 21541 additions and 60618 deletions

View File

@ -1,10 +0,0 @@
# This file contains a list of commits that are not likely what you
# are looking for in a blame, such as mass reformatting or renaming.
# You can set this file as a default ignore file for blame by running
# the following command.
#
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# format commits
aaddec81945750222721659be65ecd6bf2503c6a
b4d2a7f95952f8ca9ca13f9ff629f689a284c6fb

2
.gitattributes vendored
View File

@ -1,2 +0,0 @@
# see https://github.com/getsops/sops/blob/main/README.rst#47showing-diffs-in-cleartext-in-git how to use this
*.yaml diff=sops

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
*.retry
result
result-*
/hosts/mediawiki/MediaWikiExtensionsComposer/

View File

@ -1,436 +0,0 @@
keys:
# The PGP keys in keys/
- &admins
- DD0998E6CDF294537FC604F991FA5E5BF9AA901C # 0xA
- A5EE826D645DBE35F9B0993358512AE87A69900F # astro
- 8F79E6CD6434700615867480D11A514F5095BFA8 # dennis
- 4F9F44A64CC2E438979329E1F122F05437696FCE # poelzi
- 91EBE87016391323642A6803B966009D57E69CC6 # revol-xut
- 53B26AEDC08246715E15504B236B6291555E8401 # sandro
- 4B12EFA69166CA8C23FC47E49CD3A46248B660CA # vv01f
- A4B0F5A80C2E2448A97BEC25BB829C4DECA6CCB9 # winzlieb
- &users
- A5EE826D645DBE35F9B0993358512AE87A69900F # astro
- 8F79E6CD6434700615867480D11A514F5095BFA8 # dennis
- 53B26AEDC08246715E15504B236B6291555E8401 # sandro
- 9580391316684474BFBD41EC3E8C55248C19AF2A # xyrill
- &polygon-snowflake age12aukzah0pt2rck52hwn08kezyxueqz2f49ld7hpyuzmu847vavdqkunn5c # polygon
# Generate AGE keys from SSH keys with:
# nix-shell -p ssh-to-age --run 'ssh some.serv.zentralwerk.org cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'
- &activity-relay age1a8k72egc2vg4jn445wwcr0a68y9xu5ft68s2xwehugs5sjawpv4q5nnrmy
- &auth age1y7lxpxskqclwqluft2ct2c3u8weehus6t8evwk7cdnpakxzgcquspn827x
- &blogs age1lccjvj9z8de4hfrdeumm9eu7awef4d9jygv3w7zdash3fhv6e53quy53wz
- &broker age1dj0d0339f4law7qvuzcv2fs6sf8why63s3l8tja0f8vsj7wefcds9drvte
- &buzzrelay age1j2euh5qt4a7cvx0t93uj4n9t8y8tkv9h3nefszc6g2q7t7gvngxswhrve0
- &c3d2-web age18h6vmfduhmj28wxdgur8wugn7scm5vwvwkj5sr4f7nl0czr2zvaqscsdsv
- &caveman age13dl5qjzddaazmquf7zfecru5tr4ld8l8xd7xpmhaqqzmchpua4usswqykd
- &dacbert age1g2ewsxcu5uqlesaznp2qwlcz8w66pxh4qxkul8wu7x8g2hw83saqxynpyk
- &dn42 age1726t33dl7pv3xrxxlafj2sexh7c0jm8pza84yu6l3wpz3fw5dauqxlass3
- &drone age1w6u8zjfya63q9rjfll98eegnfdsvyaspnwn802t2mxh47gt8p30q0kn898
- &freifunk age17rrjtdgzzwgjatyqqv27pftx42t8xhksls46jc3f78juzw4g04vsd7lr7e
- &ftp age1lkr5rkf3z0976g8snmznf755gnexhjkwpzsw8xxwyesqmneawa4qgsqx77
- &gitea age12n5k6c4rxp4mjnexw9uw83yp34sallt44kldupfmxr2xkppj8a8sdsmv8h
- &glotzbert age1zqpep2vgfqeyvtj2jpxczfgrpjffwda429rnuztfp0vpqsrqdq8s8f4yua
- &gnunet age1kk0thtx6mg5cs0gqm4ylc4r8w6klq660s3j04w7m8w0w084yrpcqh3tqwf
- &grafana age1yahhqn2620300n20k68az5lr2u42wdgtjwysgqyr99a4cj52ay0qjw02pl
- &hedgedoc age1jt5pj0c0fvmzg7quaucq4n2rzcx9ajzstp8ruwc8ewjpay5vqfqsdjaal8
- &home-assistant age1l2tld2cttpkj4vpuh9hm4xjwq94rmf8vukjgvdzcvwwtze6k6s6qjf0s5r
- &hydra age1px8sjpcmnz27ayczzu883n0p5ad34vnzj6rl9y2eyye546v0m3dqfqx459
- &jabber age1tnq862ekxepjkes6efr282uj9gtcsqru04s5k0l2enq5djxyt5as0k0c2a
- &knot age1hfzpctkk5tz0ddc86ul9t0nf8c37jtngawepvgxk5rxlvv938vusx4kuc6
- &mail age15t7hj27j6ccs8u7mfz8su3aa74g4dxp4crkgc3c0rs28hct7q4ssgk8zcm
- &mastodon age1dcpd6u4psq3hehjyjrt3s7kzmnvxd20vsc8urjcdv6anr5v7ky2sq9rhtt
- &matemat age15vmz2evhnkn26fyt4vqvgztfrsr2s8qavd2m6zfjmkh84q2g75csnc5kr6
- &matrix age1s2ww76ll6nclz74gny27tk42xfsepl23z2k0849a8jv8xpnmpe3shgunxr
- &mediawiki age1xjvep7hsnfefgxvuwall8nq0486qu8yknhzwhf0cskw5xlpm8qws9txc56
- &mobilizon age182ms3ygypflk7mtpemp4k4ks9rz4gwhvzc9jlk95u4py5q68ppxstzu2e3
- &mucbot age1qen44cx5sx0y299zl93cz3tflx8agt8y9vtm0d4uxw42t9gyecdsw9jade
- &nfsroot age18yxgwpakrkzq8ca2enayf79py25se3d8dsed2q523869re30jcaqx6rjln
- &nncp age15853dr2kd6r2329tkcanwnruh6zd2xvsu5twc7gnxeyu3h7t6q5scckaq8
- &oparl age14aq8fscrwkgmu5yv86vj7p7kmxclzs6dp7fpvdhvrnmce83ztphqc4mr9q
- &owncast age1cp9gsuyfu52exk0hr3fvj404v5njhahakzwlugwtneyrs4vgdyaq0sg92f
- &pretalx age1u6xeayzwfdj9l0mg3f4xvjd8e9nemz5psqavauvacjgp2nku95yqc4f29s
- &prometheus age13xhxqulvswuckmpkmy2fgeqd5jx0ar8e2hst33leljt69r6hsvnsrdw63k
- &public-access-proxy age1xcj6peyaf5xvj2673vl9j0z7supwtw7hzuk782zk7gt69k2ykytqe65mg5
- &pulsebert age12hdk2stter0cjexxwx3sqn9wx3vmptkxszvx7knq9zgm9uqzjs7suvkcqu
- &radiobert age1lga6hjmxa95fmtdn3frlmy64ej3hyswxrcuz25qvw0kfsxkqeugs8gjw8q
- &riscbert age148d87gqw59lmst5jv3vynhsu3tv4t4sj49s4lktvnplfcrjq2y5sjcwsu8
- &scrape age1p60rg45qrzpv2hcfzxl8d8k9afkk7dtrhr98cngeyuhlega83ynssmtx5k
- &sdrweb age1makkpv2t74lxmw0nk6m89nespva7j700pmt83pl5a4ldtj2k8fzqakw8h7
- &server10 age15qj8latetnrmgzd7krq02y65kn7lhq2pcwv8cvzej2783u5a9scqs79nmf
- &server8 age12jcu0jtw7m96evxnd0vu6lvsm8uswslrdhxd2u655vjrwhljmqdsptry37
- &server9 age15vrlmtckjf4j242juw7l5e0s6eunn67ejr9acaztnl3tmvwpufrsevntva
- &spaceapi age125k9uyqw5ae5jqkfsak4d6c6rcx9q63ywuusk62pmxdnhwzqxgqq2jsau7
- &storage-ng age1qjvds58pedjdk9rj0yqfvad4xhpteapr9chvfucwcgwrsr8n7axqyhg2vu
- &stream age14h2npkt6m40ewkkaee7zx49redew5rjsjpm70qhka8cwkekmspqqpspy4g
- &ticker age1kdrpaqsy7gdnf80fpq6qrrc98nqjuzzlqx955uk2pkky3xcxky8sw9cdjl
- &vaultwarden age1xs22728ltpl3yh8hzvwt4g3gk8uc32lg8cqh86fp5d8c2jlvp3gshmejun
creation_rules:
- path_regex: modules/backup\.yaml$
key_groups:
- pgp: *admins
age:
- *activity-relay
- *auth
- *blogs
- *buzzrelay
- *caveman
- *drone
- *gitea
- *grafana
- *hedgedoc
- *home-assistant
- *hydra
- *jabber
- *mail
- *mastodon
- *matemat
- *matrix
- *mediawiki
- *mobilizon
- *owncast
- *pretalx
- *sdrweb
- *ticker
- *vaultwarden
- *polygon-snowflake
- path_regex: modules/cluster/[^/]+\.yaml$
key_groups:
- pgp: *admins
age:
- *hydra
- *server8
- *server9
- *server10
- *polygon-snowflake
- path_regex: config/[^/]+\.yaml$
key_groups:
- pgp: *admins
age:
- *polygon-snowflake
- *auth
- *blogs
- *broker
- *buzzrelay
- *c3d2-web
- *dacbert
- *dn42
- *freifunk
- *ftp
- *gitea
- *glotzbert
- *gnunet
- *grafana
- *hedgedoc
- *hydra
- *jabber
- *knot
- *mail
- *mastodon
- *matemat
- *matrix
- *mediawiki
- *mucbot
- *nfsroot
- *oparl
- *pretalx
- *prometheus
- *public-access-proxy
- *pulsebert
- *radiobert
- *riscbert
- *scrape
- *sdrweb
- *server8
- *server9
- *server10
- *spaceapi
- *storage-ng
- *stream
- *ticker
- *vaultwarden
- path_regex: hosts/activity-relay/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *activity-relay
- *polygon-snowflake
- path_regex: hosts/auth/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *auth
- *polygon-snowflake
- path_regex: hosts/knot/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *knot
- *polygon-snowflake
- path_regex: hosts/blogs/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *blogs
- *polygon-snowflake
- path_regex: hosts/broker/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *broker
- *polygon-snowflake
- path_regex: hosts/buzzrelay/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *buzzrelay
- *polygon-snowflake
- path_regex: hosts/c3d2-web/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *c3d2-web
- *polygon-snowflake
- path_regex: hosts/caveman/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *caveman
- *polygon-snowflake
- path_regex: hosts/dacbert/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *dacbert
- *polygon-snowflake
- path_regex: hosts/dn42/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *dn42
- *polygon-snowflake
- path_regex: hosts/drone/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *drone
- *polygon-snowflake
- path_regex: hosts/freifunk/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *freifunk
- *polygon-snowflake
- path_regex: hosts/gitea/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *gitea
- *polygon-snowflake
- path_regex: hosts/glotzbert/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *glotzbert
- *polygon-snowflake
- path_regex: hosts/grafana/secrets+\.yaml$
key_groups:
- pgp: *admins
age:
- *grafana
- *polygon-snowflake
- path_regex: hosts/hedgedoc/secrets+\.yaml$
key_groups:
- pgp: *admins
age:
- *hedgedoc
- *polygon-snowflake
- path_regex: hosts/home-assistant/secrets+\.yaml$
key_groups:
- pgp: *admins
age:
- *home-assistant
- *polygon-snowflake
- path_regex: hosts/hydra/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *hydra
- *polygon-snowflake
- path_regex: hosts/jabber/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *jabber
- *polygon-snowflake
- path_regex: hosts/mail/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *mail
- *polygon-snowflake
- path_regex: hosts/mastodon/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *mastodon
- *polygon-snowflake
- path_regex: hosts/matemat/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *matemat
- *polygon-snowflake
- path_regex: hosts/matrix/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *matrix
- *polygon-snowflake
- path_regex: hosts/mediawiki/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *mediawiki
- *polygon-snowflake
- path_regex: hosts/mobilizon/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *mobilizon
- *polygon-snowflake
- path_regex: hosts/mucbot/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *mucbot
- *polygon-snowflake
- path_regex: hosts/oparl/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *oparl
- *polygon-snowflake
- path_regex: hosts/owncast/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *owncast
- *polygon-snowflake
- path_regex: hosts/pretalx/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *pretalx
- *polygon-snowflake
- path_regex: hosts/sdrweb/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *sdrweb
- *polygon-snowflake
- path_regex: hosts/radiobert/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *radiobert
- *polygon-snowflake
- path_regex: hosts/scrape/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *scrape
- *polygon-snowflake
- path_regex: hosts/server8/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *server8
- *polygon-snowflake
- path_regex: hosts/server9/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *server9
- *polygon-snowflake
- path_regex: hosts/server10/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *server10
- *polygon-snowflake
- path_regex: hosts/storage-ng/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *storage-ng
- *polygon-snowflake
- path_regex: hosts/ticker/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *ticker
- *polygon-snowflake
- path_regex: hosts/prometheus/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *prometheus
- *polygon-snowflake
- path_regex: hosts/stream/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *stream
- *polygon-snowflake
- path_regex: hosts/vaultwarden/secrets\.yaml$
key_groups:
- pgp: *admins
age:
- *vaultwarden
- *polygon-snowflake

357
README.md
View File

@ -5,76 +5,108 @@ include_toc: yes
lang: en
---
# C3D2 infrastructure based on NixOS
# Setup
## Setup
## Add this repo to your local Nix registry
### Enable nix flakes user wide
As an alternative to a local checkout, always pull the latest code
from this repo.
Add the setting to the user nix.conf. Only do this once!
```bash
nix registry add c3d2 git+https://gitea.c3d2.de/C3D2/nix-config
```
This enables `nix` commands to find this Flake given the `c3d2#`
prefix in some arguments.
## Working with this repo
If you checked out this git repository for working on the code,
replace `c3d2#` with `.#` and run commands from the repository root.
Don't forget to `git add` new files! Flakes require that.
## The secrets repo
Make sure you have access.
## Install Nix Flakes
> Nix Flakes ist gegenwärtig bei Nix (Version 20.09) noch keine standardmäßige Funktionalität für Nix. Die Bereitstellung der Kommandos für Nix Flakes müssen als experimentelle Funktionalität für das Kommando ''nix'' festgelegt werden, um sie verfügbar zu machen.
A Nix environment with Nix Flakes support is required.
### Temporary Shell with Nix Flakes
Set up an environment (with the common command [nix-shell](https://nixos.org/manual/nix/unstable/command-ref/nix-shell.html)) in which the [package *nixFlakes*](https://search.nixos.org/packages?query=nixflakes) (for Nix Flakes) is available and jump into it
```bash
nix-shell -p nixFlakes
```
Set some configuration (do this only once):
```bash
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
```
### Enable nix flakes system wide (preferred for NixOS)
add this to your NixOS configuration:
### Permanent System with Nix Flakes
set this to your NixOS configuration:
```nix
nix.settings.experimental-features = [ "nix-command" "flakes" ];
{ pkgs, ... }: {
nix = {
package = pkgs.nixFlakes;
extraOptions = "experimental-features = nix-command flakes";
};
}
```
### nixpkgs/nixos
# Deployment
The nixpkgs/nixos input used lives at <https://github.com/supersandro2000/nixpkgs/tree/nixos-23.05>.
We are using a fork managed by sandro to make backports, cherry-picks and custom fixes dead easy.
If you want to have an additional backport, cherry-pick or other change, please contact sandro.
## Deploy a NixOS system from this Flake locally
### nixos-modules repo
Running `nixos-rebuild --flake c3d2 switch` on a machine should be sufficient
to update that machine to the current configuration and Nixpkgs revision.
The nixos-modules repo lives at <https://github.com/supersandro2000/nixos-modules> and is mirrored to <https://gitea.c3d2.de/c3d2/nixos-modules>.
Auto generated documentation about all options is available at <https://supersandro2000.github.io/nixos-modules/>.
It contains options sandro shares between his private nixos configs and the C3D2 one.
It sets many options by default and when searching for a particular setting you should always grep this repo, too.
In question ask sandro and consider improving the documentation about this with comments and readme explanations.
Something should be changed/added/removed/etc? Please create a PR or start a conversations with your ideas.
## Deploy to a remote NixOS system with this Flake
### secrets repo
For every host that has a `nixosConfiguration` in our Flake, there are
two scripts that can be run for deployment via ssh.
The secrets repo is absolutely deprecated!
Everything new must be done through sops and everything old should be migrated.
If you don't have secrets access ask sandro or astro to get onboarded.
- `nix run c3d2#glotzbert-nixos-rebuild switch`
### SSH access
If people should get root access to *all* machines, their keys should be added to ``ssh-public-keys.nix``.
## Deployment
### Deploy to a remote NixOS system
For every host that has a `nixosConfiguration` in our Flake, there are two scripts that can be run for deployment via ssh.
- `nix run .#HOSTNAME-nixos-rebuild switch`
Copies the current state to build on the target system.
This may fail due to resource limits on eg. Raspberry Pis.
- `nix run .#HOSTNAME-nixos-rebuild-local switch`
Builds everything locally, then uses `nix copy` to transfer the new NixOS system to the target.
Copies the current state to build on the target system. This may
fail due to eg. container resource limits.
To use the cache from hydra set the following nix options similar to enabling flakes:
The target must already be a nixFlakes system.
```
trusted-public-keys = nix-cache.hq.c3d2.de:KZRGGnwOYzys6pxgM8jlur36RmkJQ/y8y62e52fj1ps=
trusted-substituters = https://nix-cache.hq.c3d2.de
```
- `nix run c3d2#glotzbert-nixos-rebuild-local switch`
This can also be set with the `c3d2.addBinaryCache` option from the [c3d2-user-module](https://gitea.c3d2.de/c3d2/nix-user-module).
Builds locally, then uses `nix copy` to transfer the new NixOS
system to the target.
**Help!** It's needlessly rebuilding stuff that already runs on the
target? If so, use `nix copy` to transfer where
`/run/current-system` points to to your build machine.
### Checking for updates
Use `nix flake show c3d2` to show what is available.
## Remote deployment from non-NixOS
A shell script that copies the current working tree, and runs
`nixos-rebuild switch` on the target:
```shell
./deploy-flake.sh hydra.hq.c3d2.de
```
It cannot not lookup hostnames in `host-registry.nix`. To avoid
deploying the wrong container on the unrelated DNS records, the script
always uses the hostname that is already configured on the target
system.
## Checking for updates
```shell
nix run .#list-upgradable
@ -84,7 +116,7 @@ nix run .#list-upgradable
Checks all hosts with a `nixosConfiguration` in `flake.nix`.
### Update from [Hydra build](https://hydra.hq.c3d2.de/jobset/c3d2/nix-config#tabs-jobs)
## Update from [Hydra build](https://hydra.hq.c3d2.de/jobset/c3d2/nix-config#tabs-jobs)
The fastest way to update a system, a manual alternative to setting
`c3d2.autoUpdate = true;`
@ -95,153 +127,14 @@ Just run:
update-from-hydra
```
### Deploy a MicroVM
## Creating a new Proxmox container
#### Build a microvm remotely and deploy
Use the `nixprox.sh` script that should be copied to
`/usr/local/sbin/nixprox.sh` on all of the Proxmox servers.
```shell
nix run .#microvm-update-HOSTNAME
```
# Secrets management
#### Build microvm locally and deploy
```shell
nix run .#microvm-update-HOSTNAME-local
```
#### Update MicroVM from our Hydra
Our Hydra runs `nix flake update` daily in the `updater.timer`,
pushing it to the `flake-update` branch so that it can build fresh
systems. This branch is setup as the source flake in all the MicroVMs,
so the following is all that is needed on a MicroVM-hosting server:
```shell
microvm -Ru $hostname
```
## Cluster deployment with Skyflake
### About
[Skyflake](https://github.com/astro/skyflake) provides Hyperconverged
Infrastructure to run NixOS MicroVMs on a cluster. Our setup unifies
networking with one bridge per VLAN. Persistent storage is replicated
with Cephfs.
Recognize nixosConfiguration for our Skyflake deployment by the
`self.nixosModules.cluster-options` module being included.
### User interface
We use the less-privileged `c3d2@` user for deployment. This flake's
name on the cluster is `config`. Other flakes can coexist in the same
user so that we can run separately developed projects like
*dump-dvb*. *leon* and potentially other users can deploy Flakes and
MicroVMs without name clashes.
#### Deploying
**git push** this repo to any machine in the cluster, preferably to
Hydra because there building won't disturb any services.
You don't deploy all MicroVMs at once. Instead, Skyflake allows you to
select NixOS systems by the branches you push to. **You must commit
before you push!**
**Example:** deploy nixosConfigurations `mucbot` and `sdrweb` (`HEAD` is your
current commit)
```bash
git push c3d2@hydra.serv.zentralwerk.org:config HEAD:mucbot HEAD:sdrweb
```
This will:
1. Build the configuration on Hydra, refusing the branch update on
broken builds (through a git hook)
2. Copy the MicroVM package and its dependencies to the binary cache
that is accessible to all nodes with Cephfs
3. Submit one job per MicroVM into the Nomad cluster
*Deleting* a nixosConfiguration's branch will **stop** the MicroVM in Nomad.
#### Updating
**TODO:** how would you like it?
#### MicroVM status
```bash
ssh c3d2@hydra.serv.zentralwerk.org status
```
### Debugging for cluster admins
#### Nomad
##### Check the cluster state
```shell
nomad server members
```
Nomad *servers* **coordinate** the cluster.
Nomad *clients* **run** the tasks.
##### Browse in the terminal
[wander](https://github.com/robinovitch61/wander) and
[damon](https://github.com/hashicorp/damon) are nice TUIs that are
preinstalled on our cluster nodes.
##### Browse with a browser
First, tunnel TCP port `:4646` from a cluster server:
```bash
ssh -L 4646:localhost:4646 root@server10.cluster.zentralwerk.org
```
Then, visit https://localhost:4646 for for full klickibunti.
##### Reset the Nomad state on a node
After upgrades, Nomad servers may fail rejoining the cluster. Do this
to make a *Nomad server* behave like a newborn:
```shell
systemctl stop nomad
rm -rf /var/lib/nomad/server/raft/
systemctl start nomad
```
## Secrets management
### Secrets Management Using `sops-nix`
#### Adding a new host
Edit `.sops.yaml`:
1. Add an AGE key for this host. Comments in this file tell you how to do it.
2. Add a `creation_rules` section for `host/$host/*.yaml` files
#### Editing a hosts secrets
Edit `.sops.yaml` to add files for a new host and its SSH pubkey.
```bash
# Get sops
nix develop
# Decrypt, start en EDITOR, encrypt
sops hosts/.../secrets.yaml
# Push
git commit -a -m Adding new secrets
git push origin
```
### Secrets management with PGP
## Secrets managment with PGP
Add your gpg-id to the .gpg-id file in secrets and let somebody reencrypt it for you.
Maybe this works for you, maybe not. I did it somehow:
@ -253,41 +146,63 @@ PASSWORD_STORE_DIR=`pwd` tr '\n' ' ' < .gpg-id | xargs -I{} pass init {}
Your gpg key has to have the Authenticate flag set. If not update it and push it to a keyserver and wait.
This is necessary, so you can login to any machine with your gpg key.
## Laptops / Desktops
## Secrets Management Using `sops-nix`
This repo could be used in the past as a module. While still technically possible, it is not recommended
because the amounts of flake inputs highly increased and the modules are not designed with that in mind.
### Adding a new host
For end user modules take a look at the [c3d2-user-module](https://gitea.c3d2.de/c3d2/nix-user-module).
Edit `secrets/.sops.yaml`:
For the deployment options take a look at [deployment](https://gitea.c3d2.de/c3d2/deployment).
1. Add an AGE key for this host. Comments in this file tell you how to
do it.
2. Add a `creation_rules` section for `host/$host/*yaml` files
## File system setup
### Editing a hosts secrets
Set the `disko` options for the machine and run:
Edit `secrets/.sops.yaml` to add files for a new host and its SSH pubkey.
```shell
$(nix build --print-out-paths --no-link -L '.#nixosConfigurations.HOSTNAME.config.system.build.disko')
```bash
# Enter the secrets flake
cd secrets
# Get sops
nix develop
# Decrypt, start en EDITOR, encrypt
sops hosts/.../secrets.yaml
# Push
git commit -a -m YOLO
git push origin HEAD:master
# Go back to this flake
cd ..
# Update flake.lock file
nix flake lock . --update-input secrets
```
When adding new disks the paths under ``/dev/disk/by-id/`` should be used, so that the script is idempotent across device restarts.
# Laptops / Desktops
## Install new server
This repository contains a NixOS module that can be used with personal machines
as well. This module appends `/etc/ssh/ssh_known_hosts` with the host keys of
registered HQ hosts, and optionally appends `/etc/hosts` with static IPv6
addresses local to HQ. Simply import the `lib` directory to use the module. As
an example:
- Copy the nix files from an existing, similar host.
- Disable all secrets until after the installation is finished.
- Set `simd.arch` option to the output of ``nix shell nixpkgs#gcc -c gcc -march=native -Q --help=target | grep march`` and update the comment next to it
- If that returns `x86_64` search on a search engine for the `ark.intel.com` entry for the processor which can be found by catting ``/proc/cpuinfo``
- Generate `networking.hostId` with ``head -c4 /dev/urandom | od -A none -t x4`` according to the options description.
- Boot live ISO
- If your ssh key is not baked into the iso, set a password for the `nixos` with passwd to be able to log in over ssh.
- `rsync` the this directory into the live system.
- generate and apply disk layout with disko (see above).
- Generate `hardware-configuration.nix` with ``sudo nixos-generate-config --no-filesystems --root /mnt``.
- If luks disks should be decrypted in initrd over ssh, enable DHCP in the `hardware-configuration.nix` for the interfaces that should be used for that.
- Install nixos system with ``sudo nixos-install --root /mnt --no-channel-copy --no-root-passwd --flake .#HOSTNAME``.
- After a reboot add age key to sops-nix with ``nix shell nixpkgs#ssh-to-age`` and ``ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub``.
- Add ``/etc/machine-id`` and luks password to sops secrets.
- Enable and deploy secrets again.
- Improve new machine setup by automating easy to automate steps and document others.
- Commit everything and push
```nix
# /etc/nixos/configuration.nix
{ config, pkgs, lib, ... }:
let
c3d2Config =
builtins.fetchGit { url = "https://gitea.c3d2.de/C3D2/nix-config.git"; };
in {
imports = [
# ...
"${c3d2Config}/modules/c3d2.nix"
];
c3d2 = {
isInHq = false; # not in HQ, this is the default.
mergeHostsFile = true; # Make entries in /etc/hosts form hosts.nix
enableMotd = true; # Set the login shell message to the <<</>> logo.
};
# ...
}
```

5
ansible/ansible.cfg Normal file
View File

@ -0,0 +1,5 @@
[defaults]
# some basic default values...
inventory = ./hosts

21
ansible/hosts Normal file
View File

@ -0,0 +1,21 @@
[hypervisor]
server3.hq.c3d2.de
server5.hq.c3d2.de
server6.hq.c3d2.de
server7.hq.c3d2.de
server8.hq.c3d2.de
server9.hq.c3d2.de
[hypervisor:vars]
ansible_connection=ssh
ansible_user=root
[kubernetes]
k8s-1.hq.c3d2.de
k8s-2.hq.c3d2.de
k8s-3.hq.c3d2.de
k8s-4.hq.c3d2.de
[kubernetes:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_user=ubuntu

57
ansible/hypervisor.yml Normal file
View File

@ -0,0 +1,57 @@
---
# file: hypervisor.yml
- hosts: hypervisor
roles:
- proxmox
- { role: "elastic.beats", beat: "filebeat",
become: true,
tags: ["filebeat", "logging"],
beats_version: "7.12.1",
beat_conf: {
filebeat: {
"inputs":[{
"type": log,
"enabled": true,
"paths": [
"/var/log/ceph/*.log",
"/var/log/pve/tasks/*/*",
"/var/log/vzdump/*.log",
],
tags: ["hypervisor", "proxmox"]
}]
}
},
"output_conf": {
"logstash": {
"hosts": ["logging.serv.zentralwerk.org:5044", "172.20.73.13:5044"]
}
},
logging_conf: {
level: warning,
to_files: false
}
}
- { role: "elastic.beats", beat: "journalbeat",
become: true,
tags: ["journalbeat", "logging"],
beats_version: "7.12.1",
beat_conf: {
journalbeat: {
"inputs":[{
seek: cursor,
"paths": []
}]
},
tags: ["hypervisor", "proxmox"]
},
"output_conf": {
"logstash": {
"hosts": ["logging.serv.zentralwerk.org:5044", "172.20.73.13:5044"]
}
},
logging_conf: {
level: warning,
to_files: false,
}
}

19
ansible/kubernetes.yml Normal file
View File

@ -0,0 +1,19 @@
---
# file: hypervisor.yml
- hosts: kubernetes
become: yes
become_user: root
become_method: sudo
vars:
check_mk_agent_over_ssh: False
check_mk_agent_monitoring_host: monit.hq.c3d2.de
check_mk_agent_monitoring_user: monitoring
check_mk_agent_add_to_wato: False
check_mk_agent_monitoring_host_wato_username: monitoring
check_mk_agent_monitoring_host_url: https://monit.hq.c3d2.de/c3d2/
# check_mk_agent_local_checks:
# filecount:
# src: files/check_mk_local_checks/filecount
roles:
- k8s
- elnappo.check_mk_agent

View File

@ -0,0 +1,58 @@
---
##### GLOBAL METADATA
- meta:
cluster: devops-ci
##### JOB DEFAULTS
- job:
project-type: matrix
logrotate:
daysToKeep: 30
numToKeep: 100
parameters:
- string:
name: branch_specifier
default: master
description: the Git branch specifier to build (&lt;branchName&gt;, &lt;tagName&gt;,
&lt;commitId&gt;, etc.)
properties:
- github:
url: https://github.com/elastic/ansible-beats/
- inject:
properties-content: HOME=$JENKINS_HOME
concurrent: true
node: master
scm:
- git:
name: origin
credentials-id: f6c7695a-671e-4f4f-a331-acdce44ff9ba
reference-repo: /var/lib/jenkins/.git-references/ansible-beats.git
branches:
- ${branch_specifier}
url: git@github.com:elastic/ansible-beats.git
basedir: ansible-beats
wipe-workspace: 'False'
axes:
- axis:
type: slave
name: label
values:
- linux
- axis:
name: OS
filename: ansible-beats/test/matrix.yml
type: yaml
- axis:
name: TEST_TYPE
filename: ansible-beats/test/matrix.yml
type: yaml
wrappers:
- ansicolor
- timeout:
type: absolute
timeout: 360
fail: true
- timestamps

View File

@ -0,0 +1,27 @@
---
- job:
name: elastic+ansible-beats+master
display-name: elastic / ansible-beats - master
description: Master branch testing with test kitchen
triggers:
- timed: H H(02-04) * * *
builders:
- shell: |-
#!/usr/local/bin/runbld
set -euo pipefail
export RBENV_VERSION='2.5.7'
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv local $RBENV_VERSION
make setup
make verify PATTERN=$TEST_TYPE-$OS
publishers:
- slack:
notify-back-to-normal: True
notify-every-failure: True
room: infra-release-notify
team-domain: elastic
auth-token-id: release-slack-integration-token
auth-token-credential-id: release-slack-integration-token

View File

@ -0,0 +1,31 @@
---
- job:
name: elastic+ansible-beats+pull-request
display-name: elastic / ansible-beats - pull-request
description: Pull request testing with test kitchen
parameters: []
scm:
- git:
branches:
- $ghprbActualCommit
refspec: +refs/pull/*:refs/remotes/origin/pr/*
triggers:
- github-pull-request:
github-hooks: true
org-list:
- elastic
allow-whitelist-orgs-as-admins: true
cancel-builds-on-update: true
status-context: devops-ci
builders:
- shell: |-
#!/usr/local/bin/runbld
set -euo pipefail
export RBENV_VERSION='2.5.7'
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv local $RBENV_VERSION
make setup
make verify PATTERN=$TEST_TYPE-$OS

View File

@ -0,0 +1,40 @@
<!--
** Please read the guidelines below. **
Issues that do not follow these guidelines are likely to be closed.
1. GitHub is reserved for bug reports and feature requests. The best place to
ask a general question is at the Elastic [forums](https://discuss.elastic.co).
GitHub is not the place for general questions.
2. Is this bug report or feature request for a supported OS? If not, it
is likely to be closed. See https://www.elastic.co/support/matrix#show_os
3. Please fill out EITHER the feature request block or the bug report block
below, and delete the other block.
-->
<!-- Feature request -->
**Describe the feature**:
<!-- Bug report -->
**Beats product**:
**Beats version**
**Role version**: (If using master please specify github sha)
**OS version** (`uname -a` if on a Unix-like system):
**Description of the problem including expected versus actual behaviour**:
**Playbook**:
Please specify the full playbook used to reproduce this issue.
**Provide logs from Ansible**:
**Beats logs if relevant**:

View File

@ -0,0 +1,35 @@
---
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before an stale issue is closed
daysUntilClose: 30
# Label to use when marking an issue as stale
staleLabel: triage/stale
issues:
# Comment to post when marking an issue as stale.
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank
you for your contributions.
# Comment to post when closing a stale issue.
closeComment: >
This issue has been automatically closed because it has not had recent
activity since being marked as stale.
pulls:
# Comment to post when marking a PR as stale.
markComment: >
This PR has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
To track this PR (even if closed), please open a corresponding issue if one
does not already exist.
# Comment to post when closing a stale PR.
closeComment: >
This PR has been automatically closed because it has not had recent
activity since being marked as stale.
Please reopen when work resumes.

View File

@ -0,0 +1,9 @@
.kitchen/
*.pyc
.vendor
.bundle
Converging
TODO
.idea/
beats.iml
Dockerfile-*

View File

@ -0,0 +1,136 @@
---
driver:
name: docker
transport:
max_ssh_sessions: 6
provisioner:
name: ansible_playbook
hosts: localhost
roles_path: ./
require_ansible_repo: true
ansible_verbose: true
idempotency_test: true
platforms:
- name: ubuntu-16.04
driver_config:
image: ubuntu:16.04
privileged: true
provision_command:
- apt-get update && apt-get install -y software-properties-common && add-apt-repository -y ppa:ansible/ansible
- apt-get update && apt-get -y -q install ansible python-apt python-pycurl
use_sudo: false
- name: ubuntu-18.04
driver_config:
image: ubuntu:18.04
privileged: true
provision_command:
- apt-get update && apt-get install -y software-properties-common && add-apt-repository -y ppa:ansible/ansible
- apt-get update && apt-get -y -q install ansible python-apt python-pycurl
- mkdir -p /run/sshd
use_sudo: false
- name: ubuntu-20.04
driver_config:
image: ubuntu:20.04
privileged: true
provision_command:
- apt-get update && apt-get install -y software-properties-common && add-apt-repository -y ppa:ansible/ansible
- apt-get update && apt-get -y -q install ansible python-apt python-pycurl
use_sudo: false
- name: debian-8
driver_config:
image: debian:8
privileged: true
provision_command:
- echo "deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main" > /etc/apt/sources.list.d/ansible.list
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
- apt-get update && apt-get -y install ansible
use_sudo: false
- name: debian-9
driver_config:
image: debian:9
privileged: true
provision_command:
- apt-get update && apt-get -y install gnupg2
- echo "deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main" > /etc/apt/sources.list.d/ansible.list
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
- apt-get update && apt-get -y install ansible
use_sudo: false
- name: debian-10
driver_config:
image: debian:10
privileged: true
provision_command:
- apt-get update && apt-get -y install gnupg2
- echo "deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main" > /etc/apt/sources.list.d/ansible.list
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
- apt-get update && apt-get -y install ansible
use_sudo: false
- name: centos-7
driver_config:
image: centos:7
provision_command:
- yum -y install epel-release
- yum -y install ansible
run_command: "/usr/sbin/init"
privileged: true
use_sudo: false
- name: centos-8
driver_config:
image: centos:8
provision_command:
- yum -y install epel-release
- yum -y install ansible
run_command: "/usr/sbin/init"
privileged: true
use_sudo: false
- name: amazonlinux-2
driver_config:
image: amazonlinux:2
provision_command:
- yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
- yum -y install ansible
volume:
- <%=ENV['ES_XPACK_LICENSE_FILE']%>:/tmp/license.json
- /etc # This fixes certain java file actions that check the mount point. Without this adding users fails for some docker storage drivers
run_command: "/usr/sbin/init"
privileged: true
use_sudo: false
suites:
- name: standard
provisioner:
playbook: test/integration/standard.yml
additional_copy_path:
- "."
run_list:
attributes:
- name: standard-6x
provisioner:
playbook: test/integration/standard-6x.yml
additional_copy_path:
- "."
run_list:
attributes:
- name: multi
provisioner:
playbook: test/integration/multi.yml
additional_copy_path:
- "."
run_list:
attributes:
- name: config
provisioner:
playbook: test/integration/config.yml
additional_copy_path:
- "."
run_list:
attributes:
- name: oss
provisioner:
playbook: test/integration/oss.yml
additional_copy_path:
- "."
run_list:
attributes:

View File

@ -0,0 +1 @@
2.5.7

View File

@ -0,0 +1,203 @@
# Changelog
## 7.12.0
* 7.12.0 as default version.
* 6.8.15 as 6.x tested version
| PR | Author | Title |
| --- | --- | --- |
| [#138](https://github.com/elastic/ansible-beats/pull/138) | [@jmlrt](https://github.com/jmlrt) | [meta] fix changelog after 7.11.2 release |
## 7.11.2
* 7.11.2 as default version.
| PR | Author | Title |
| --- | --- | --- |
| [#135](https://github.com/elastic/ansible-beats/pull/135) | [@v1v](https://github.com/v1v) | Update metadata reference for CentOS 8 |
| [#134](https://github.com/elastic/ansible-beats/pull/134) | [@v1v](https://github.com/v1v) | Remove Ubuntu-14.04 support |
| [#118](https://github.com/elastic/ansible-beats/pull/118) | [@v1v](https://github.com/v1v) | Support ubuntu-20 |
| [#116](https://github.com/elastic/ansible-beats/pull/116) | [@v1v](https://github.com/v1v) | Support debian 10 |
| [#131](https://github.com/elastic/ansible-beats/pull/131) | [@jmlrt](https://github.com/jmlrt) | Copy ILM policy file with root permission |
## 7.11.1
* 7.11.1 as default version.
* 6.8.14 as 6.x tested version
## 7.10.2
* 7.10.2 as default version.
| PR | Author | Title |
| --- | --- | --- |
| [#123](https://github.com/elastic/ansible-beats/pull/123) | [@jmlrt](https://github.com/jmlrt) | Cleanup init_script variable |
## 7.10.1
* 7.10.1 as default version.
| PR | Author | Title |
| --- | --- | --- |
| [#115](https://github.com/elastic/ansible-beats/pull/115) | [@v1v](https://github.com/v1v) | Support CentOS-8 |
| [#120](https://github.com/elastic/ansible-beats/pull/120) | [@jmlrt](https://github.com/jmlrt) | Remove CentOS 6 support |
## 7.10.0
* 7.10.0 as default version.
| PR | Author | Title |
| --- | --- | --- |
| [#113](https://github.com/elastic/ansible-beats/pull/113) | [@jmlrt](https://github.com/jmlrt) | [meta] clean deprecated bumper script |
## 7.9.3
* 7.9.3 as default version.
* 6.8.13 as 6.x tested version
## 7.9.2 - 2020/09/24
* 7.9.2 as default version
## 7.9.1 - 2020/09/03
* 7.9.1 as default version
## 7.9.0 - 2020/08/18
* 7.9.0 as default version
* 6.8.12 as 6.x tested version
## 7.8.1 - 2020/07/28
* 7.8.1 as default version
* 6.8.11 as 6.x tested version
| PR | Author | Title |
|---------------------------------------------------------|------------------------------------|--------------------------|
| [#89](https://github.com/elastic/ansible-beats/pull/89) | [@jmlrt](https://github.com/jmlrt) | Add amazonlinux2 support |
## 7.8.0 - 2020/06/18
* 7.8.0 as default version
## 7.7.1 - 2020/06/04
* 7.7.1 as default version
* 6.8.10 as 6.x tested version
## 7.7.0 - 2020/05/13
* 7.7.0 as default version
* 6.8.9 as 6.x tested version
* Fix CentOS tests in [#86](https://github.com/elastic/ansible-beats/pull/86) ([@jmlrt](https://github.com/jmlrt))
| PR | Author | Title |
|---------------------------------------------------------|------------------------------------------|---------------------------------------------|
| [#84](https://github.com/elastic/ansible-beats/pull/84) | [@kravietz](https://github.com/kravietz) | Minor formatting fixes to pass ansible-lint |
## 7.6.2 - 2020/03/31
* 7.6.2 as default version
* 6.8.8 as 6.x tested version
| PR | Author | Title |
|---------------------------------------------------------|------------------------------------|---------------------------------------------------------------------------|
| [#77](https://github.com/elastic/ansible-beats/pull/77) | [@jmlrt](https://github.com/jmlrt) | Add become to individual tasks |
| [#75](https://github.com/elastic/ansible-beats/pull/75) | [@ktibi](https://github.com/ktibi) | Add option to disable the repo installation and lock package installation |
| [#78](https://github.com/elastic/ansible-beats/pull/78) | [@astik](https://github.com/astik) | Aad task to create directory for default policies |
## 7.6.1 - 2020/03/04
* 7.6.1 as default version
## 7.6.0 - 2020/02/11
* 7.6.0 as default version
| PR | Author | Title |
|---------------------------------------------------------|--------------------------------------------------------|------------------------------------|
| [#69](https://github.com/elastic/ansible-beats/pull/69) | [@dependabot[bot]](https://github.com/apps/dependabot) | Bump rubyzip from 1.2.2 to 2.0.0 |
| [#71](https://github.com/elastic/ansible-beats/pull/71) | [@jmlrt](https://github.com/jmlrt) | Fix filebeat example configuration |
| [#72](https://github.com/elastic/ansible-beats/pull/72) | [@beand](https://github.com/beand) | Fixed typo |
## 7.5.2 - 2020/01/21
* 7.5.2 as default version
| PR | Author | Title |
|---------------------------------------------------------|------------------------------------|-----------------------------------------------|
| [#66](https://github.com/elastic/ansible-beats/pull/66) | [@jmlrt](https://github.com/jmlrt) | [doc] switched relative URLs to absolute URLs |
| [#67](https://github.com/elastic/ansible-beats/pull/67) | [@jmlrt](https://github.com/jmlrt) | [ci] bump ruby to 2.5.7 |
## 7.5.1 - 2019/12/18
* 7.5.1 as default version
* 6.8.6 as 6.x tested version
| PR | Author | Title |
|---------------------------------------------------------|----------------------------------------------------|--------------------------|
| [#61](https://github.com/elastic/ansible-beats/pull/61) | [@robsonpeixoto](https://github.com/robsonpeixoto) | Allow use oss repository |
## 7.5.0 - 2019/12/02
* 7.5.0 as default version
* 6.8.5 as 6.x tested version in [#57](https://github.com/elastic/ansible-beats/pull/57) [@jmlrt](https://github.com/jmlrt)
| PR | Author | Title |
|---------------------------------------------------------|--------------------------------------------------|-----------------------------------------------------------------|
| [#50](https://github.com/elastic/ansible-beats/pull/50) | [@jmlrt](https://github.com/jmlrt) | Add bumper script |
| [#55](https://github.com/elastic/ansible-beats/pull/55) | [@tgadiev](https://github.com/tgadiev) | Update syntax to make it compliant to modern ansible-lint rules |
| [#53](https://github.com/elastic/ansible-beats/pull/53) | [@jmlrt](https://github.com/jmlrt) | Indent yaml for config file |
| [#51](https://github.com/elastic/ansible-beats/pull/51) | [@ktibi](https://github.com/ktibi) | Rename the handlers |
| [#59](https://github.com/elastic/ansible-beats/pull/59) | [@MartinVerges](https://github.com/MartinVerges) | Beat config improvements |
## 7.4.1 - 2019/10/23
* 7.4.1 as default version
* 6.8.4 as 6.x tested version
| PR | Author | Title |
|---------------------------------------------------------|------------------------------------|---------------------|
| [#48](https://github.com/elastic/ansible-beats/pull/48) | [@jmlrt](https://github.com/jmlrt) | Fix probot newlines |
## 7.4.0 - 2019/10/01
* 7.4.0 as default version
| PR | Author | Title |
|---------------------------------------------------------|------------------------------------------|---------------------------------------------------------------------|
| [#25](https://github.com/elastic/ansible-beats/pull/25) | [@jmlrt](https://github.com/jmlrt) | Update kitchen Gem dependencies |
| [#6](https://github.com/elastic/ansible-beats/pull/6) | [@levonet](https://github.com/levonet) | Remove `beat_install` variable |
| [#32](https://github.com/elastic/ansible-beats/pull/32) | [@astik](https://github.com/astik) | Remove unused `es_conf_dir` variable |
| [#33](https://github.com/elastic/ansible-beats/pull/33) | [@astik](https://github.com/astik) | Replace custom filter with yaml handling |
| [#10](https://github.com/elastic/ansible-beats/pull/10) | [@Meecr0b](https://github.com/Meecr0b) | Move the `repo_key` configuration to a variable |
| [#34](https://github.com/elastic/ansible-beats/pull/34) | [@nyetwurk](https://github.com/nyetwurk) | Make sure the right beat service gets restarted |
| [#38](https://github.com/elastic/ansible-beats/pull/38) | [@jmlrt](https://github.com/jmlrt) | Add probot config to manage stale issues/pr + GH issue template |
| [#40](https://github.com/elastic/ansible-beats/pull/40) | [@nyetwurk](https://github.com/nyetwurk) | Make beats `repo_key` variable a unique name less likely to collide |
| [#41](https://github.com/elastic/ansible-beats/pull/41) | [@jmlrt](https://github.com/jmlrt) | Enhance ansible-beats documentation |
## 7.0.0 - 2019/05/09
* First release
* 7.0.0 as default version

View File

@ -0,0 +1,6 @@
source 'https://rubygems.org'
gem 'test-kitchen'
gem 'kitchen-docker'
gem 'kitchen-ansible'
gem 'net-ssh'

View File

@ -0,0 +1,117 @@
GEM
remote: https://rubygems.org/
specs:
bcrypt_pbkdf (1.0.1)
builder (3.2.4)
ed25519 (1.2.4)
equatable (0.5.0)
erubi (1.9.0)
ffi (1.12.1)
gssapi (1.3.0)
ffi (>= 1.0.1)
gyoku (1.3.1)
builder (>= 2.1.2)
httpclient (2.8.3)
kitchen-ansible (0.50.0)
net-ssh (>= 3)
test-kitchen (>= 1.4)
kitchen-docker (2.9.0)
test-kitchen (>= 1.0.0)
license-acceptance (1.0.11)
pastel (~> 0.7)
tomlrb (~> 1.2)
tty-box (~> 0.3)
tty-prompt (~> 0.18)
little-plugger (1.1.4)
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
mixlib-install (3.11.18)
mixlib-shellout
mixlib-versioning
thor
mixlib-shellout (2.4.4)
mixlib-versioning (1.2.7)
multi_json (1.14.1)
necromancer (0.4.0)
net-scp (2.0.0)
net-ssh (>= 2.6.5, < 6.0.0)
net-ssh (5.2.0)
net-ssh-gateway (2.0.0)
net-ssh (>= 4.0.0)
nori (2.6.0)
pastel (0.7.2)
equatable (~> 0.5.0)
tty-color (~> 0.4.0)
rubyntlm (0.6.2)
rubyzip (2.0.0)
strings (0.1.5)
strings-ansi (~> 0.1)
unicode-display_width (~> 1.5)
unicode_utils (~> 1.4)
strings-ansi (0.1.0)
test-kitchen (2.2.5)
bcrypt_pbkdf (~> 1.0)
ed25519 (~> 1.2)
license-acceptance (~> 1.0, >= 1.0.11)
mixlib-install (~> 3.6)
mixlib-shellout (>= 1.2, < 3.0)
net-scp (>= 1.1, < 3.0)
net-ssh (>= 2.9, < 6.0)
net-ssh-gateway (>= 1.2, < 3.0)
thor (~> 0.19)
winrm (~> 2.0)
winrm-elevated (~> 1.0)
winrm-fs (~> 1.1)
thor (0.20.3)
timers (4.3.0)
tomlrb (1.2.8)
tty-box (0.3.0)
pastel (~> 0.7.2)
strings (~> 0.1.4)
tty-cursor (~> 0.6.0)
tty-color (0.4.3)
tty-cursor (0.6.1)
tty-prompt (0.18.1)
necromancer (~> 0.4.0)
pastel (~> 0.7.0)
timers (~> 4.0)
tty-cursor (~> 0.6.0)
tty-reader (~> 0.5.0)
tty-reader (0.5.0)
tty-cursor (~> 0.6.0)
tty-screen (~> 0.6.4)
wisper (~> 2.0.0)
tty-screen (0.6.5)
unicode-display_width (1.6.0)
unicode_utils (1.4.0)
winrm (2.3.4)
builder (>= 2.1.2)
erubi (~> 1.8)
gssapi (~> 1.2)
gyoku (~> 1.0)
httpclient (~> 2.2, >= 2.2.0.2)
logging (>= 1.6.1, < 3.0)
nori (~> 2.0)
rubyntlm (~> 0.6.0, >= 0.6.1)
winrm-elevated (1.1.1)
winrm (~> 2.0)
winrm-fs (~> 1.0)
winrm-fs (1.3.4)
erubi (~> 1.8)
logging (>= 1.6.1, < 3.0)
rubyzip (~> 2.0)
winrm (~> 2.0)
wisper (2.0.0)
PLATFORMS
ruby
DEPENDENCIES
kitchen-ansible
kitchen-docker
net-ssh
test-kitchen
BUNDLED WITH
1.17.0

View File

@ -0,0 +1,13 @@
Copyright (c) 2012-2016 Elasticsearch <http://www.elastic.co>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,31 @@
default: build
SHELL:=/bin/bash -eux
PATTERN := standard-ubuntu-1804
.PHONY: converge verify test login destroy list
setup:
bundle install
docker ps
converge:
bundle exec kitchen converge $(PATTERN)
verify:
bundle exec kitchen verify $(PATTERN)
test:
bundle exec kitchen test $(PATTERN) --destroy=always
login:
bundle exec kitchen login $(PATTERN)
destroy:
bundle exec kitchen destroy $(PATTERN)
destroy-all:
bundle exec kitchen destroy
list:
bundle exec kitchen list

View File

@ -0,0 +1,218 @@
# ansible-beats
[![Build Status](https://img.shields.io/jenkins/s/https/devops-ci.elastic.co/job/elastic+ansible-beats+master.svg)](https://devops-ci.elastic.co/job/elastic+ansible-beats+master/)
[![Ansible Galaxy](https://img.shields.io/badge/ansible--galaxy-elastic.beats-blue.svg)](https://galaxy.ansible.com/elastic/beats/)
This role provides a generic means of installing Elastic supported Beats
**Tested Beats**
* Filebeat
* MetricBeat (TopBeat in 1.x)
* Packetbeat
**Tested Versions**
* 7.x
* 6.x
**Tested Platforms**
* Ubuntu 16.04
* Ubuntu 18.04
* Ubuntu 20.04
* Debian 8
* Debian 9
* Debian 10
* CentOS 7
* CentOS 8
* Amazon Linux 2
## Usage
Create your Ansible playbook with your own tasks, and include the role beats. You will have to have this repository accessible within the context of playbook.
```sh
ansible-galaxy install elastic.beats,v7.12.0
```
Then create your playbook yaml adding the role beats.
The application of the beats role results in the installation of a node on a host.
The simplest configuration therefore consists of:
```yaml
hosts: localhost
roles:
- role: elastic.beats
vars:
beats_version: 7.12.0
beat: filebeat
beat_conf:
filebeat:
inputs:
- type: log
enabled: true
paths:
- /var/log/*.log
```
The above installs Filebeat 7.12.0 on the hosts 'localhost'.
**Notes**:
- Beats default version is described in [`beats_version`](https://github.com/elastic/ansible-beats/blob/master/defaults/main.yml#L4). You can override this variable in your playbook to install another version.
While we are testing this role only with one 7.x and one 6.x version (respectively [7.12.0](https://github.com/elastic/ansible-beats/blob/master/defaults/main.yml#L4) and [6.8.15](https://github.com/elastic/ansible-beats/blob/master/test/integration/standard-6x.yml#L7) at the time of writing), this role should work with others version also in most cases.
- Beat product is described in `beat` variable. While currently tested Beats are Filebeat, Metricbeat & Packetbeat, this role should work also with other member of [The Beats Family](https://www.elastic.co/products/beats) in most cases.
## Testing
This playbook uses [Kitchen](https://kitchen.ci/) for CI and local testing.
### Requirements
* Ruby
* Bundler
* Docker
* Make
### Running the tests
To converge an Ubuntu 18.04 host
```sh
$ make converge
```
To run the tests
```sh
$ make verify
```
To list all of the different test suits
```sh
$ make list
```
The default test suite is Ubuntu 18.04. If you want to test another suite you can override this with the `PATTERN` variable
```sh
$ make converge PATTERN=standard-centos-7
```
The `PATTERN` is a kitchen pattern which can match multiple suites. To run all tests for CentOS
```sh
$ make converge PATTERN=centos-7
```
When you are finished testing you can clean up everything with
```sh
$ make destroy-all
```
### Basic Beats configuration
All Beats configuration parameters are supported. This is achieved using a configuration map parameter `beat_conf` which is serialized into the `${beat}.yml` file.
The use of a map ensures the Ansible playbook does not need to be updated to reflect new/deprecated/plugin configuration parameters.
In addition to the `beat_conf` map, several other parameters are supported for additional functions e.g. script installation. These can be found in the role's `defaults/main.yml` file.
The following illustrates applying configuration parameters to Packetbeat instance.
```yaml
- name: Example playbook for installing packetbeat
hosts: localhost
roles:
- { role: beats, beat: "packetbeat",
beat_conf: {
"interfaces": {"device":"any"},
"protocols": {
"dns": {
"ports": [53],
"include_authorities":true
},
"http": {
"ports": [80, 8080, 8000, 5000, 8002]
},
"memcache": {
"ports": [11211]
},
"mysql": {
"ports": [3306]
},
"pgsql": {
"ports": [5432]
},
"redis": {
"ports": [6379]
},
"thrift": {
"ports": [9090]
},
"mongodb": {
"ports": [27017]
}
}
},
output_conf : {
"elasticsearch": {
"hosts": ["localhost:9200"]
}
}
}
vars:
use_repository: "true"
```
### Additional Configuration
Supported variables are as follows:
- **beat** (*MANDATORY*): Beat product. Supported values are: "filebeat", "metricbeat" & "packetbeat" (others beats from [The Beats Family](https://www.elastic.co/products/beats) should work in most cases but aren't currently tested).
- **beat_conf** (*MANDATORY*): Beat Configuration. Should be defined as a map.
- **beats_version** (*Defaults to `7.12.0`*): Beats version.
- **version_lock** (*Defaults to `false`*): Locks the installed version if set to true, thus preventing other processes from updating. This will not impact the roles ability to update the beat on subsequent runs (it unlocks and re-locks if required).
- **use_repository** (*Defaults to `true`*): Use elastic repo for yum or apt if true. If false, a custom custom_package_url must be provided.
- **beats_add_repository** (*Defaults to `{use_repository}`*): Install elastic repo for yum or apt if true. If false, the present repositories will be used. Useful if you already have beats packages in your repo.
- **start_service** (*Defaults to `true`*): service will be started if true, false otherwise.
- **restart_on_change** (*Defaults to `true`*): Changes to configuration or installed versions, will result in a restart if true.
- **daemon_args** (*Applicable to version 1.x of beats*): Allows run time params to be passed to beats.
- **logging_conf** (*Defaults to `{"files":{"rotateeverybytes":10485760}}`*): Logging configuration. Should be defined as a map. Map is serialized into logging section of beat config.
- **shipper_conf** (*Applicable to version 1.x of beats*): Shipper configuration. Should be defined as a map . Map is serialized into shipper section of beat config.
- **output_conf** (*Defaults to `{"elasticsearch":{"hosts":["localhost:9200"]}}`*): Output configuration. Map is serialized into output section of beat config.
- **beats_pid_dir** (*Defaults to `/var/run`*): Location of beats pid file.
- **beats_conf_dir** (*Defaults to `/etc/{beat}`*): Location of conf directory for beats configuration file.
- **default_ilm_policy** (*Defaults undefined*): local path to default policy if any custom one is defined
### Focus on ILM
By default, *beat* will create a default policy defined as part of the beat being deployed.
You can override default ILM setup by defining ILM conf as part of *beat_conf*.
For example:
```
- role: ansible-beats
beat: metricbeat
beat_conf:
setup:
ilm:
policy_file: /etc/filebeat/policies/my-default-metricbeat.json
overwrite: true
metricbeat.modules:
...
default_ilm_policy: conf/my-default-metricbeat.json
become: yes
```
This will copy *conf/my-default-filebeat.json* to */etc/filebeat/policies/my-default-filebeat.json*.
This policy will be used as default one for this beat.
## License
Apache 2.0
## Limitations
Multiple instances of the same beat cannot be installed on the same target server.
## Questions on Usage
We welcome questions on how to use the role. However, in order to keep the GitHub issues list focused on "issues" we ask the community to raise questions at https://discuss.elastic.co/c/beats. This is monitored by the maintainers.
Community Contributions always appreciated and welcome! Please ensure all contributions include tests as appropriate.

View File

@ -0,0 +1 @@
[defaults]

View File

@ -0,0 +1,14 @@
---
# defaults file for beats
beats_version: 7.12.0
oss_version: false
version_lock: false
use_repository: true
beats_add_repository: "{{ use_repository }}"
start_service: true
restart_on_change: true
daemon_args: ""
logging_conf: {"files":{"rotateeverybytes":10485760}}
output_conf: {"elasticsearch":{"hosts":["localhost:9200"]}}
beats_pid_dir: "/var/run"
beats_conf_dir: "/etc/{{beat}}"

View File

@ -0,0 +1,10 @@
---
# handlers file for beats
- name: restart the service
become: yes
service:
name: "{{ beat_product }}"
state: restarted
enabled: true
when: start_service and restart_on_change and not beats_started.changed

View File

@ -0,0 +1,2 @@
install_date: Sat Apr 10 14:18:16 2021
version: v7.12.0

View File

@ -0,0 +1,28 @@
---
allow_duplicates: true
dependencies: []
galaxy_info:
role_name: beats
author: Dale McDiarmid
description: Beats for Linux
company: "Elastic.co"
issue_tracker_url: https://github.com/elastic/ansible-beats/issues
license: "license (Apache)"
min_ansible_version: 2.0
platforms:
- name: EL
versions:
- 7
- 8
- name: Debian
versions:
- all
- name: Ubuntu
versions:
- all
galaxy_tags:
- beats
- elastic
- elk
- logging
- monitoring

View File

@ -0,0 +1,87 @@
---
# Configure Beats Node
- name: Set default facts
set_fact:
pid_file: '{{ beats_pid_dir }}/{{ beat }}.pid'
instance_default_file: '{{ default_file }}/{{ beat }}'
conf_file: '{{ beats_conf_dir }}/{{ beat }}.yml'
beat_output_conf:
output: '{{ output_conf }}'
- name: Set beat_shipper_conf
set_fact:
beat_shipper_conf:
shipper: '{{ shipper_conf }}'
when: shipper_conf is defined
- name: Set beat_logging_conf
set_fact:
beat_logging_conf:
logging: '{{ logging_conf }}'
- name: Check pid_dir status
stat:
path: '{{ beats_pid_dir }}'
register: pid_stat
- name: Create PID Directory
become: yes
file:
path: '{{ beats_pid_dir }}'
state: directory
when: pid_stat.stat.isdir is not defined or pid_stat.stat.islnk is not defined
# fail if pid and config directories are not links or not directories i.e files
- name: Create Config Directory
become: yes
file:
path: '{{ beats_conf_dir }}'
state: directory
# Copy the default file
- name: Copy Default File for Instance
become: yes
template:
src: beat.j2
dest: '{{ instance_default_file }}'
mode: 0644
force: true
owner: root
group: root
notify: restart the service
# Copy templated config file
- name: Copy Configuration File for {{ beat }}
become: yes
template:
src: beat.yml.j2
dest: '{{ conf_file }}'
mode: 0644
force: true
owner: root
group: root
notify: restart the service
# Copy default ILM policy file
- name: Create default policies config directory
become: yes
file:
path: '{{ beat_conf.setup.ilm.policy_file | dirname }}'
state: directory
mode: 0755
owner: root
group: root
when: default_ilm_policy is defined
- name: Copy default ILM policy file for {{ beat }}
become: yes
copy:
src: '{{default_ilm_policy}}'
dest: '{{ beat_conf.setup.ilm.policy_file }}'
mode: 0644
owner: root
group: root
when: default_ilm_policy is defined
notify: restart the service

View File

@ -0,0 +1,100 @@
---
- name: Debian - Ensure apt-transport-https is installed
become: yes
apt:
name: apt-transport-https
state: present
cache_valid_time: 86400
when: use_repository | bool
register: beat_install
until: beat_install is succeeded
notify: restart the service
- name: Debian - Ensure python-urllib3, python-openssl, python-pyasn1 & python-pip are installed
become: yes
apt:
name:
- python-urllib3
- python-openssl
- python-pyasn1
- python-pip
state: present
register: libs_install
until: libs_install is succeeded
when:
- use_repository | bool
- ansible_distribution_release == "trusty"
- name: Debian - ensure ndg-httpsclient pip is installed
become: yes
pip:
name: ndg-httpsclient
state: present
register: ndg_install
until: ndg_install is succeeded
when:
- use_repository | bool
- ansible_distribution_release == "trusty"
- name: Debian - Add Beats repository key
become: yes
apt_key:
url: '{{ elastic_repo_key }}'
state: present
register: apt_key_install
until: apt_key_install is succeeded
when: beats_add_repository | bool
- name: Debian - add beats repository
become: yes
apt_repository:
repo: 'deb {{ repo_url }} stable main'
state: present
register: repo_install
until: repo_install is succeeded
when: beats_add_repository | bool
- name: Debian - unhold {{ beat }} version for install
become: yes
command: apt-mark unhold {{ beat }}
changed_when: false
- name: Debian - Ensure {{ beat }} is installed
become: yes
apt:
name: >-
{{ beat }}{% if beats_version is defined and beats_version|length>0 %}={{ beats_version }}{% endif %}
state: present
cache_valid_time: 86400
register: beat_install
until: beat_install is succeeded
when: use_repository | bool
notify: restart the service
- name: Debian - hold {{ beat }} version
become: yes
command: apt-mark hold {{ beat }}
when: version_lock
changed_when: false
- name: Set os_arch
set_fact:
os_arch: >-
{{ ansible_architecture == 'x86_64' | ternary('amd64', 'i386') }}
- name: Debian - Download {{ beat }} from url
get_url:
url: >-
{% if custom_package_url is defined %}{{ custom_package_url }}{%
else %}{{ beats_package_url }}/{{ beat }}/{{ beat }}_{{ beats_version }}_{{ os_arch }}.deb{% endif %}
dest: '/tmp/{{ beat }}_{{ beats_version }}_{{ os_arch }}.deb'
validate_certs: false
when: not use_repository | bool
- name: Debian - Ensure {{ beat }} is installed from downloaded package
become: yes
apt:
deb: '/tmp/{{ beat }}_{{ beats_version }}_{{ os_arch }}.deb'
when: not use_repository | bool
notify: restart the service

View File

@ -0,0 +1,19 @@
---
- name: Check beat variable
fail:
msg: "beat must be specified and cannot be blank e.g. filebeat"
when: beat is not defined or (beat | length == 0)
- name: Check beat_conf variable
fail:
msg: "beat_conf must be specified"
when: beat_conf is not defined
- name: Check ILM variables
fail:
msg: "beat_conf.setup.ilm.policy_file must be specified if default_ilm_policy is used"
when: default_ilm_policy is defined and beat_conf.setup.ilm.policy_file is not defined
- name: Set beats_major_version
set_fact:
beats_major_version: '{% if oss_version %}oss-{% endif %}{{ beats_version.split(".")[0] }}.x'

View File

@ -0,0 +1,59 @@
---
- name: Redhat - add beats repository
become: yes
template:
src: beats.repo.j2
dest: /etc/yum.repos.d/beats.repo
when: beats_add_repository | bool
- name: RedHat - install yum-version-lock
become: yes
yum:
name: yum-plugin-versionlock
state: present
update_cache: true
when: version_lock | bool
register: versionlock_install
until: versionlock_install is succeeded
- name: RedHat - unlock {{ beat }} for install
become: yes
shell: yum versionlock delete {{ beat }} || true
changed_when: false
when: version_lock | bool
tags:
- skip_ansible_lint
- name: RedHat - Ensure {{ beat }} is installed
become: yes
yum:
name: >-
{{ beat }}{% if beats_version is defined and beats_version|length %}-{{ beats_version }}{% endif %}
state: present
update_cache: true
register: beat_install
until: beat_install is succeeded
when: use_repository | bool
notify: restart the service
- name: RedHat - lock {{ beat }} version
become: yes
shell: >-
yum versionlock add
{{ beat }}{% if beats_version is defined and beats_version|length %}-{{ beats_version }}{% endif %}
when: version_lock | bool
changed_when: false
tags:
- skip_ansible_lint
- name: RedHat - Install {{ beat }} from url
become: yes
yum:
name: >-
{% if custom_package_url is defined %}{{ custom_package_url }}{%
else %}{{ beats_package_url }}/{{ beat }}-{{ beats_version }}-{{ ansible_architecture }}.rpm{% endif %}
state: present
register: beat_install
until: beat_install is succeeded
when: not use_repository
notify: restart the service

View File

@ -0,0 +1,24 @@
---
# Install OS specific beats
- name: Include specific Beats
include_tasks: beats-debian.yml
when: ansible_os_family == 'Debian'
- name: Include specific Beats
include_tasks: beats-redhat.yml
when: ansible_os_family == 'RedHat'
# Configuration file for beats
- name: Beats configuration
include_tasks: beats-config.yml
# Make sure the service is started, and restart if necessary
- name: Start {{ beat_product }} service
become: yes
service:
name: '{{ beat }}'
state: started
enabled: true
when: start_service
register: beats_started

View File

@ -0,0 +1,17 @@
---
# tasks file for beats
- name: check-parameters
include_tasks: beats-param-check.yml
- name: define beat product
set_fact:
beat_product: "{{ beat }}"
- name: os-specific vars
include_vars: '{{ ansible_os_family }}.yml'
- include_tasks: beats.yml
- name: Force all notified handlers to run at this point, not waiting for normal sync points
meta: flush_handlers

View File

@ -0,0 +1,7 @@
################################
# {{beat}}
################################
# Beats PID File
PIDFILE={{pid_file}}
DAEMON_ARGS="-c {{conf_file}} {{daemon_args}}"

View File

@ -0,0 +1,22 @@
# {{ ansible_managed }}
################### {{beat}} Configuration #########################
############################# {{beat}} ######################################
{{ beat_conf | to_nice_yaml(indent=2) }}
###############################################################################
############################# Libbeat Config ##################################
# Base config file used by all other beats for using libbeat features
############################# Output ##########################################
{{ beat_output_conf | to_nice_yaml(indent=2) }}
{% if shipper_conf is defined %}############################# Shipper #########################################
{{ beat_shipper_conf | to_nice_yaml(indent=2) }}
{% endif %}
############################# Logging #########################################
{{ beat_logging_conf | to_nice_yaml(indent=2) }}

View File

@ -0,0 +1,6 @@
[beats]
name=Elastic Beats Repository
baseurl={{ repo_url }}
enabled=1
gpgkey={{ elastic_repo_key }}
gpgcheck=1

View File

@ -0,0 +1,46 @@
---
# Install specific version here
- name: wrapper playbook for kitchen testing beats
hosts: localhost
roles:
- role: ansible-beats
beat: packetbeat
version_lock: true
beat_conf:
interfaces:
device: any
protocols:
dns:
ports:
- 53
include_authorities: true
http:
ports:
- 80
- 8080
- 8000
- 5000
- 8002
memcache:
ports:
- 11211
mysql:
ports:
- 3306
pgsql:
ports:
- 5432
redis:
ports:
- 6379
thrift:
ports:
- 9090
mongodb:
ports:
- 27017
output_conf:
elasticsearch:
hosts: ["localhost:9200"]
vars:
use_repository: true

View File

@ -0,0 +1,49 @@
require 'spec_helper'
describe 'Config Tests' do
describe service('packetbeat') do
it { should be_running }
end
describe package('packetbeat') do
it { should be_installed }
end
describe file('/etc/packetbeat/packetbeat.yml') do
it { should be_file }
it { should be_owned_by 'root' }
end
describe file('/etc/packetbeat/packetbeat.yml') do
it { should contain 'logging:' }
it { should contain 'output:' }
it { should contain 'protocols:' }
it { should contain 'dns:' }
it { should contain 'memcache:' }
it { should contain 'http:' }
it { should contain 'mongodb:' }
it { should contain 'mysql:' }
it { should contain 'pgsql:' }
it { should contain 'redis:' }
it { should contain 'thrift:' }
it { should contain 'interfaces:' }
it { should contain 'device: any' }
end
describe file('/etc/init.d/packetbeat') do
it { should exist }
end
if os[:family] == 'redhat'
describe command('yum versionlock list | grep packetbeat') do
its(:stdout) { should match /packetbeat/ }
end
elsif ['debian', 'ubuntu'].include?(os[:family])
describe command('sudo apt-mark showhold | grep packetbeat') do
its(:stdout) { should match /packetbeat/ }
end
end
end

View File

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'rspec-retry'

View File

@ -0,0 +1,11 @@
require 'serverspec'
set :backend, :exec
require 'rspec/retry'
RSpec.configure do |config|
# show retry status in spec process
config.verbose_retry = true
# show exception that triggers a retry if verbose_retry is set to true
config.display_try_failure_messages = true
end

View File

@ -0,0 +1,29 @@
---
- name: wrapper playbook for kitchen testing "beats"
hosts: localhost
roles:
- role: ansible-beats
beat: filebeat
beat_conf:
filebeat:
inputs:
- paths:
- /var/log/*.log
type: log
- role: ansible-beats
beat: metricbeat
beat_conf:
metricbeat:
modules:
- module: "system"
metricsets:
- cpu
- filesystem
- network
- process
enabled: true
period: 10s
processes: [".*"]
cpu_ticks: false
vars:
use_repository: true

View File

@ -0,0 +1,57 @@
require 'spec_helper'
describe 'Multi Tests' do
describe service('filebeat') do
it { should be_running }
end
describe package('filebeat') do
it { should be_installed }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should be_file }
it { should be_owned_by 'root' }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should contain 'filebeat:' }
it { should contain 'logging:' }
it { should contain 'output:' }
end
describe file('/etc/init.d/filebeat') do
it { should exist }
end
describe service('metricbeat') do
it { should be_running }
end
describe package('metricbeat') do
it { should be_installed }
end
describe file('/etc/metricbeat/metricbeat.yml') do
it { should be_file }
it { should be_owned_by 'root' }
end
describe file('/etc/metricbeat/metricbeat.yml') do
it { should contain 'module: system' }
it { should contain 'metricsets:' }
it { should contain 'period: 10s' }
it { should contain 'processes:' }
it { should contain 'cpu_ticks:' }
it { should contain 'logging:' }
it { should contain 'output:' }
end
describe file('/etc/init.d/metricbeat') do
it { should exist }
end
end

View File

@ -0,0 +1,15 @@
---
- name: wrapper playbook for kitchen testing "beats"
hosts: localhost
roles:
- role: ansible-beats
beat: filebeat
beat_conf:
filebeat:
inputs:
- paths:
- /var/log/*.log
type: log
vars:
use_repository: true
oss_version: true

View File

@ -0,0 +1,39 @@
require 'spec_helper'
describe 'Open Source Tests' do
describe service('filebeat') do
it { should be_running }
end
describe package('filebeat') do
it { should be_installed }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should be_file }
it { should be_owned_by 'root' }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should contain 'filebeat:' }
it { should contain 'logging:' }
it { should contain 'output:' }
end
describe file('/etc/init.d/filebeat') do
it { should exist }
end
if os[:family] == 'redhat'
describe command('yum versionlock list | grep filebeat') do
its(:stdout) { should_not match /filebeat/ }
end
elsif ['debian', 'ubuntu'].include?(os[:family])
describe command('sudo apt-mark showhold | grep filebeat') do
its(:stdout) { should_not match /filebeat/ }
end
end
end

View File

@ -0,0 +1,16 @@
---
- name: wrapper playbook for kitchen testing "beats"
hosts: localhost
roles:
- role: ansible-beats
beat: filebeat
beat_conf:
filebeat:
prospectors:
- paths:
- /var/log/*.log
input_type: log
registry_file: /var/lib/filebeat/registry
vars:
beats_version: 6.8.15
use_repository: "true"

View File

@ -0,0 +1,39 @@
require 'spec_helper'
describe 'Standard Tests' do
describe service('filebeat') do
it { should be_running }
end
describe package('filebeat') do
it { should be_installed }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should be_file }
it { should be_owned_by 'root' }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should contain 'filebeat:' }
it { should contain 'logging:' }
it { should contain 'output:' }
end
describe file('/etc/init.d/filebeat') do
it { should exist }
end
if os[:family] == 'redhat'
describe command('yum versionlock list | grep filebeat') do
its(:stdout) { should_not match /filebeat/ }
end
elsif ['debian', 'ubuntu'].include?(os[:family])
describe command('sudo apt-mark showhold | grep filebeat') do
its(:stdout) { should_not match /filebeat/ }
end
end
end

View File

@ -0,0 +1,14 @@
---
- name: wrapper playbook for kitchen testing "beats"
hosts: localhost
roles:
- role: ansible-beats
beat: filebeat
beat_conf:
filebeat:
inputs:
- paths:
- /var/log/*.log
type: log
vars:
use_repository: true

View File

@ -0,0 +1,39 @@
require 'spec_helper'
describe 'Standard Tests' do
describe service('filebeat') do
it { should be_running }
end
describe package('filebeat') do
it { should be_installed }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should be_file }
it { should be_owned_by 'root' }
end
describe file('/etc/filebeat/filebeat.yml') do
it { should contain 'filebeat:' }
it { should contain 'logging:' }
it { should contain 'output:' }
end
describe file('/etc/init.d/filebeat') do
it { should exist }
end
if os[:family] == 'redhat'
describe command('yum versionlock list | grep filebeat') do
its(:stdout) { should_not match /filebeat/ }
end
elsif ['debian', 'ubuntu'].include?(os[:family])
describe command('sudo apt-mark showhold | grep filebeat') do
its(:stdout) { should_not match /filebeat/ }
end
end
end

View File

@ -0,0 +1,17 @@
---
OS:
- ubuntu-1604
- ubuntu-1804
- ubuntu-2004
- debian-8
- debian-9
- debian-10
- centos-7
- centos-8
- amazonlinux-2
TEST_TYPE:
- standard
- standard-6x
- multi
- config
- oss

View File

@ -0,0 +1,3 @@
---
default_file: "/etc/default"
repo_url: "https://artifacts.elastic.co/packages/{{ beats_major_version }}/apt"

View File

@ -0,0 +1,3 @@
---
default_file: "/etc/sysconfig"
repo_url: "https://artifacts.elastic.co/packages/{{ beats_major_version }}/yum"

View File

@ -0,0 +1,5 @@
---
# vars file for beats
beats_package_url: "https://download.elastic.co/beats"
elastic_repo_key: "https://packages.elastic.co/GPG-KEY-elasticsearch"

View File

@ -0,0 +1,173 @@
# Created by https://www.gitignore.io
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Linux ###
*~
# KDE directory preferences
.directory
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
### Python Patch ###
.venv/

View File

@ -0,0 +1,20 @@
---
language: python
services: docker
env:
matrix:
- MOLECULE_DISTRO: ubuntu1604
- MOLECULE_DISTRO: ubuntu1804
- MOLECULE_DISTRO: debian8
- MOLECULE_DISTRO: debian9
- MOLECULE_DISTRO: centos7
install:
- pip install molecule docker
script:
- molecule test
notifications:
webhooks: https://galaxy.ansible.com/api/v1/notifications/

View File

@ -0,0 +1,13 @@
extends: default
rules:
braces:
max-spaces-inside: 1
level: error
brackets:
max-spaces-inside: 1
level: error
line-length: disable
# NOTE(retr0h): Templates no longer fail this lint rule.
# Uncomment if running old Molecule templates.
# truthy: disable

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 elnappo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,79 @@
# ansible-role-check-mk-agent
[![Build Status](https://travis-ci.org/elnappo/ansible-role-check-mk-agent.svg?branch=master)](https://travis-ci.org/elnappo/ansible-role-check-mk-agent) [![Ansible Galaxy](https://img.shields.io/badge/galaxy-elnappo.check--mk--agent-blue.svg?style=flat)](https://galaxy.ansible.com/elnappo/check-mk-agent/)
Installs check mk\_agent. Run it with systemd-socket, SSH with sudo or SSH as root (default). Get more information about check\_mk at [https://mathias-kettner.de/check_mk.html]()
## Features
* Install check_mk agent
* Query check_mk agent over systemd-socket (only with check_mk_agent >= v1.4), SSH as root or SSH with sudo
* Setup firewall if systemd-socket ist used (ufw or firewalld)
* Add SSH host key to check_mk server
* Install check_mk agent plugins/local checks and their dependencies
* **Add hosts to check_mk server via WATO API**
## Requirements
* Python requests >= v2.5.0
Tested on Ubuntu 16.04, 18.04 and CentOS 7, should also run under Debian and RedHat.
## Install
$ ansible-galaxy install elnappo.check_mk_agent
## Role Variables
* `check_mk_agent_over_ssh: True`
* `check_mk_agent_with_sudo: False` Adds a user which is allowed to run check_mk_agent with sudo
* `check_mk_agent_add_host_pubkey: False` Import SSH host keys into your check_mk servers known_hosts file
* `check_mk_agent_monitoring_host:` Hostname of your check_mk server
* `check_mk_agent_monitoring_user:` Username under which your check_mk instance runs
* `check_mk_agent_plugins_requirements: []` Requirements for extra plugins
* `check_mk_agent_plugins: []` List of extra plugins to install
* `check_mk_agent_local_checks: {}`
* `check_mk_agent_pubkey_file:` Path to SSH pubkey file
* `check_mk_agent_add_to_wato: False`
* `check_mk_agent_monitoring_host_folder: ""`
* `check_mk_agent_monitoring_host_discovery_mode: new`
* `check_mk_agent_monitoring_host_url:`
* `check_mk_agent_monitoring_host_wato_username:`
* `check_mk_agent_monitoring_host_wato_secret:`
* `check_mk_agent_setup_firewall: True` Add firewall rule (ufw/firewalld) when using systemd-socket
* `check_mk_agent_manual_install: False` Leave agent package installation to the user
## Included check_mk extra plugins
Could be found under `files/plugins/`. As it is hard to keep these plugins
up-to-date, these will be removed in a future version from the repository.
## Dependencies
None.
## Example Playbook
```yaml
- hosts: servers
vars:
check_mk_agent_pubkey_file: omd_rsa.pub
check_mk_agent_add_host_pubkey: True
check_mk_agent_monitoring_host: checkmk.example.com
check_mk_agent_monitoring_user: monitoring
check_mk_agent_add_to_wato: True
check_mk_agent_monitoring_host_url: http://cmk.example.com/monitoring/
check_mk_agent_monitoring_host_wato_username: ansible
check_mk_agent_monitoring_host_wato_secret: 7JTuBt6nETYHG1GS
check_mk_agent_local_checks:
filecount:
src: files/check_mk_local_checks/filecount
cache_time: 600
filestat:
src: files/check_mk_local_checks/filestat
roles:
- elnappo.check_mk_agent
```
## License
MIT
## Author Information
elnappo <elnappo@nerdpol.io>

View File

@ -0,0 +1,14 @@
---
# defaults file for check_mk agent
check_mk_agent_over_ssh: true
check_mk_agent_with_sudo: false
check_mk_agent_add_host_pubkey: false
check_mk_agent_plugins_requirements: []
check_mk_agent_plugins: []
check_mk_agent_local_checks: {}
check_mk_agent_pubkey_file:
check_mk_agent_add_to_wato: false
check_mk_agent_monitoring_host_folder: ""
check_mk_agent_monitoring_host_discovery_mode: "new"
check_mk_agent_setup_firewall: true
check_mk_agent_manual_install: false

View File

@ -0,0 +1,17 @@
These plugins can be installed in the plugins directory of the Linux agent
in /usr/lib/check_mk_agent/plugins/. Please only install the plugins that
you really need.
If you want a plugin to be run asynchronously and also in
a larger interval then the normal check interval, then you can
copy it to a subdirectory named after a number of *minutes*,
e.g.:
/usr/lib/check_mk_agent/plugins/60/mk_zypper
In that case the agent will:
- Run this plugin in the background and wait not for it to finish.
- Store the result of the plugin in a cache file below /etc/check_mk/cache.
- Use that file for one hour before running the script again.

View File

@ -0,0 +1,170 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Check_MK-Agent-Plugin - Apache Server Status
#
# Fetches the server-status page from detected or configured apache
# processes to gather status information about this apache process.
#
# To make this agent plugin work you have to load the status_module
# into your apache process. It is also needed to enable the "server-status"
# handler below the URL "/server-status".
#
# By default this plugin tries to detect all locally running apache processes
# and to monitor them. If this is not good for your environment you might
# create an apache_status.cfg file in MK_CONFDIR and populate the servers
# list to prevent executing the detection mechanism.
#
# It is also possible to override or extend the ssl_ports variable to make the
# check contact other ports than 443 with HTTPS requests.
import os, sys, urllib2, re, socket
config_dir = os.getenv("MK_CONFDIR", "/etc/check_mk")
config_file = config_dir + "/apache_status.conf"
if not os.path.exists(config_file):
config_file = config_dir + "/apache_status.cfg"
# We have to deal with socket timeouts. Python > 2.6
# supports timeout parameter for the urllib2.urlopen method
# but we are on a python 2.5 system here which seem to use the
# default socket timeout. We are local here so set it to 1 second.
socket.setdefaulttimeout(5.0)
# None or list of (proto, ipaddress, port) tuples.
# proto is 'http' or 'https'
servers = None
ssl_ports = [ 443, ]
if os.path.exists(config_file):
execfile(config_file)
def try_detect_servers():
results = []
for line in os.popen('netstat -tlnp 2>/dev/null').readlines():
parts = line.split()
# Skip lines with wrong format
if len(parts) < 7 or '/' not in parts[6]:
continue
pid, proc = parts[6].split('/', 1)
to_replace = re.compile('^.*/')
proc = to_replace.sub('', proc)
procs = [ 'apache2', 'httpd', 'httpd2-prefork', 'httpd2-worker', 'httpd.worker', 'fcgi-pm' ]
# the pid/proc field length is limited to 19 chars. Thus in case of
# long PIDs, the process names are stripped of by that length.
# Workaround this problem here
procs = [ p[:19 - len(pid) - 1] for p in procs ]
# Skip unwanted processes
if proc not in procs:
continue
address, port = parts[3].rsplit(':', 1)
port = int(port)
# Use localhost when listening globally
if address == '0.0.0.0':
address = '127.0.0.1'
elif address == '::':
address = '[::1]'
elif ':' in address:
address = '[%s]' % address
# Switch protocol if port is SSL port. In case you use SSL on another
# port you would have to change/extend the ssl_port list
if port in ssl_ports:
proto = 'https'
else:
proto = 'http'
results.append((proto, address, port))
return results
if servers is None:
servers = try_detect_servers()
if not servers:
sys.exit(0)
sys.stdout.write('<<<apache_status>>>\n')
for server in servers:
if isinstance(server, tuple):
proto, address, port = server
page = 'server-status'
else:
proto = server['protocol']
address = server['address']
port = server['port']
page = server.get('page', 'server-status')
portspec = port and ":%d" % port or ""
try:
url = '%s://%s%s/%s?auto' % (proto, address, portspec, page)
# Try to fetch the status page for each server
try:
request = urllib2.Request(url, headers={"Accept" : "text/plain"})
fd = urllib2.urlopen(request)
except urllib2.URLError, e:
if 'unknown protocol' in str(e):
# HACK: workaround misconfigurations where port 443 is used for
# serving non ssl secured http
url = 'http://%s%s/server-status?auto' % (address, portspec)
fd = urllib2.urlopen(url)
else:
raise
except Exception, e:
if 'doesn\'t match' in str(e):
# HACK: workaround if SSL port is found and localhost is using
# SSL connections but certificate does not match
portspec = ':80'
url = 'http://%s%s/server-status?auto' % (address, portspec)
fd = urllib2.urlopen(url)
else:
raise
for line in fd.read().split('\n'):
if not line.strip():
continue
if line.lstrip()[0] == '<':
# Seems to be html output. Skip this server.
break
sys.stdout.write("%s %s %s\n" % (address, port, line))
except urllib2.HTTPError, e:
sys.stderr.write('HTTP-Error (%s%s): %s %s\n' % (address, portspec, e.code, e))
except Exception, e:
sys.stderr.write('Exception (%s%s): %s\n' % (address, portspec, e))

View File

@ -0,0 +1,26 @@
#!/bin/sh
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
su - griduser -c "asmcmd $@"

View File

@ -0,0 +1,34 @@
#!/bin/sh
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
INSTANCES=$(ps -ef | grep db2sysc | awk '{print $1}' | sort -u | grep -v root)
if [ "$INSTANCES" ] ; then
echo "<<<db2_mem>>>"
for INSTANCE in $INSTANCES; do
echo "Instance $INSTANCE"
su - $INSTANCE -c "db2pd -dbptnmem " | egrep '(Memory Limit|HWM usage)'
done
fi

View File

@ -0,0 +1,48 @@
#!/bin/sh
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# This check can be used to test the name resolution of a given host
# address using the local resolver of the system this script is
# running on.
HOSTADDRESSES=mathias-kettner.de
if [ -e $MK_CONFDIR/dnsclient.cfg ] ; then
. $MK_CONFDIR/dnsclient.cfg
fi
echo "<<<mrpe>>>"
for HOSTADDRESS in $HOSTADDRESSES
do
ADDRESSES=`nslookup $HOSTADDRESS | sed -n -e 1,3d -e '/^Address: *\(.*\)$/s//\1/p'`
if [ ! "$ADDRESSES" ] ; then
STATE=2
OUTPUT="CRIT - $HOSTADDRESS could not be resolved"
else
STATE=0
OUTPUT="OK - $HOSTADDRESS resolved into $ADDRESSES"
fi
echo Resolve_$HOSTADDRESS $STATE $OUTPUT
done

View File

@ -0,0 +1,92 @@
#!/usr/bin/ksh
# Monitor status of LUNs on HP-UX
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Put this file into /usr/lib/check_mk_agent/plugins. Then
# reinventorize your host.
# Actually querying these stats is quite slow since they freshly update
# on each call. If you have a few 1000 luns then this will not work.
get_stats()
{
scsimgr get_stat -D $LUN | tr '\=' ':' | grep -e 'STATISTICS FOR LUN' -e 'Bytes' -e 'Total I/Os processed' -e 'I/O failure' -e 'IO failures due
to'
return $?
}
# Ex:
#LUN PATH INFORMATION FOR LUN : /dev/pt/pt2
#World Wide Identifier(WWID) =
#LUN PATH INFORMATION FOR LUN : /dev/rdisk/disk5
#World Wide Identifier(WWID) = 0x60a98000572d44745634645076556357
#LUN PATH INFORMATION FOR LUN : /dev/rdisk/disk6
get_lun_map()
{
scsimgr lun_map | egrep '^[[:space:]]*(LUN PATH|World Wide Identifier)' | tr '\=' ':'
}
main()
{
get_lun_map | while read line ; do
descr=$(echo $line | awk -F: '{print $1}')
val=$( echo $line | awk -F: '{print $2}')
case $descr in
LUN*)
if echo $val | grep /dev/rdisk 1>/dev/null; then
DMP=yes
LUN=$val
else
DMP=no
unset LUN
fi
;;
World*)
if [ $DMP = "yes" ]; then
echo "WWID: $val"
get_stats $LUN
fi
;;
*)
echo "Fehler:"
echo $line
echo $descr
echo $val
sleep 1
;;
esac
done
}
# Verify the system is using new multipath device model.
if [ -d /dev/rdisk ] && [ -d /dev/disk ]; then
echo '<<<hpux_lunstats:sep(58)>>>'
main
fi

View File

@ -0,0 +1,49 @@
#!/bin/sh
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# this is for users who compiled statgrab on hp-ux.
# note you'll need a 0.18+ version, from their github page at
# https://github.com/i-scream/libstatgrab
# flags used for compiling - disable documentation, examples and set*id
if which statgrab > /dev/null ; then
if statgrab const. cpu. general. mem. page. proc. swap. user. > /tmp/statgrab.$$ 2>/dev/null
then
for s in proc cpu page
do
echo "<<<statgrab_$s>>>"
cat /tmp/statgrab.$$ | grep "^$s\." | cut -d. -f2-99 | sed 's/ *= */ /'
done
echo '<<<statgrab_mem>>>'
cat /tmp/statgrab.$$ | egrep "^(swap|mem)\." | sed 's/ *= */ /'
echo '<<<uptime>>>'
cat /tmp/statgrab.$$ | egrep "^general\.uptime" | sed 's/.* //'
fi
[ -f /tmp/statgrab.$$ ] && rm -f /tmp/statgrab.$$
fi

View File

@ -0,0 +1,85 @@
#!/usr/bin/python
# Monitor leases if ISC-DHCPD
import os, sys, time, re, calendar
conf_file = None
for path in [ '/etc/dhcpd.conf', '/etc/dhcp/dhcpd.conf', '/usr/local/etc/dhcpd.conf' ]:
if os.path.exists(path):
conf_file = path
break
leases_file = None
for path in [
'/var/lib/dhcp/db/dhcpd.leases',
'/var/lib/dhcp/dhcpd.leases',
'/var/lib/dhcpd/dhcpd.leases', # CentOS
]:
if os.path.exists(path):
leases_file = path
break
# If no configuration and leases are found, we assume that
# no dhcpd is running.
if not conf_file or not leases_file:
sys.exit(0)
pidof_dhcpd = os.popen("pidof dhcpd").read().strip()
sys.stdout.write('<<<isc_dhcpd>>>\n[general]\nPID: %s\n' % pidof_dhcpd)
sys.stdout.write('[pools]\n')
def parse_config(filename):
for line in file(filename):
line = line.strip()
if line.startswith("include"):
included_file = re.search('include\s+"(.*)"', line).group(1)
parse_config(included_file)
elif line.startswith("range"):
sys.stdout.write(line[5:].strip("\t ;") + "\n")
parse_config(conf_file)
# lease 10.1.1.81 {
# starts 3 2015/09/09 11:42:20;
# ends 3 2015/09/09 19:42:20;
# tstp 3 2015/09/09 19:42:20;
# cltt 3 2015/09/09 11:42:20;
# binding state free;
# hardware ethernet a4:5e:60:de:1f:c3;
# uid "\001\244^`\336\037\303";
# set ddns-txt = "318c69bae8aeae6f8c723e96de933c7149";
# set ddns-fwd-name = "Sebastians-MBP.dhcp.mathias-kettner.de";
# }
sys.stdout.write('[leases]\n')
now = time.time()
ip_address = None
binding_state = None
seen_addresses = set()
for line in file(leases_file):
parts = line.strip().rstrip(";").split()
if not parts:
continue
if parts[0] == "lease":
ip_address = parts[1]
elif parts[0] == "ends":
if parts[1] != "never":
ends_date_string = parts[2] + " " + parts[3]
ends_date = calendar.timegm(time.strptime(ends_date_string, "%Y/%m/%d %H:%M:%S"))
if ends_date < now:
ip_address = None # skip this address, this lease is outdated
elif parts[0] == "binding" and parts[1] == "state":
binding_state = parts[2]
elif parts[0] == "}":
if ip_address and binding_state == "active" and ip_address not in seen_addresses:
sys.stdout.write("%s\n" % ip_address)
seen_addresses.add(ip_address)
ip_address = None
binding_state = None

View File

@ -0,0 +1,52 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# This agent uses the program "jarsigner" to read ssl certificate
# information of jar files and outputs the information to stdout
# for the Check_MK check.
# We assume that all files in the jar archive are signed with the
# same certificate. So we only deal with the last signed file here.
JAVA_HOME=/home/oracle/bin/jdk_latest_version
JAR_PATH=/home/oracle/fmw/11gR2/as_1/forms/java/*.jar
# Let user override these defaults in a configuration file
if [ -e $MK_CONFDIR/jar_signature.cfg ] ; then
. $MK_CONFDIR/jar_signature.cfg
fi
PATH=$JAVA_HOME/bin:$PATH
echo "<<<jar_signature>>>"
for JAR in $JAR_PATH; do
if [ -e "$JAR" ] ; then # avoid entry for '*.jar'
echo "[[[${JAR##*/}]]]"
OUTPUT=$(jarsigner -verify -verbose -certs "$JAR")
LINE=$(echo "$OUTPUT" | grep -n ^s | tail -n1 | cut -d: -f1)
echo "$(echo "$OUTPUT" | tail -n +$LINE)"
echo
fi
done

View File

@ -0,0 +1,38 @@
#!/bin/sh
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
if [ -f /opt/kaspersky/kav4fs/bin/kav4fs-control ]
then
echo "<<<kaspersky_av_updates:sep(58)>>>"
/opt/kaspersky/kav4fs/bin/kav4fs-control --get-stat Update
echo "<<<kaspersky_av_quarantine:sep(58)>>>"
/opt/kaspersky/kav4fs/bin/kav4fs-control -Q --get-stat
echo "<<<kaspersky_av_tasks>>>"
/opt/kaspersky/kav4fs/bin/kav4fs-control --get-task-list
fi

View File

@ -0,0 +1,40 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
if type repquota >/dev/null ; then
echo "<<<lnx_quota>>>"
# User Quota
for VOL in $(grep -E usr[j]?quota /etc/fstab | tr -s '\t' ' ' | cut -d' ' -f2); do
echo "[[[usr:$VOL]]]"
repquota -up $VOL | tail -n +6 | head -n -2
done
# Group Quota
for VOL in $(grep -E grp[j]?quota /etc/fstab | tr -s '\t' ' ' | cut -d' ' -f2); do
echo "[[[grp:$VOL]]]"
repquota -gp $VOL | tail -n +6 | head -n -2
done
fi

View File

@ -0,0 +1,9 @@
#!/bin/bash
echo "<<<lvm_vgs>>>"
vgs --units b --nosuffix --noheadings --separator ' '
echo "<<<lvm_lvs:sep(124)>>>"
lvs --units b --nosuffix --noheadings --separator '|'
#echo "<<<lvm_pvs>>>"
#pvs --units b --nosuffix --noheadings --separator ' '

View File

@ -0,0 +1,55 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# This Check_MK-Agent plugin gathers information about mailinglists hosted
# by the local mailman instance.
# Needed if you have located your mailman python modules not in default
# python module paths
import sys
sys.path.append("/usr/local/mailman")
sys.path.append("/usr/lib/mailman")
# Set to True to filter out all "hidden" mailinglists
only_advertised = True
from Mailman import Utils, MailList # pylint: disable=import-error
# 1. list memberships
sys.stdout.write('<<<mailman_lists>>>\n')
total_members = set([])
for name in sorted(Utils.list_names()):
mlist = MailList.MailList(name, lock=0)
if only_advertised and not mlist.advertised:
continue
rmembers = mlist.getRegularMemberKeys()
dmembers = mlist.getDigestMemberKeys()
members = rmembers + dmembers
total_members.update(members)
sys.stdout.write('%s %d\n' % (name, len(members)))
sys.stdout.write('TOTAL %d\n' % len(total_members))

View File

@ -0,0 +1,65 @@
#!/bin/bash
# Check for APT updates (Debian, Ubuntu)
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# TODO:
# Einstellungen:
# - upgrade oder dist-upgrade
# - vorher ein update machen
# Bakery:
# - Bakelet anlegen
# - Async-Zeit einstellbar machen und das Ding immer async laufen lassen
# Check programmieren:
# * Schwellwerte auf Anzahlen
# * Regexen auf Pakete, die zu CRIT/WARN führen
# - Graph malen mit zwei Kurven
# This variable can either be "upgrade" or "dist-upgrade"
UPGRADE=upgrade
DO_UPDATE=yes
function check_apt_update {
if [ "$DO_UPDATE" = yes ] ; then
# NOTE: Even with -qq, apt-get update can output several lines to
# stderr, e.g.:
#
# W: There is no public key available for the following key IDs:
# 1397BC53640DB551
apt-get update -qq 2> /dev/null
fi
apt-get -o 'Debug::NoLocking=true' -o 'APT::Get::Show-User-Simulation-Note=false' -s -qq "$UPGRADE" | grep -v '^Conf'
}
if type apt-get > /dev/null ; then
echo '<<<apt:sep(0)>>>'
out=$(check_apt_update)
if [ -z "$out" ]; then
echo "No updates pending for installation"
else
echo "$out"
fi
fi

View File

@ -0,0 +1,44 @@
#!/bin/bash
# Check for APT updates (Debian, Ubuntu)
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2017 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Config file must contain:
# USER=client.admin
# KEYRING=/etc/ceph/ceph.client.admin.keyring
if [ -e "$MK_CONFDIR/ceph.cfg" ]; then
. $MK_CONFDIR/ceph.cfg
fi
if [ ! -z "$USER" ] && [ ! -z "$KEYRING" ]; then
CEPH_CMD="ceph -n $USER --keyring=$KEYRING"
echo "<<<ceph_status>>>"
$CEPH_CMD -s -f json-pretty
echo "<<<ceph_df>>>"
$CEPH_CMD df detail
fi

View File

@ -0,0 +1,54 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2017 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
if type lpstat > /dev/null 2>&1 ; then
export LC_TIME="en_US.UTF-8"
echo "<<<cups_queues>>>"
CPRINTCONF=/etc/cups/printers.conf
if [ -r "$CPRINTCONF" ] ; then
LOCAL_PRINTERS=$(grep -E "<(Default)?Printer .*>" $CPRINTCONF | awk '{print $2}' | sed -e 's/>//')
lpstat -p | while read LINE
do
PRINTER=$(echo "$LINE" | awk '{print $2}')
if echo "$LOCAL_PRINTERS" | grep -q "$PRINTER"; then
echo "$LINE"
fi
done
echo '---'
lpstat -o | while read LINE
do
PRINTER=${LINE%%-*}
if echo "$LOCAL_PRINTERS" | grep -q "$PRINTER"; then
echo "$LINE"
fi
done
else
PRINTER=$(lpstat -p)
echo "$PRINTER"
echo '---'
QUEUE=$(lpstat -o | sort)
echo "$QUEUE"
fi
fi

View File

@ -0,0 +1,231 @@
#!/usr/bin/ksh
# Monitor DB/2 databases on AIX
# $HOME/sqllib/db2profile
# This script can be called in two ways
# Without any arguments:
# Checks if cache of the instances is up to date and starts the
# command 'mk_db.aix query {instance}' if applicable
# If its outdated the script calls itself with the argument 'query'
# With 'query {instance}' as argument:
# Does the actual queries to the db2 instance and writes this info
# into the cache file
# Each instance has its own cache file and all of them are filled in parallel
if [ ! "$MK_CONFDIR" ] ; then
echo "MK_CONFDIR not set!" >&2
exit 1
fi
if [ ! "$MK_VARDIR" ] ; then
export MK_VARDIR=$MK_CONFDIR
fi
function waitmax
{
TIMEOUT=${1}0
SIGNAL=9
shift
# Run command in background
if [ "${#}" -ge 1 ] ; then
ksh -c "$*" &
else
TEST=$(cat)
ksh -c "$TEST" &
fi
PID=$!
# Wait for termination within TIMOUT seconds
while [ $TIMEOUT -gt 0 ]
do
TIMEOUT=$((TIMEOUT - 1))
if [ ! -e /proc/$PID ] ; then
return 0
fi
perl -e "select(undef, undef, undef, 0.1);"
done
# Process did not terminate in time. Kill and
# return with an error
kill -9 $PID
return 255
}
function query_instance {
INSTANCE=$1
# find home directory
HOMEDIR=$(grep "^$INSTANCE" /etc/passwd | awk -F: '{print $6}' | grep "$INSTANCE$")
NOW=$(perl -e "print time();")
waitmax 200 << WAITMAX
su $INSTANCE << EOF
if [ ! -f $HOMEDIR/sqllib/db2profile ] ;
then
exit 0
fi
. $HOMEDIR/sqllib/db2profile >/dev/null 2>&1 ;
function compare_version_greater_equal {
GREATER_ONE=\\\$(echo "\\\$1 \\\$2" | awk "{if (\\\$1 >= \\\$2) print \\\$1; else print \\\$2}")
if [ \\\$GREATER_ONE == \\\$1 ] ; then
return 0
else
return 1
fi
}
echo '<<<db2_version:sep(1)>>>'
DBVERSION=\\\$(db2 get snapshot for dbm | grep -e 'Product name' -e 'Service level' | awk -v FS='=' '{print \\\$2}' | sed 'N;s/\n/,/g' | sed 's/ //g')
echo $INSTANCE \\\$DBVERSION
VERSION_NUMBER=\\\$(echo \\\$DBVERSION | sed -e 's/DB2v\\\(.*\),.*/\\\1/' | awk -v FS="." '{print \\\$1"."\\\$2}')
DBS=\\\$(db2 list database directory on $HOMEDIR | grep 'Database name' | awk '{ print \\\$NF }')
GET_PORT=1
DB_PORT='port 0'
for DB in \\\$DBS; do
db2 connect to \\\$DB > /dev/null;
if [ $? -nq 0 ] ; then
exit 1
fi
if [ 1 -eq \\\$GET_PORT ] ; then
# Each database in an instance has the same port information
db2_tcp_service=\\\$(db2 -x get dbm cfg | grep $INSTANCE | grep "TCP/IP Service" | awk -v FS='=' '{print \\\$2}'|tr -d ' ')
if ( grep \\\$db2_tcp_service /etc/services | grep -q "^\\\$db2_tcp_service " ); then
DB_PORT='port '\\\$(grep \\\$db2_tcp_service /etc/services | grep "^\\\$db2_tcp_service " | awk '{print \\\$2}' | awk -v FS="/" '{print \\\$1}')
fi
GET_PORT=0
fi
echo "<<<db2_tablespaces>>>"
echo "[[[$INSTANCE:\\\$DB]]]"
db2 "SELECT tbsp_name, tbsp_type, tbsp_state, tbsp_usable_size_kb, tbsp_total_size_kb, tbsp_used_size_kb, tbsp_free_size_kb FROM sysibmadm.tbsp_utilization WHERE tbsp_type = 'DMS' UNION ALL SELECT tu.tbsp_name, tu.tbsp_type, tu.tbsp_state, tu.tbsp_usable_size_kb, tu.tbsp_total_size_kb, tu.tbsp_used_size_kb, (cu.fs_total_size_kb - cu.fs_used_size_kb) AS tbsp_free_size_kb FROM sysibmadm.tbsp_utilization tu INNER JOIN ( SELECT tbsp_id, 1 AS fs_total_size_kb, 0 AS fs_used_size_kb FROM sysibmadm.container_utilization WHERE (fs_total_size_kb IS NULL OR fs_used_size_kb IS NULL) GROUP BY tbsp_id) cu ON (tu.tbsp_type = 'SMS' AND tu.tbsp_id = cu.tbsp_id) UNION ALL SELECT tu.tbsp_name, tu.tbsp_type, tu.tbsp_state, tu.tbsp_usable_size_kb, tu.tbsp_total_size_kb, tu.tbsp_used_size_kb, (cu.fs_total_size_kb - cu.fs_used_size_kb) AS tbsp_free_size_kb FROM sysibmadm.tbsp_utilization tu INNER JOIN ( SELECT tbsp_id, SUM(fs_total_size_kb) AS fs_total_size_kb, SUM(fs_used_size_kb) AS fs_used_size_kb FROM sysibmadm.container_utilization WHERE (fs_total_size_kb IS NOT NULL AND fs_used_size_kb IS NOT NULL) GROUP BY tbsp_id) cu ON (tu.tbsp_type = 'SMS' AND tu.tbsp_id = cu.tbsp_id)" | awk '{print \\\$1" "\\\$2" "\\\$3" "\\\$4" "\\\$5" "\\\$6" "\\\$7}' | sed -e '/^[ ]*$/d' -e '/^-/d' -e '/selected/d'
echo "<<<db2_counters>>>"
echo "TIMESTAMP $NOW"
cat \\\$(db2 get dbm cfg|grep "Default database path"|awk -v FS="=" '{print \\\$2"/sqllib/db2nodes.cfg"}'|tr -d ' ') | sed "s/\(.*\)/$INSTANCE:\\\$DB node \1/"
db2 -x "SELECT deadlocks from sysibmadm.snapdb" | tr -d ' ' | sed "s/\(.*\)/$INSTANCE:\\\$DB deadlocks \1/"
db2 -x "SELECT lock_waits from sysibmadm.snapdb" | tr -d ' ' | sed "s/\(.*\)/$INSTANCE:\\\$DB lockwaits \1/"
db2 -x "SELECT sort_overflows from sysibmadm.snapdb" | tr -d ' ' | sed "s/\(.*\)/$INSTANCE:\\\$DB sortoverflows \1/"
echo "<<<db2_logsizes>>>"
echo "[[[$INSTANCE:\\\$DB]]]"
echo "TIMESTAMP $NOW"
cat \\\$(db2 get dbm cfg|grep "Default database path"|awk -v FS="=" '{print \\\$2"/sqllib/db2nodes.cfg"}'|tr -d ' ') | sed 's/\(.*\)/node \1/'
db2 -x "SELECT 'usedspace', total_log_used from sysibmadm.snapdb" | awk '{print \\\$1" "\\\$2}'
db2 -x "SELECT NAME, VALUE FROM SYSIBMADM.DBCFG WHERE NAME IN ('logfilsiz','logprimary','logsecond')"| awk '{print \\\$1" "\\\$2}'
echo "<<<db2_connections>>>"
echo "[[[$INSTANCE:\\\$DB]]]"
echo \\\$DB_PORT
echo "connections " | tr -d '\n'
db2 list applications | grep -v Auth | grep -v Name | sed -e '/^$/d' | wc -l | tr -d ' '
# TODO: the time command seems to be broken and outputs 1 second steps
ksh -c "time db2 connect to \\\$DB > /dev/null" 2>&1 | grep real | awk '{print "latency "\\\$2}'| sed -e 's/m/:/' -e 's/s//'
echo "<<<db2_bp_hitratios>>>"
echo "[[[$INSTANCE:\\\$DB]]]"
cat \\\$(db2 get dbm cfg|grep "Default database path"|awk -v FS="=" '{print \\\$2"/sqllib/db2nodes.cfg"}'|tr -d ' ') | sed "s/\(.*\)/node \1/"
db2 "SELECT SUBSTR(BP_NAME,1,14) AS BP_NAME, TOTAL_HIT_RATIO_PERCENT, DATA_HIT_RATIO_PERCENT, INDEX_HIT_RATIO_PERCENT, XDA_HIT_RATIO_PERCENT FROM SYSIBMADM.BP_HITRATIO" | grep -v "selected." | sed -e '/^$/d' -e '/^-/d'
echo "<<<db2_sort_overflow>>>"
echo "[[[$INSTANCE:\\\$DB]]]"
db2 -x "get snapshot for database on \\\$DB" | grep -e "^Total sorts" -e "^Sort overflows" | tr -d '='
echo "<<<db2_backup>>>"
echo "[[[$INSTANCE:\\\$DB]]]"
if compare_version_greater_equal \\\$VERSION_NUMBER 10.5; then
# MON_GET_DATBASE(-2) gets information of all active members
db2 -x "select LAST_BACKUP from TABLE (MON_GET_DATABASE(-2))" | grep -v "selected." | tail -n 1
else
db2 -x "select SQLM_ELM_LAST_BACKUP from table(SNAPSHOT_DATABASE( cast( null as VARCHAR(255)), cast(null as int))) as ref" | grep -v "selected." | tail -n 1
fi
# disconnect from database
db2 connect reset > /dev/null
done
EOF
WAITMAX
return $?
}
if [ "$1" = "query" ]; then
query_instance $2
exit $?
else
#### RUN CACHED #####
function file_age {
/usr/bin/perl -e 'if (! -f $ARGV[0]){die "0000000"};$mtime=(stat($ARGV[0]))[9];print ($^T-$mtime);' "$1"
}
if [ ! -d $MK_VARDIR/cache ]; then mkdir -p $MK_VARDIR/cache ; fi
if [ -e "$MK_VARDIR/cache/mk_db2.aix.cache" ] ; then
rm $MK_VARDIR/cache/mk_db2.aix.cache
fi
INSTANCES=$(ps -ef | grep [d]b2sysc | awk '{print $1 }')
# Output any section headers
# If no data is available there will be at least the section headers
# This happens when a database is down. In this scenario the db2_version check
# should go CRIT and the other checks go stale
echo "<<<db2_version:sep(1)>>>"
echo "<<<db2_tablespaces>>>"
echo "<<<db2_counters>>>"
echo "<<<db2_logsizes>>>"
echo "<<<db2_connections>>>"
echo "<<<db2_bp_hitratios>>>"
echo "<<<db2_sort_overflow>>>"
echo "<<<db2_backup>>>"
for INSTANCE in $INSTANCES; do
CACHEFILE="$MK_VARDIR/cache/mk_db2.aix.cache.$INSTANCE"
MAXAGE=300
# Check if the creation of the cache takes way to long and delete this file
# The process might have crashed...
# Since the processes are called with waitmax it is very unlikely that
# there are still unwanted processes soiling the system.
if [ -e "$CACHEFILE.new" ] ; then
AGE=$(file_age "$CACHEFILE.new")
if [ $AGE -ge $((MAXAGE * 10)) ] ; then
rm "$CACHEFILE.new"
fi
fi
# Check if the creation of the cache takes suspiciously long and return
# nothing if the age (access time) of $CACHEFILE.new is twice the MAXAGE
if [ -e "$CACHEFILE.new" ] ; then
AGE=$(file_age "$CACHEFILE.new")
if [ $AGE -ge $((MAXAGE * 2)) ] ; then
return
fi
fi
# Check if cache file exists and is recent enough
USE_CACHEFILE=""
if [ -s "$CACHEFILE" ] ; then
AGE=$(file_age "$CACHEFILE")
if [ $AGE -le $MAXAGE ] ; then USE_CACHEFILE=1 ; fi
# Output the file in any case, even if it is
# outdated. The new file will not yet be available
cat "$CACHEFILE"
fi
# Cache file outdated and new job not yet running? Start it
if [ -z "$USE_CACHEFILE" -a ! -e "$CACHEFILE.new" ] ; then
echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; ./$0 query $INSTANCE && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup ksh 2>/dev/null &
fi
done
fi
exit 0

View File

@ -0,0 +1,147 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2018 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Agent plugin to monitor DB/2 databases on Linux.
#
# Note: The script has to be accessible and executable by the DB/2
# instance users to function properly.
#
# The script is called in two different modes. Without arguments it:
#
# - outputs the db2_version section,
# - collects all db2 instances and databases, and
# - runs the script for each database as an instance user
#
# With arguments the script queries the information for each database and
# outputs the corresponding sections.
if [ $# -eq 0 ]; then
if type timeout >/dev/null 2>&1 ; then
function waitmax () {
timeout "$@"
}
fi
INSTANCES=$(ps -ef | grep "[d]b2sysc" | awk '{print $1 }')
for INSTANCE in $INSTANCES; do
NOW=$(perl -e "print time();")
echo ""
echo '<<<db2_version:sep(1)>>>'
DBVERSION=$(su - "${INSTANCE}" -c "db2 get snapshot for dbm" | grep -e 'Product name' -e 'Service level' | awk -v FS='=' '{print $2}' | sed 'N;s/\n/,/g' | sed 's/ //g')
echo "$INSTANCE" "$DBVERSION"
VERSION_NUMBER=$(echo "$DBVERSION" | sed -e 's/DB2v\(.*\),.*/\1/' | awk -v FS="." '{print $1"."$2}')
DBS=$(su - "${INSTANCE}" -c "db2 list active databases" | grep 'Database name' | awk '{ print $NF }')
DB_PORT='port 0'
GET_PORT=1
for DB in $DBS; do
if [ 1 -eq $GET_PORT ] ; then
# Each database in an instance has the same port information
db2_tcp_service=$(su - "${INSTANCE}" -c "db2 -x get dbm cfg" | grep "TCP/IP Service" | awk -v FS='=' '{print $2}' | tr -d ' ')
if ( grep "$db2_tcp_service" /etc/services | grep -q "^$db2_tcp_service " ); then
DB_PORT='port '$(grep "$db2_tcp_service" /etc/services | grep "^$db2_tcp_service " | awk '{print $2}' | awk -v FS="/" '{print $1}')
fi
GET_PORT=0
fi
SCRIPT=$(readlink -f "$0")
waitmax -s 9 10 su - "${INSTANCE}" -c "\"${SCRIPT}\" \"${INSTANCE}\" \"${DB}\" \"${VERSION_NUMBER}\" \"${NOW}\" \"${DB_PORT}\""
done
done
else
INSTANCE=$1
DB=$2
VERSION_NUMBER=$3
NOW=$4
DB_PORT=$5
function compare_version_greater_equal {
GREATER_ONE=$(echo "$1 $2" | awk "{if ($1 >= $2) print $1; else print $2}")
if [ "$GREATER_ONE" == "$1" ] ; then
return 0
else
return 1
fi
return 0
}
millis_before=$(date +"%s%3N")
if db2 +o connect to "$DB"; then
millis_after=$(date +"%s%3N")
millis_diff=$(( millis_after - millis_before ))
echo "<<<db2_connections>>>"
echo "[[[$INSTANCE:$DB]]]"
echo "$DB_PORT"
echo "connections " | tr -d '\n'
db2 -x "SELECT count(*)-1 FROM TABLE(mon_get_connection(CAST(NULL AS BIGINT), -2)) AS t"
echo "latency ${millis_diff}"
echo "<<<db2_tablespaces>>>"
echo "[[[$INSTANCE:$DB]]]"
SQL="SELECT tbsp_name, tbsp_type, tbsp_state, tbsp_usable_size_kb, tbsp_total_size_kb, tbsp_used_size_kb, tbsp_free_size_kb FROM sysibmadm.tbsp_utilization WHERE tbsp_type = 'DMS' UNION ALL SELECT tu.tbsp_name, tu.tbsp_type, tu.tbsp_state, tu.tbsp_usable_size_kb, tu.tbsp_total_size_kb, tu.tbsp_used_size_kb, (cu.fs_total_size_kb - cu.fs_used_size_kb) AS tbsp_free_size_kb FROM sysibmadm.tbsp_utilization tu INNER JOIN ( SELECT tbsp_id, 1 AS fs_total_size_kb, 0 AS fs_used_size_kb FROM sysibmadm.container_utilization WHERE (fs_total_size_kb IS NULL OR fs_used_size_kb IS NULL) GROUP BY tbsp_id) cu ON (tu.tbsp_type = 'SMS' AND tu.tbsp_id = cu.tbsp_id) UNION ALL SELECT tu.tbsp_name, tu.tbsp_type, tu.tbsp_state, tu.tbsp_usable_size_kb, tu.tbsp_total_size_kb, tu.tbsp_used_size_kb, (cu.fs_total_size_kb - cu.fs_used_size_kb) AS tbsp_free_size_kb FROM sysibmadm.tbsp_utilization tu INNER JOIN ( SELECT tbsp_id, SUM(fs_total_size_kb) AS fs_total_size_kb, SUM(fs_used_size_kb) AS fs_used_size_kb FROM sysibmadm.container_utilization WHERE (fs_total_size_kb IS NOT NULL AND fs_used_size_kb IS NOT NULL) GROUP BY tbsp_id) cu ON (tu.tbsp_type = 'SMS' AND tu.tbsp_id = cu.tbsp_id)"
db2 "${SQL}" | awk '{print $1" "$2" "$3" "$4" "$5" "$6" "$7}' | sed -e '/^[ ]*$/d' -e '/^-/d' -e '/selected/d'
echo "<<<db2_counters>>>"
echo "TIMESTAMP $NOW"
echo "$INSTANCE:$DB deadlocks " | tr -d '\n'
db2 -x "SELECT deadlocks from sysibmadm.snapdb" | tr -d ' '
echo "$INSTANCE:$DB lockwaits " | tr -d '\n'
db2 -x "SELECT lock_waits from sysibmadm.snapdb" | tr -d ' '
echo "$INSTANCE:$DB sortoverflows " | tr -d '\n'
db2 -x "SELECT sort_overflows from sysibmadm.snapdb" | tr -d ' '
echo "<<<db2_logsizes>>>"
echo "TIMESTAMP $NOW"
echo "[[[$INSTANCE:$DB]]]"
echo "usedspace " | tr -d '\n'
db2 -x "SELECT total_log_used from sysibmadm.snapdb" | tr -d ' '
db2 -x "SELECT NAME, VALUE FROM SYSIBMADM.DBCFG WHERE NAME IN ('logfilsiz','logprimary','logsecond')"| awk '{print $1" "$2}'
echo "<<<db2_bp_hitratios>>>"
echo "[[[$INSTANCE:$DB]]]"
db2 "SELECT SUBSTR(BP_NAME,1,14) AS BP_NAME, TOTAL_HIT_RATIO_PERCENT, DATA_HIT_RATIO_PERCENT, INDEX_HIT_RATIO_PERCENT, XDA_HIT_RATIO_PERCENT FROM SYSIBMADM.BP_HITRATIO" | grep -v "selected." | sed -e '/^$/d' -e '/^-/d'
echo "<<<db2_sort_overflow>>>"
echo "[[[$INSTANCE:$DB]]]"
db2 -x "get snapshot for database on $DB" | grep -e "^Total sorts" -e "^Sort overflows" | tr -d '='
echo "<<<db2_backup>>>"
echo "[[[$INSTANCE:$DB]]]"
if compare_version_greater_equal "$VERSION_NUMBER" 10.5; then
# MON_GET_DATBASE(-2) gets information of all active members
db2 -x "select LAST_BACKUP from TABLE (MON_GET_DATABASE(-2))" | grep -v "selected." | tail -n 1
else
db2 -x "select SQLM_ELM_LAST_BACKUP from table(SNAPSHOT_DATABASE( cast( null as VARCHAR(255)), cast(null as int))) as ref" | grep -v "selected." | tail -n 1
fi
# disconnect from database
db2 connect reset > /dev/null
fi
fi
exit 0

View File

@ -0,0 +1,55 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2018 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# $REMOTE is exported from check_mk_agent.linux
if type docker > /dev/null 2>&1; then
NODE_NAME=$(docker info --format "{{json .Name}}")
# For the container status, we want information about *all* containers
for CONTAINER_ID in $(docker container ls -q --all); do
echo "<<<<${CONTAINER_ID}>>>>"
docker inspect "$CONTAINER_ID" \
--format='{{println "<<<docker_container_status>>>"}}{{json .State}}{{println}}{{println "<<<docker_container_node_name>>>"}}{{println '"$NODE_NAME"'}}{{println "<<<docker_container_labels>>>"}}{{json .Config.Labels}}{{println}}{{println "<<<docker_container_network>>>"}}{{json .NetworkSettings}}{{println}}'
if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_ID")" = "true" ]; then
# Is there a regular agent available in the container? Use it!
#
# Otherwise execute the agent of the node in the context of the container.
# Using this approach we should always get at least basic information from
# the container.
# Once it comes to plugins and custom configuration the user needs to use
# a little more complex setup. Have a look at the documentation.
AGENT_PATH=$(docker container exec "$CONTAINER_ID" bash -c "type check_mk_agent" 2>/dev/null) || AGENT_PATH=
if [ -n "$AGENT_PATH" ]; then
docker container exec --env "REMOTE=$REMOTE" "$CONTAINER_ID" check_mk_agent
elif docker container exec "$CONTAINER_ID" which bash >/dev/null 2>&1; then
docker container exec --env MK_FROM_NODE=1 --env "REMOTE=$REMOTE" -i "$CONTAINER_ID" bash < "$0"
fi
fi
echo "<<<<>>>>"
done
fi

View File

@ -0,0 +1,33 @@
#!/bin/bash
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
FILE=/proc/sys/fs/file-nr
echo '<<<filehandler>>>'
if [ -a $FILE ]; then
cat $FILE
fi

View File

@ -0,0 +1,5 @@
if [ -r /var/run/haproxy.stat ]; then
echo "<<<haproxy:sep(44)>>>"
echo "show stat" | socat - UNIX-CONNECT:/var/run/haproxy.sock
fi

View File

@ -0,0 +1,296 @@
#!/bin/bash
# Monitor status of LUNs on HP-UX
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Make ENV-VARs avail for subshells
set -a
# .--helper--------------------------------------------------------------.
# | _ _ |
# | | |__ ___| |_ __ ___ _ __ |
# | | '_ \ / _ \ | '_ \ / _ \ '__| |
# | | | | | __/ | |_) | __/ | |
# | |_| |_|\___|_| .__/ \___|_| |
# | |_| |
# '----------------------------------------------------------------------'
function do_check () {
# $1:section, $2:excludelist
if echo "$2" | grep -qe "${1}"; then
return 1
else
return 0
fi
}
function sql () {
db="sysmaster"
sqltxt="$1"
dbaccess_par=
export DBDELIMITER="|"
echo "$sqltxt" | dbaccess ${db}
}
function set_excludes () {
excludes=""
if [ "$EXCLUDES" = "ALL" ]; then
excludes="$all_sections"
global_exclude=true
elif [ ! -z "$EXCLUDES" ]; then
excludes=$EXCLUDES
global_exclude=true
else
global_exclude=false
fi
if [ "$global_exclude" = "false" ]; then
excludes_i="EXCLUDES_${1}"
if [ "${!excludes_i}" = "ALL" ]; then
excludes="$all_sections"
elif [ ! -z "${!excludes_i}" ]; then
excludes=${!excludes_i}
fi
fi
}
#.
# .--sqls----------------------------------------------------------------.
# | _ |
# | ___ __ _| |___ |
# | / __|/ _` | / __| |
# | \__ \ (_| | \__ \ |
# | |___/\__, |_|___/ |
# | |_| |
# '----------------------------------------------------------------------'
all_sections="sessions locks tabextents dbspaces logusage"
function informix_status(){
echo "<<<informix_status:sep(58)>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
$INFORMIXDIR/bin/onstat - >/dev/null 2>&1
state=$?
echo "Status:"$state
$INFORMIXDIR/bin/onstat -g dis
port=$(grep $INFORMIXSERVER /etc/services)
echo "PORT:"$port
}
function informix_sessions(){
echo "<<<informix_sessions>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
# don't count our own session
sql "select 'SESSIONS', (count(*)-1)::int from syssessions"
}
function informix_locks(){
echo "<<<informix_locks>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
# don't count our own session
sql "select 'LOCKS', (count(*)-1)::int, type from syslocks group by type"
}
function informix_tabextents(){
echo "<<<informix_tabextents>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
sql "select first 10
'TABEXTENTS',
trim(n.dbsname) db,
trim(n.tabname) tab,
h.nextns extents,
nrows
from sysptnhdr h, systabnames n
where h.partnum = n.partnum
and nrows > 0
and n.dbsname not in ( 'sysadmin', 'sysuser', 'sysutils', 'sysmaster' )
and n.tabname not like 'sys%'
order by extents desc"
}
function informix_dbspaces(){
echo "<<<informix_dbspaces>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
sql "select
trim(sd.name) || ' DBSPACE',
sd.dbsnum,
sd.is_temp,
sd.flags,
'CHUNK',
sc.fname,
sc.pagesize,
sc.chksize,
sc.nfree,
sc.flags,
trim(sc.mfname),
sc.mflags
from sysdbspaces sd, syschunks sc
where sd.dbsnum = sc.dbsnum
-- NO SBSPACE CURRENTLY
and sd.is_sbspace = 0
order by sd.name"
}
function informix_logusage(){
echo "<<<informix_logusage>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
sql "select 'LOGUSAGE',
number,
sh_pagesize,
size,
used,
flags,
'is_used:'||is_used,
'is_current:'||is_current,
'is_backed_up:'||is_backed_up,
'is_new:'||is_new,
'is_archived:'||is_archived,
'is_temp:'||is_temp,
'is_pre_dropped:'||is_pre_dropped
from syslogs, sysshmvals
order by number"
}
#.
# .--config--------------------------------------------------------------.
# | __ _ |
# | ___ ___ _ __ / _(_) __ _ |
# | / __/ _ \| '_ \| |_| |/ _` | |
# | | (_| (_) | | | | _| | (_| | |
# | \___\___/|_| |_|_| |_|\__, | |
# | |___/ |
# '----------------------------------------------------------------------'
# Config opts:
# - oninit-path; Default is empty, which means autodetection:
# ONINIT_PATH=<path to oninit-binary>
# - Excluding sections ("status sessions locks tabextents dbspaces logusage"):
# EXCLUDES_INFORMIX_INSTANCE="SECTION SECTION ..."
# EXCLUDES_INFORMIX_INSTANCE=ALL
# EXCLUDES="SECTION SECTION ..."
# EXCLUDES=ALL
if [ -f "$MK_CONFDIR/informix.cfg" ]; then
. $MK_CONFDIR/informix.cfg
fi
if [ -z "$ONINIT_PATH" -o ! -x "$ONINIT_PATH" ]; then
ONINIT=$(UNIX95=true ps ax | grep oninit | grep -v grep | head -1 | awk '{print $1 " " $5}')
if [ -z "$ONINIT" ]; then
exit 0
fi
ONINIT_PATH=${ONINIT#* }
ONINIT_PID=${ONINIT% *}
case "$ONINIT_PATH" in
/*)
;;
*) # BUG not platform independent!
ONINIT_PATH=$(ls -l /proc/$ONINIT_PID/exe 2>/dev/null| sed 's/.* //')
;;
esac
# If not set in config or not found we end up here
if [ -z "$ONINIT_PATH" -o ! -f "$ONINIT_PATH" ]; then
exit 1
fi
fi
#.
# .--main----------------------------------------------------------------.
# | _ |
# | _ __ ___ __ _(_)_ __ |
# | | '_ ` _ \ / _` | | '_ \ |
# | | | | | | | (_| | | | | | |
# | |_| |_| |_|\__,_|_|_| |_| |
# | |
# '----------------------------------------------------------------------'
for IDSENV in $( export INFORMIXDIR=${ONINIT_PATH%/bin*}
$INFORMIXDIR/bin/onstat -g dis | \
egrep '^Server[ ]*:|^Server Number[ ]*:|^INFORMIX|^SQLHOSTS|^ONCONFIG' | \
sed -e 's/Server Number/SERVERNUM/' \
-e 's/Server/INFORMIXSERVER/' \
-e 's/SQLHOSTS/INFORMIXSQLHOSTS/' \
-e 's/[ ]*:[ ]*/=/' | \
tr '\n' ';' | \
sed -e 's/;$/\n/' -e 's/;\(INFORMIXSERVER=[^;]*;\)/\n\1/g'
) ; do
(
# Set environment
eval $IDSENV
PATH=$INFORMIXDIR/bin:$PATH
# try to set them via 'onstat -g env' otherwise
# DB HAS TO BE RUNNING
if [ -z "$INFORMIXSQLHOSTS" -o -z "$ONCONFIG" ]; then
onstat -g env | egrep -e '^INFORMIXSQLHOSTS' \
-e '^ONCONFIG' | \
sed -e 's/[ ][ ]*/=/'
fi
informix_status
set_excludes $INFORMIXSERVER
if do_check "sessions" "$excludes"; then
informix_sessions
fi
if do_check "locks" "$excludes"; then
informix_locks
fi
if do_check "tabextents" "$excludes"; then
informix_tabextents
fi
if do_check "dbspaces" "$excludes"; then
informix_dbspaces
fi
if do_check "logusage" "$excludes"; then
informix_logusage
fi
)
done

View File

@ -0,0 +1,357 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
import os
import sys
import time
import signal
import ConfigParser
try:
# TODO: We should probably ship this package.
import pyinotify # pylint: disable=import-error
except:
sys.stderr.write("Error: Python plugin pyinotify is not installed")
sys.exit(1)
def usage():
sys.stdout.write("Usage: mk_inotify [-g]\n")
sys.stdout.write(" -g: run in foreground\n\n")
# Available options:
# -g: run in foreground
opt_foreground = False
if len(sys.argv) == 2 and sys.argv[1] == "-g":
opt_foreground = True
mk_confdir = os.getenv("MK_CONFDIR") or "/etc/check_mk"
mk_vardir = os.getenv("MK_VARDIR") or "/var/lib/check_mk_agent"
config_filename = mk_confdir + "/mk_inotify.cfg"
configured_paths = mk_vardir + "/mk_inotify.configured"
pid_filename = mk_vardir + "/mk_inotify.pid"
config = ConfigParser.SafeConfigParser({})
if not os.path.exists(config_filename):
sys.exit(0)
config_mtime = os.stat(config_filename).st_mtime
config.read(config_filename)
# Configurable in Agent Bakery
heartbeat_timeout = config.getint("global", "heartbeat_timeout")
write_interval = config.getint("global", "write_interval")
max_messages_per_interval = config.getint("global", "max_messages_per_interval")
stats_retention = config.getint("global", "stats_retention")
config.remove_section("global")
def output_data():
sys.stdout.write("<<<inotify:sep(9)>>>\n")
if os.path.exists(configured_paths):
sys.stdout.write(file(configured_paths).read())
now = time.time()
for dirpath, _unused_dirnames, filenames in os.walk(mk_vardir):
for filename in filenames:
if filename.startswith("mk_inotify.stats"):
try:
the_file = "%s/%s" % (dirpath, filename)
filetime = os.stat(the_file).st_mtime
file_age = now - filetime
if file_age > 5:
sys.stdout.write(file(the_file).read())
if file_age > stats_retention:
os.unlink(the_file)
except:
pass
break
# Check if another mk_inotify process is already running
if os.path.exists(pid_filename):
pid = file(pid_filename).read()
proc_cmdline = "/proc/%s/cmdline" % pid
if os.path.exists(proc_cmdline):
cmdline = file(proc_cmdline).read()
cmdline_tokens = cmdline.split("\0")
if "mk_inotify" in cmdline_tokens[1]:
# Another mk_notify process is already running..
# Simply output the current statistics and exit
output_data()
# The pidfile is also the heartbeat file for the running process
os.utime(pid_filename, None)
sys.exit(0)
# .--Fork----------------------------------------------------------------.
# | _____ _ |
# | | ___|__ _ __| | __ |
# | | |_ / _ \| '__| |/ / |
# | | _| (_) | | | < |
# | |_| \___/|_| |_|\_\ |
# | |
# +----------------------------------------------------------------------+
# Reaching this point means that no mk_inotify is currently running
if not opt_foreground:
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
# Decouple from parent environment
os.chdir("/")
os.umask(0)
os.setsid()
# Close all fd
for fd in range(0, 256):
try:
os.close(fd)
except OSError:
pass
except Exception, e:
sys.stderr.write("Error forking mk_inotify: %s" % e)
# Save pid of working process.
file(pid_filename, "w").write("%d" % os.getpid())
#.
# .--Main----------------------------------------------------------------.
# | __ __ _ |
# | | \/ | __ _(_)_ __ |
# | | |\/| |/ _` | | '_ \ |
# | | | | | (_| | | | | | |
# | |_| |_|\__,_|_|_| |_| |
# | |
# +----------------------------------------------------------------------+
folder_configs = {} # Computed configuration
output = [] # Data to be written to disk
def get_watched_files():
files = set([])
for folder, attributes in folder_configs.items():
for filenames in attributes["monitor_files"].values():
for filename in filenames:
files.add("configured\tfile\t%s/%s" % (folder, filename))
if attributes.get("monitor_all"):
files.add("configured\tfolder\t%s" % (folder))
return files
def wakeup_handler(signum, frame):
global output
if output:
if opt_foreground:
sys.stdout.write("%s\n" % "\n".join(output))
sys.stdout.write("%s\n" % "\n".join(get_watched_files()))
else:
filename = "mk_inotify.stats.%d" % time.time()
file("%s/%s" % (mk_vardir, filename), "w").write("\n".join(output)+"\n")
output = []
# Check if configuration has changed -> restart
if (config_mtime != os.stat(config_filename).st_mtime):
os.execv(__file__, sys.argv)
# Exit on various instances
if not opt_foreground:
if not os.path.exists(pid_filename): # pidfile is missing
sys.exit(0)
if time.time() - os.stat(pid_filename).st_mtime > heartbeat_timeout: # heartbeat timeout
sys.exit(0)
if os.getpid() != int(file(pid_filename).read()): # pidfile differs
sys.exit(0)
update_watched_folders()
signal.alarm(write_interval)
def do_output(what, event):
if event.dir:
return # Only monitor files
if len(output) > max_messages_per_interval:
last_message = "warning\tMaximum messages reached: %d per %d seconds" % \
(max_messages_per_interval, write_interval)
if output[-1] != last_message:
output.append(last_message)
return
path = event.path
path_config = folder_configs.get(path)
if not path_config:
return # shouldn't happen, maybe on subfolders (not supported)
filename = os.path.basename(event.pathname)
if what in path_config["monitor_all"] or\
filename in path_config["monitor_files"].get(what, []):
line = "%d\t%s\t%s" % (time.time(), what, event.pathname)
if map_events[what][1]: # Check if filestats are enabled
try:
stats = os.stat(event.pathname)
line += "\t%d\t%d" % (stats.st_size, stats.st_mtime)
except Exception:
pass
output.append(line)
if opt_foreground:
sys.stdout.write("%s\n" % line)
map_events = {
# Mode Mask Report_filestats (currently unused)
"access" : (pyinotify.IN_ACCESS, False), # pylint: disable=no-member
"open" : (pyinotify.IN_OPEN, False), # pylint: disable=no-member
"create" : (pyinotify.IN_CREATE, False), # pylint: disable=no-member
"delete" : (pyinotify.IN_DELETE, False), # pylint: disable=no-member
"modify" : (pyinotify.IN_MODIFY, False), # pylint: disable=no-member
"movedto" : (pyinotify.IN_MOVED_TO, False), # pylint: disable=no-member
"movedfrom": (pyinotify.IN_MOVED_FROM, False), # pylint: disable=no-member
"moveself" : (pyinotify.IN_MOVE_SELF, False), # pylint: disable=no-member
}
class NotifyEventHandler(pyinotify.ProcessEvent):
def process_IN_MOVED_TO(self, event):
do_output("movedto", event)
def process_IN_MOVED_FROM(self, event):
do_output("movedfrom", event)
def process_IN_MOVE_SELF(self, event):
do_output("moveself", event)
# def process_IN_CLOSE_NOWRITE(self, event):
# print "CLOSE_NOWRITE event:", event.pathname
#
# def process_IN_CLOSE_WRITE(self, event):
# print "CLOSE_WRITE event:", event.pathname
def process_IN_CREATE(self, event):
do_output("create", event)
def process_IN_DELETE(self, event):
do_output("delete", event)
def process_IN_MODIFY(self, event):
do_output("modify", event)
def process_IN_OPEN(self, event):
do_output("open", event)
# Watch manager
wm = pyinotify.WatchManager()
def update_watched_folders():
for folder, attributes in folder_configs.items():
if attributes.get("watch_descriptor"):
if not wm.get_path(attributes["watch_descriptor"].get(folder)):
del attributes["watch_descriptor"]
else:
if os.path.exists(folder):
new_wd = wm.add_watch(folder, attributes["mask"], rec=True)
if new_wd.get(folder) > 0:
attributes["watch_descriptor"] = new_wd
def main():
# Read config
for section in config.sections():
section_tokens = section.split("|")
folder = section_tokens[0]
folder_configs.setdefault(folder, {"add_modes": {},
"del_modes": {},
"all_add_modes": set([]),
"all_del_modes": set([])})
files = None
if len(section_tokens) > 1:
files = set(section_tokens[1:])
add_modes = set([])
del_modes = set([])
for key, value in config.items(section):
if key in map_events:
if value == "1":
add_modes.add(key)
else:
del_modes.add(key)
if files:
for mode in add_modes:
folder_configs[folder]["add_modes"].setdefault(mode, set([]))
folder_configs[folder]["add_modes"][mode].update(files)
for mode in del_modes:
folder_configs[folder]["del_modes"].setdefault(mode, set([]))
folder_configs[folder]["del_modes"][mode].update(files)
else:
folder_configs[folder]["all_add_modes"].update(add_modes)
folder_configs[folder]["all_del_modes"].update(del_modes)
# Evaluate config
for folder, attributes in folder_configs.items():
required_modes = set([])
for mode in attributes["add_modes"].keys():
if mode not in attributes["all_del_modes"]:
required_modes.add(mode)
files_to_monitor = {}
skip_modes = set([])
for mode in required_modes:
files_to_monitor.setdefault(mode, set([]))
files_to_monitor[mode].update(attributes["add_modes"][mode])
files_to_monitor[mode] -= attributes["del_modes"].get(mode, set([]))
if not files_to_monitor[mode]:
skip_modes.add(mode)
attributes["monitor_files"] = files_to_monitor
attributes["monitor_all"] = attributes["all_add_modes"] - attributes["all_del_modes"]
attributes["modes"] = required_modes - skip_modes
# Determine mask
attributes["mask"] = 0
for mode in attributes["modes"]:
attributes["mask"] |= map_events[mode][0]
for mode in attributes["monitor_all"]:
attributes["mask"] |= map_events[mode][0]
update_watched_folders()
if opt_foreground:
import pprint
sys.stdout.write(pprint.pformat(folder_configs))
# Save monitored file/folder information specified in mk_inotify.cfg
file(configured_paths, "w").write("\n".join(get_watched_files())+"\n")
# Event handler
eh = NotifyEventHandler()
notifier = pyinotify.Notifier(wm, eh)
# Wake up every few seconds, check heartbeat and write data to disk
signal.signal(signal.SIGALRM, wakeup_handler)
signal.alarm(write_interval)
notifier.loop()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,69 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Run and *send* only once every 4 hours
INTERVAL=14400
FLAGFILE=$MK_VARDIR/mk_inventory.last.$REMOTE
NOW=$(date +%s)
UNTIL=$((NOW + INTERVAL + 600))
#check if flagfile exits
if [ -e "$FLAGFILE" ]; then
LAST_RUN=$(cat $FLAGFILE)
else
#First run of the script
LAST_RUN=0
fi
if [ $(( NOW - LAST_RUN )) -ge $INTERVAL ]
then
echo $NOW > $FLAGFILE
# List of installed AIX packages
if type lslpp >/dev/null; then
echo "<<<aix_packages:sep(58):persist($UNTIL)>>>"
lslpp -c -L
fi
if type oslevel > /dev/null; then
# base level of the system
echo "<<<aix_baselevel:persist($UNTIL)>>>"
oslevel
# list the known service packs on a system
echo "<<<aix_service_packs:persist($UNTIL)>>>"
oslevel -sq
fi
# If you run the prtconf command without any flags, it displays the system model, machine serial,
# processor type, number of processors, processor clock speed, cpu type, total memory size, network information, filesystem
# information, paging space information, and devices information.
if type prtconf >/dev/null ; then
echo "<<<prtconf:sep(58):persist($UNTIL)>>>"
prtconf
fi
fi

View File

@ -0,0 +1,100 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Run and *send* only once every __ seconds
. $MK_CONFDIR/mk_inventory.cfg 2>/dev/null || true
# Default to four hours
INTERVAL=${INVENTORY_INTERVAL:-14400}
FLAGFILE=$MK_VARDIR/mk_inventory.last.$REMOTE
LAST_RUN=$(stat -c %Y $FLAGFILE)
NOW=$(date +%s)
UNTIL=$((NOW + INTERVAL + 600))
if [ $(( NOW - LAST_RUN )) -ge $INTERVAL ]
then
touch $FLAGFILE
# List of DEB packages
if type dpkg-query >/dev/null; then
echo "<<<lnx_packages:sep(124):persist($UNTIL)>>>"
dpkg-query --show --showformat='${Package}|${Version}|${Architecture}|deb|-|${Summary}|${Status}\n'
fi
# List of RPM packages in same format
if type rpm >/dev/null; then
echo "<<<lnx_packages:sep(9):persist($UNTIL)>>>"
rpm -qa --qf '%{NAME}\t%{VERSION}\t%{ARCH}\trpm\t%{RELEASE}\t%{SUMMARY}\t-\n'
fi
# List Gentoo packages
if type equery >/dev/null; then
echo "<<<lnx_packages:sep(124):persist($UNTIL)>>>"
equery -C list --format '$category/$name|$fullversion|$mask2|ebuild|Repository $repo|installed' \* | head -n -1
fi
# Information about distribution
echo "<<<lnx_distro:sep(124):persist($UNTIL)>>>"
for f in {/etc/{oracle-release,debian_version,gentoo-release,lsb-release,redhat-release,SuSE-release,os-release},/usr/share/cma/version} ; do
if [ -e $f ] ; then
echo "[[[$f]]]"
tr \\n \| < $f | sed 's/|$//' ; echo
fi
done
# CPU Information. We need just the first one
if [ -e /proc/cpuinfo ] ; then
echo "<<<lnx_cpuinfo:sep(58):persist($UNTIL)>>>"
sed 's/[[:space:]]*:[[:space:]]*/:/' < /proc/cpuinfo
fi
# Information about main board, memory, etc.
if type dmidecode >/dev/null ; then
echo "<<<dmidecode:sep(58):persist($UNTIL)>>>"
dmidecode -q | sed 's/\t/:/g'
fi
# Information about kernel architecture
if type uname >/dev/null ; then
echo "<<<lnx_uname:persist($UNTIL)>>>"
uname -m
uname -r
fi
if type lspci > /dev/null ; then
echo "<<<lnx_video:sep(58):persist($UNTIL)>>>"
lspci -v -s $(lspci | grep VGA | cut -d" " -f 1)
fi
# Some networking information
if type ip > /dev/null ; then
echo "<<<lnx_ip_a:persist($UNTIL)>>>"
ip a
echo "<<<lnx_ip_r:persist($UNTIL)>>>"
ip r
fi
fi

View File

@ -0,0 +1,91 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Run and *send* only once every __ seconds
. $MK_CONFDIR/mk_inventory.cfg 2>/dev/null || true
# Default to four hours
INTERVAL=${INVENTORY_INTERVAL:-14400}
FLAGFILE=$MK_VARDIR/mk_inventory.last.$REMOTE
if [ `uname -r` = "5.10" ]; then
NOW=$(truss /usr/bin/date 2>&1 | grep ^time | awk -F"= " '{print $2}')
else
NOW=`date +%s`
fi
UNTIL=$((NOW + INTERVAL + 600))
#check if flagfile exits
if [ -e "$FLAGFILE" ]; then
LAST_RUN=$(cat $FLAGFILE)
else
#First run of the script
LAST_RUN=0
fi
if [ $(( NOW - LAST_RUN )) -ge $INTERVAL ]
then
echo $NOW > $FLAGFILE
echo "<<<solaris_uname:sep(61):persist($UNTIL)>>>"
uname -X
if zoneadm list | grep global >/dev/null 2>&1
then
if type prtdiag > /dev/null; then
echo "<<<solaris_prtdiag:sep(10):persist($UNTIL)>>>"
if type sneep >/dev/null 2>&1; then
SN=$(sneep -t serial)
else
SN=$(smbios -t SMB_TYPE_SYSTEM | grep 'Serial Number:' | awk '{print substr($0, index($0,$3))}')
fi
echo "SerialNumber: $SN"
prtdiag -v
fi
if type prtpicl > /dev/null; then
echo "<<<solaris_prtpicl:persist($UNTIL)>>>"
prtpicl -v
fi
fi
if type psrinfo > /dev/null; then
echo "<<<solaris_psrinfo:persist($UNTIL)>>>"
psrinfo -p -v
fi
if type pkginfo >/dev/null ; then
echo "<<<solaris_pkginfo:sep(58):persist($UNTIL)>>>"
pkginfo -l
fi
echo "<<<solaris_addresses:persist($UNTIL)>>>"
ifconfig -a
echo "<<<solaris_routes:persist($UNTIL)>>>"
netstat -nr
fi

View File

@ -0,0 +1,34 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2017 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# iptables
if type iptables-save > /dev/null
then
echo "<<<iptables>>>"
# output filter configuration without table name, comments and
# status data, i.e. lines beginning with '*', '#' or ':'.
iptables-save -t filter | sed '/^[#*:]/d'
fi

View File

@ -0,0 +1,523 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
import urllib2, sys, os, socket, base64, ssl
from httplib import HTTPConnection, HTTPSConnection
try:
from simplejson import json
except ImportError:
try:
import json
except ImportError:
sys.stdout.write("<<<jolokia_info>>>\n")
sys.stdout.write("Error: Missing JSON library for Agent Plugin mk_jolokia\n")
exit()
opt_verbose = '--verbose' in sys.argv
opt_debug = '--debug' in sys.argv
class PreemptiveBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
"""
sends basic authentication with the first request,
before the server even asks for it
"""
def http_request(self, req):
url = req.get_full_url()
realm = None
user, pw = self.passwd.find_user_password(realm, url)
if pw:
raw = "%s:%s" % (user, pw)
auth = 'Basic %s' % base64.b64encode(raw).strip()
req.add_unredirected_header(self.auth_header, auth)
return req
https_request = http_request
class HTTPSValidatingConnection(HTTPSConnection):
def __init__(self, host, ca_file, key_file, cert_file):
HTTPSConnection.__init__(self, host, key_file=key_file, cert_file=cert_file)
self.__ca_file = ca_file
self.__key_file = key_file
self.__cert_file = cert_file
def connect(self):
HTTPConnection.connect(self)
if self.__ca_file:
self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, certfile=self.cert_file,
ca_certs=self.__ca_file, cert_reqs=ssl.CERT_REQUIRED)
else:
self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, certfile=self.cert_file,
ca_certs=self.__ca_file, cert_reqs=ssl.CERT_NONE)
class HTTPSAuthHandler(urllib2.HTTPSHandler):
def __init__(self, ca_file, key, cert):
urllib2.HTTPSHandler.__init__(self)
self.__ca_file = ca_file
self.__key = key
self.__cert = cert
def https_open(self, req):
# do_open expects a class as the first parameter but getConnection will act
# as a facotry function
return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout):
return HTTPSValidatingConnection(host, ca_file=self.__ca_file,
key_file=self.__key, cert_file=self.__cert)
def fetch_url_get(base_url, path, function):
if path:
url = "%s/%s/%s" % (base_url, function, path)
else:
url = base_url + "/"
if opt_verbose:
sys.stderr.write("DEBUG: Fetching: %s\n" % url)
try:
json_data = urllib2.urlopen(url).read()
if opt_verbose:
sys.stderr.write("DEBUG: Result: %s\n\n" % json_data)
except Exception, e:
if opt_debug:
raise
sys.stderr.write("ERROR: %s\n" % e)
return []
return json_data
def fetch_url_post(base_url, path, service_url, service_user, service_password, function):
segments = path.split("/")
data = {
"type": function.upper(),
"mbean": segments[0],
"attribute": segments[1],
"target": {
"url": service_url,
},
}
if len(segments) > 2:
data["path"] = segments[2]
if service_user:
data["target"]["user"] = service_user
data["target"]["password"] = service_password
if opt_verbose:
sys.stderr.write("DEBUG: Fetching: %s\n" % base_url)
try:
json_data = urllib2.urlopen(base_url, data=json.dumps(data)).read()
if opt_verbose:
sys.stderr.write("DEBUG: Result: %s\n\n" % json_data)
except Exception, e:
if opt_debug:
raise
sys.stderr.write("ERROR: %s\n" % e)
return []
return json_data
def fetch_var(protocol, server, port, path, suburi, itemspec, service_url, service_user,
service_password, function="read"):
base_url = "%s://%s:%d/%s" % (protocol, server, port, suburi)
if service_url is not None:
json_data = fetch_url_post(base_url, path,
service_url, service_user, service_password, function)
else:
json_data = fetch_url_get(base_url, path, function)
try:
obj = json.loads(json_data)
except Exception, e:
sys.stderr.write('ERROR: Invalid json code (%s)\n' % e)
sys.stderr.write(' Response %s\n' % json_data)
return []
if obj.get('status', 200) != 200:
sys.stderr.write('ERROR: Invalid response when fetching url %s\n' % base_url)
sys.stderr.write(' Response: %s\n' % json_data)
return []
# Only take the value of the object. If the value is an object
# take the first items first value.
# {'Catalina:host=localhost,path=\\/test,type=Manager': {'activeSessions': 0}}
if 'value' not in obj:
if opt_verbose:
sys.stderr.write("ERROR: not found: %s\n" % path)
return []
val = obj.get('value', None)
return make_item_list((), val, itemspec)
# convert single values into lists of items in
# case value is a 1-levelled or 2-levelled dict
def make_item_list(path, value, itemspec):
if type(value) != dict:
if type(value) == str:
value = value.replace(r'\/', '/')
return [(path, value)]
else:
result = []
for key, subvalue in value.items():
# Handle filtering via itemspec
miss = False
while itemspec and '=' in itemspec[0]:
if itemspec[0] not in key:
miss = True
break
itemspec = itemspec[1:]
if miss:
continue
item = extract_item(key, itemspec)
if not item:
item = (key,)
result += make_item_list(path + item, subvalue, [])
return result
# Example:
# key = 'Catalina:host=localhost,path=\\/,type=Manager'
# itemsepc = [ "path" ]
# --> "/"
def extract_item(key, itemspec):
path = key.split(":", 1)[-1]
components = path.split(",")
item = ()
comp_dict = {}
for comp in components:
parts = comp.split("=")
if len(parts) == 2:
left, right = parts
comp_dict[left] = right
for pathkey in itemspec:
if pathkey in comp_dict:
right = comp_dict[pathkey]
right = right.replace(r'\/', '/')
if '/' in right:
right = '/' + right.split('/')[-1]
item = item + (right,)
return item
def fetch_metric(inst, path, title, itemspec, inst_add=None):
values = fetch_var(inst["protocol"], inst["server"], inst["port"], path,
inst["suburi"], itemspec,
inst["service_url"], inst["service_user"], inst["service_password"])
for subinstance, value in values:
if not subinstance and not title:
sys.stdout.write("INTERNAL ERROR: %s\n" % value)
continue
if "threadStatus" in subinstance or "threadParam" in subinstance:
continue
if len(subinstance) > 1:
item = ",".join((inst["instance"],) + subinstance[:-1])
elif inst_add is not None:
item = inst["instance"] + "," + inst_add
else:
item = inst["instance"]
if title:
if subinstance:
tit = title + "." + subinstance[-1]
else:
tit = title
else:
tit = subinstance[-1]
yield (item.replace(" ", "_"), tit, value)
def query_instance(inst):
# Prepare user/password authentication via HTTP Auth
password_mngr = urllib2.HTTPPasswordMgrWithDefaultRealm()
if inst.get("password"):
password_mngr.add_password(None, "%s://%s:%d/" %
(inst["protocol"], inst["server"], inst["port"]), inst["user"], inst["password"])
handlers = []
if inst["protocol"] == "https":
if inst["mode"] == 'https' and (inst["client_key"] is None or
inst["client_cert"] is None):
sys.stdout.write('<<<jolokia_info>>>\n')
sys.stderr.write("ERROR: https set up as authentication method but certificate "
"wasn't provided\n")
return
handlers.append(HTTPSAuthHandler(inst["cert_path"],
inst["client_key"], inst["client_cert"]))
if inst["mode"] == 'digest':
handlers.append(urllib2.HTTPDigestAuthHandler(password_mngr))
elif inst["mode"] == "basic_preemptive":
handlers.append(PreemptiveBasicAuthHandler(password_mngr))
elif inst["mode"] == "basic" and inst["protocol"] != "https":
handlers.append(urllib2.HTTPBasicAuthHandler(password_mngr))
if handlers:
opener = urllib2.build_opener(*handlers)
urllib2.install_opener(opener)
# Determine type of server
server_info = fetch_var(inst["protocol"], inst["server"], inst["port"], "", inst["suburi"],
"", None, None, None)
sys.stdout.write('<<<jolokia_info>>>\n')
if server_info:
d = dict(server_info)
version = d.get(('info', 'version'), "unknown")
product = d.get(('info', 'product'), "unknown")
if inst.get("product"):
product = inst["product"]
agentversion = d.get(('agent',), "unknown")
sys.stdout.write("%s %s %s %s\n" % (inst["instance"], product, version, agentversion))
else:
sys.stdout.write("%s ERROR\n" % (inst["instance"],))
sys.stdout.write('<<<jolokia_metrics>>>\n')
sys.stdout.write("%s ERROR\n" % (inst["instance"],))
return
mbean_search_results = {}
sys.stdout.write('<<<jolokia_metrics>>>\n')
# Fetch the general information first
for var in global_vars + specific_vars.get(product, []):
# support old and new configuration format so we stay compatible with older
# configuration files
if len(var) == 3:
path, title, itemspec = var
mbean, path = path.split("/", 1)
do_search = False
else:
mbean, path, title, itemspec, do_search = var
queries = []
if do_search:
if mbean in mbean_search_results:
paths = mbean_search_results[mbean]
else:
paths = fetch_var(inst["protocol"], inst["server"], inst["port"], mbean,
inst["suburi"], "", None, None, None, function="search")[0][1]
mbean_search_results[mbean] = paths
for mbean_exp in paths:
queries.append( (inst, "%s/%s" % (urllib2.quote(mbean_exp), path), path,
itemspec, mbean_exp) )
else:
queries.append( (inst, mbean + "/" + path, title, itemspec) )
for inst, mbean_path, title, itemspec in queries:
try:
for out_item, out_title, out_value in fetch_metric(inst, mbean_path, title, itemspec):
sys.stdout.write("%s %s %s\n" % (out_item, out_title, out_value) )
except IOError:
return
except socket.timeout:
return
except:
if opt_debug:
raise
# Simply ignore exceptions. Need to be removed for debugging
continue
if custom_vars:
sys.stdout.write('<<<jolokia_generic>>>\n')
for var in custom_vars:
mbean, path, title, itemspec, do_search, value_type = var
queries = []
if do_search:
if mbean in mbean_search_results:
paths = mbean_search_results[mbean]
else:
paths = fetch_var(inst["protocol"], inst["server"], inst["port"], mbean,
inst["suburi"], "", None, None, None, function="search")[0][1]
mbean_search_results[mbean] = paths
for mbean_exp in paths:
queries.append( (inst, "%s/%s" % (urllib2.quote(mbean_exp), path), path,
itemspec, mbean_exp) )
else:
queries.append( (inst, mbean + "/" + path, title, itemspec) )
for inst, mbean_path, title, itemspec in queries:
try:
for out_item, out_title, out_value in fetch_metric(inst, mbean_path, title, itemspec):
sys.stdout.write("%s %s %s %s\n" % (out_item, out_title, out_value, value_type) )
except IOError:
return
except socket.timeout:
return
except:
if opt_debug:
raise
# Simply ignore exceptions. Need to be removed for debugging
continue
# Default configuration for all instances
protocol = "http"
server = "localhost"
port = 8080
user = "monitoring"
password = None
mode = "digest"
suburi = "jolokia"
instance = None
cert_path = "_default"
client_cert = None
client_key = None
service_url = None
service_user = None
service_password = None
product = None
global_vars = [
( "java.lang:type=Memory", "NonHeapMemoryUsage/used", "NonHeapMemoryUsage", [], False),
( "java.lang:type=Memory", "NonHeapMemoryUsage/max", "NonHeapMemoryMax", [], False),
( "java.lang:type=Memory", "HeapMemoryUsage/used", "HeapMemoryUsage", [], False),
( "java.lang:type=Memory", "HeapMemoryUsage/max", "HeapMemoryMax", [], False),
( "java.lang:type=Threading", "ThreadCount", "ThreadCount", [], False),
( "java.lang:type=Threading", "DaemonThreadCount", "DeamonThreadCount", [], False),
( "java.lang:type=Threading", "PeakThreadCount", "PeakThreadCount", [], False),
( "java.lang:type=Threading", "TotalStartedThreadCount", "TotalStartedThreadCount", [], False),
( "java.lang:type=Runtime", "Uptime", "Uptime", [], False),
( "java.lang:type=GarbageCollector,name=*", "CollectionCount", "", [], False),
( "java.lang:type=GarbageCollector,name=*", "CollectionTime", "", [], False),
( "java.lang:name=CMS%20Perm%20Gen,type=MemoryPool", "Usage/used", "PermGenUsage", [], False),
( "java.lang:name=CMS%20Perm%20Gen,type=MemoryPool", "Usage/max", "PermGenMax", [], False),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapHits", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OnDiskHits", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "InMemoryHitPercentage", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheMisses", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OnDiskHitPercentage", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "MemoryStoreObjectCount", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "DiskStoreObjectCount", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheMissPercentage", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheHitPercentage", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapHitPercentage", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "InMemoryMisses", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapStoreObjectCount", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "WriterQueueLength", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "WriterMaxQueueSize", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapMisses", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "InMemoryHits", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "AssociatedCacheName", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "ObjectCount", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OnDiskMisses", "", [], True),
( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheHits", "", [], True),
]
specific_vars = {
"weblogic" : [
( "*:*", "CompletedRequestCount", None, [ "ServerRuntime" ] , False),
( "*:*", "QueueLength", None, [ "ServerRuntime" ] , False),
( "*:*", "StandbyThreadCount", None, [ "ServerRuntime" ] , False),
( "*:*", "PendingUserRequestCount", None, [ "ServerRuntime" ] , False),
( "*:Name=ThreadPoolRuntime,*", "ExecuteThreadTotalCount", None, [ "ServerRuntime" ] , False),
( "*:*", "ExecuteThreadIdleCount", None, [ "ServerRuntime" ] , False),
( "*:*", "HoggingThreadCount", None, [ "ServerRuntime" ] , False),
( "*:Type=WebAppComponentRuntime,*", "OpenSessionsCurrentCount", None, [ "ServerRuntime", "ApplicationRuntime" ] , False),
],
"tomcat" : [
( "*:type=Manager,*", "activeSessions,maxActiveSessions", None, [ "path", "context" ] , False),
( "*:j2eeType=Servlet,name=default,*", "stateName", None, [ "WebModule" ] , False),
# Check not yet working
( "*:j2eeType=Servlet,name=default,*", "requestCount", None, [ "WebModule" ], False),
( "*:name=*,type=ThreadPool", "maxThreads", None, [], False),
( "*:name=*,type=ThreadPool", "currentThreadCount", None, [], False),
( "*:name=*,type=ThreadPool", "currentThreadsBusy", None, [], False),
# too wide location for addressing the right info
# ( "*:j2eeType=Servlet,*", "requestCount", None, [ "WebModule" ] , False),
],
"jboss" : [
( "*:type=Manager,*", "activeSessions,maxActiveSessions", None, [ "path", "context" ] , False),
],
}
# ( '*:j2eeType=WebModule,name=/--/localhost/-/%(app)s,*/state', None, [ "name" ]),
# ( '*:j2eeType=Servlet,WebModule=/--/localhost/-/%(app)s,name=%(servlet)s,*/requestCount', None, [ "WebModule", "name" ]),
# ( "Catalina:J2EEApplication=none,J2EEServer=none,WebModule=*,j2eeType=Servlet,name=*", None, [ "WebModule", "name" ]),
# List of instances to monitor. Each instance is a dict where
# the global configuration values can be overridden.
instances = [{}]
custom_vars = []
conffile = os.getenv("MK_CONFDIR", "/etc/check_mk") + "/jolokia.cfg"
if os.path.exists(conffile):
execfile(conffile)
if server == "use fqdn":
server = socket.getfqdn()
if instance == None:
instance = str(port)
# We have to deal with socket timeouts. Python > 2.6
# supports timeout parameter for the urllib2.urlopen method
# but we are on a python 2.5 system here which seem to use the
# default socket timeout. We are local here so set it to 1 second.
socket.setdefaulttimeout(1.0)
# Compute list of instances to monitor. If the user has defined
# instances in his configuration, we will use this (a list
# of dicts).
for inst in instances:
for varname, value in [
("protocol", protocol),
("server", server),
("port", port),
("user", user),
("password", password),
("mode", mode),
("suburi", suburi),
("instance", instance),
("cert_path", cert_path),
("client_cert", client_cert),
("client_key", client_key),
("service_url", service_url),
("service_user", service_user),
("service_password", service_password),
( "product", product ),
]:
if varname not in inst:
inst[varname] = value
if not inst["instance"]:
inst["instance"] = str(inst["port"])
inst["instance"] = inst["instance"].replace(" ", "_")
if inst.get("server") == "use fqdn":
inst["server"] = socket.getfqdn()
query_instance(inst)

View File

@ -0,0 +1,29 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
if type who >/dev/null; then
echo "<<<logins>>>"
who | wc -l
fi

View File

@ -0,0 +1,564 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Call with -d for debug mode: colored output, no saving of status
import sys, os, re, time, glob
# .--MEI-Cleanup---------------------------------------------------------.
# | __ __ _____ ___ ____ _ |
# | | \/ | ____|_ _| / ___| | ___ __ _ _ __ _ _ _ __ |
# | | |\/| | _| | |_____| | | |/ _ \/ _` | '_ \| | | | '_ \ |
# | | | | | |___ | |_____| |___| | __/ (_| | | | | |_| | |_) | |
# | |_| |_|_____|___| \____|_|\___|\__,_|_| |_|\__,_| .__/ |
# | |_| |
# +----------------------------------------------------------------------+
# In case the program crashes or is killed in a hard way, the frozen binary .exe
# may leave temporary directories named "_MEI..." in the temporary path. Clean them
# up to prevent eating disk space over time.
########################################################################
############## DUPLICATE CODE WARNING ##################################
### This code is also used in the cmk-update-agent frozen binary #######
### Any changes to this class should also be made in cmk-update-agent ##
### In the bright future we will move this code into a library #########
########################################################################
class MEIFolderCleaner(object):
def pid_running(self, pid):
import ctypes
kernel32 = ctypes.windll.kernel32
SYNCHRONIZE = 0x100000
process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
if process != 0:
kernel32.CloseHandle(process)
return True
else:
return False
def find_and_remove_leftover_folders(self, hint_filenames):
if not hasattr(sys, "frozen"):
return
import win32file # pylint: disable=import-error
import tempfile
base_path = tempfile.gettempdir()
for f in os.listdir(base_path):
try:
path = os.path.join(base_path, f)
if not os.path.isdir(path):
continue
# Only care about directories related to our program
invalid_dir = False
for hint_filename in hint_filenames:
if not os.path.exists(os.path.join(path, hint_filename)):
invalid_dir = True
break
if invalid_dir:
continue
pyinstaller_tmp_path = win32file.GetLongPathName(sys._MEIPASS).lower() # pylint: disable=no-member
if pyinstaller_tmp_path == path.lower():
continue # Skip our own directory
# Extract the process id from the directory and check whether or not it is still
# running. Don't delete directories of running processes!
# The name of the temporary directories is "_MEI<PID><NR>". We try to extract the PID
# by stripping of a single digit from the right. In the hope the NR is a single digit
# in all relevant cases.
pid = int(f[4:-1])
if self.pid_running(pid):
continue
shutil.rmtree(path)
except Exception, e:
# TODO: introduce verbose mode for mk_logwatch
pass
#.
os_type = "linux"
try:
import platform
os_type = platform.system().lower()
except:
pass
if '-d' in sys.argv[1:] or '--debug' in sys.argv[1:]:
tty_red = '\033[1;31m'
tty_green = '\033[1;32m'
tty_yellow = '\033[1;33m'
tty_blue = '\033[1;34m'
tty_normal = '\033[0m'
debug = True
else:
tty_red = ''
tty_green = ''
tty_yellow = ''
tty_blue = ''
tty_normal = ''
debug = False
# The configuration file and status file are searched
# in the directory named by the environment variable
# LOGWATCH_DIR. If that is not set, MK_CONFDIR is used.
# If that is not set either, the current directory ist
# used.
logwatch_dir = os.getenv("LOGWATCH_DIR")
if logwatch_dir:
mk_confdir = logwatch_dir
mk_vardir = logwatch_dir
else:
mk_confdir = os.getenv("MK_CONFDIR") or "."
mk_vardir = os.getenv("MK_VARDIR") or os.getenv("MK_STATEDIR") or "."
sys.stdout.write("<<<logwatch>>>\n")
config_filename = mk_confdir + "/logwatch.cfg"
config_dir = mk_confdir + "/logwatch.d/*.cfg"
# Determine the name of the state file
# $REMOTE set -> logwatch.state.$REMOTE
# $REMOTE not set and a tty -> logwatch.state.local
# $REMOTE not set and not a tty -> logwatch.state
remote_hostname = os.getenv("REMOTE", "")
remote_hostname = remote_hostname.replace(":", "_")
if remote_hostname != "":
status_filename = "%s/logwatch.state.%s" % (mk_vardir, remote_hostname)
else:
if sys.stdout.isatty():
status_filename = "%s/logwatch.state.local" % mk_vardir
else:
status_filename = "%s/logwatch.state" % mk_vardir
# Copy the last known state from the logwatch.state when there is no status_filename yet.
if not os.path.exists(status_filename) and os.path.exists("%s/logwatch.state" % mk_vardir):
import shutil
shutil.copy("%s/logwatch.state" % mk_vardir, status_filename)
def is_not_comment(line):
if line.lstrip().startswith('#') or \
line.strip() == '':
return False
return True
def parse_filenames(line):
return line.split()
def parse_pattern(level, pattern, line):
if level not in [ 'C', 'W', 'I', 'O' ]:
raise Exception("Invalid pattern line '%s'" % line)
try:
compiled = re.compile(pattern)
except:
raise Exception("Invalid regular expression in line '%s'" % line)
return (level, compiled)
def read_config():
config_lines = []
try:
config_lines += [ line.rstrip() for line in filter(is_not_comment, file(config_filename).readlines()) ]
except IOError, e:
if debug:
raise
# Add config from a logwatch.d folder
for config_file in glob.glob(config_dir):
config_lines += [ line.rstrip() for line in filter(is_not_comment, file(config_file).readlines()) ]
have_filenames = False
config = []
cont_list = []
rewrite_list = []
for line in config_lines:
if line[0].isspace(): # pattern line
if not have_filenames:
raise Exception("Missing logfile names")
level, pattern = line.split(None, 1)
if level == 'A':
cont_list.append(parse_cont_pattern(pattern))
elif level == 'R':
rewrite_list.append(pattern)
else:
level, compiled = parse_pattern(level, pattern, line)
# New pattern for line matching => clear continuation and rewrite patterns
cont_list = []
rewrite_list = []
# TODO: Fix the code and remove the pragma below!
patterns.append((level, compiled, cont_list, rewrite_list)) # pylint: disable=used-before-assignment
else: # filename line
patterns = []
cont_list = [] # Clear list of continuation patterns from last file
rewrite_list = [] # Same for rewrite patterns
config.append((parse_filenames(line), patterns))
have_filenames = True
return config
def parse_cont_pattern(pattern):
try:
return int(pattern)
except:
try:
return re.compile(pattern)
except:
if debug:
raise
raise Exception("Invalid regular expression in line '%s'" % pattern)
# structure of statusfile
# # LOGFILE OFFSET INODE
# /var/log/messages|7767698|32455445
# /var/test/x12134.log|12345|32444355
def read_status():
if debug:
return {}
status = {}
for line in file(status_filename):
# TODO: Remove variants with spaces. rsplit is
# not portable. split fails if logfilename contains
# spaces
inode = -1
try:
parts = line.split('|')
filename = parts[0]
offset = parts[1]
if len(parts) >= 3:
inode = parts[2]
except:
try:
filename, offset = line.rsplit(None, 1)
except:
filename, offset = line.split(None, 1)
status[filename] = int(offset), int(inode)
return status
def save_status(status):
f = file(status_filename, "w")
for filename, (offset, inode) in status.items():
f.write("%s|%d|%d\n" % (filename, offset, inode))
pushed_back_line = None
def next_line(file_handle):
global pushed_back_line
if pushed_back_line != None:
line = pushed_back_line
pushed_back_line = None
return line
else:
try:
line = file_handle.next()
# Avoid parsing of (yet) incomplete lines (when acutal application
# is just in the process of writing)
# Just check if the line ends with a \n. This handles \n and \r\n
if not line.endswith("\n"):
begin_of_line_offset = file_handle.tell() - len(line)
os.lseek(file_handle.fileno(), begin_of_line_offset, 0)
return None
return line
except:
return None
def is_inode_cabable(path):
if "linux" in os_type:
return True
elif "windows" in os_type:
volume_name = "%s:\\\\" % path.split(":", 1)[0]
import win32api # pylint: disable=import-error
volume_info = win32api.GetVolumeInformation(volume_name)
volume_type = volume_info[-1]
if "ntfs" in volume_type.lower():
return True
else:
return False
else:
return False
def process_logfile(logfile, patterns):
global pushed_back_line
# Look at which file offset we have finished scanning
# the logfile last time. If we have never seen this file
# before, we set the offset to -1
offset, prev_inode = status.get(logfile, (-1, -1))
try:
file_desc = os.open(logfile, os.O_RDONLY)
if not is_inode_cabable(logfile):
inode = 1 # Create a dummy inode
else:
inode = os.fstat(file_desc)[1] # 1 = st_ino
except:
if debug:
raise
sys.stdout.write("[[[%s:cannotopen]]]\n" % logfile)
return
sys.stdout.write("[[[%s]]]\n" % logfile)
# Seek to the current end in order to determine file size
current_end = os.lseek(file_desc, 0, 2) # os.SEEK_END not available in Python 2.4
status[logfile] = current_end, inode
# If we have never seen this file before, we just set the
# current pointer to the file end. We do not want to make
# a fuss about ancient log messages...
if offset == -1:
if not debug:
return
else:
offset = 0
# If the inode of the logfile has changed it has appearently
# been started from new (logfile rotation). At least we must
# assume that. In some rare cases (restore of a backup, etc)
# we are wrong and resend old log messages
if prev_inode >= 0 and inode != prev_inode:
offset = 0
# Our previously stored offset is the current end ->
# no new lines in this file
if offset == current_end:
return # nothing new
# If our offset is beyond the current end, the logfile has been
# truncated or wrapped while keeping the same inode. We assume
# that it contains all new data in that case and restart from
# offset 0.
if offset > current_end:
offset = 0
# now seek to offset where interesting data begins
os.lseek(file_desc, offset, 0) # os.SEEK_SET not available in Python 2.4
if os_type == "windows":
import io # Available with python 2.6
import codecs
# Some windows files are encoded in utf_16
# Peak the first two bytes to determine the encoding...
peak_handle = os.fdopen(file_desc, "rb")
first_two_bytes = peak_handle.read(2)
use_encoding = None
if first_two_bytes == "\xFF\xFE":
use_encoding = "utf_16"
elif first_two_bytes == "\xFE\xFF":
use_encoding = "utf_16_be"
os.lseek(file_desc, offset, 0) # os.SEEK_SET not available in Python 2.4
file_handle = io.open(file_desc, encoding = use_encoding)
else:
file_handle = os.fdopen(file_desc)
worst = -1
outputtxt = ""
lines_parsed = 0
start_time = time.time()
while True:
line = next_line(file_handle)
if line == None:
break # End of file
# Handle option maxlinesize
if opt_maxlinesize != None and len(line) > opt_maxlinesize:
line = line[:opt_maxlinesize] + "[TRUNCATED]\n"
lines_parsed += 1
# Check if maximum number of new log messages is exceeded
if opt_maxlines != None and lines_parsed > opt_maxlines:
outputtxt += "%s Maximum number (%d) of new log messages exceeded.\n" % (
opt_overflow, opt_maxlines)
worst = max(worst, opt_overflow_level)
os.lseek(file_desc, 0, 2) # Seek to end of file, skip all other messages
break
# Check if maximum processing time (per file) is exceeded. Check only
# every 100'th line in order to save system calls
if opt_maxtime != None and lines_parsed % 100 == 10 \
and time.time() - start_time > opt_maxtime:
outputtxt += "%s Maximum parsing time (%.1f sec) of this log file exceeded.\n" % (
opt_overflow, opt_maxtime)
worst = max(worst, opt_overflow_level)
os.lseek(file_desc, 0, 2) # Seek to end of file, skip all other messages
break
level = "."
for lev, pattern, cont_patterns, replacements in patterns:
matches = pattern.search(line[:-1])
if matches:
level = lev
levelint = {'C': 2, 'W': 1, 'O': 0, 'I': -1, '.': -1}[lev]
worst = max(levelint, worst)
# Check for continuation lines
for cont_pattern in cont_patterns:
if type(cont_pattern) == int: # add that many lines
for _unused_x in range(cont_pattern):
cont_line = next_line(file_handle)
if cont_line == None: # end of file
break
line = line[:-1] + "\1" + cont_line
else: # pattern is regex
while True:
cont_line = next_line(file_handle)
if cont_line == None: # end of file
break
elif cont_pattern.search(cont_line[:-1]):
line = line[:-1] + "\1" + cont_line
else:
pushed_back_line = cont_line # sorry for stealing this line
break
# Replacement
for replace in replacements:
line = replace.replace('\\0', line.rstrip()) + "\n"
for nr, group in enumerate(matches.groups()):
line = line.replace('\\%d' % (nr+1), group)
break # matching rule found and executed
color = {'C': tty_red, 'W': tty_yellow, 'O': tty_green, 'I': tty_blue, '.': ''}[level]
if debug:
line = line.replace("\1", "\nCONT:")
if level == "I":
level = "."
if opt_nocontext and level == '.':
continue
outputtxt += "%s%s %s%s\n" % (color, level, line[:-1], tty_normal)
new_offset = os.lseek(file_desc, 0, 1) # os.SEEK_CUR not available in Python 2.4
status[logfile] = new_offset, inode
# output all lines if at least one warning, error or ok has been found
if worst > -1:
sys.stdout.write(outputtxt)
sys.stdout.flush()
# Handle option maxfilesize, regardless of warning or errors that have happened
if opt_maxfilesize != None and (offset / opt_maxfilesize) < (new_offset / opt_maxfilesize):
sys.stdout.write("%sW Maximum allowed logfile size (%d bytes) exceeded for the %dth time.%s\n" %
(tty_yellow, opt_maxfilesize, new_offset / opt_maxfilesize, tty_normal))
try:
# This removes leftover folders which may be generated by crashing frozen binaries
folder_cleaner = MEIFolderCleaner()
folder_cleaner.find_and_remove_leftover_folders(hint_filenames = ["mk_logwatch.exe.manifest"])
except Exception, e:
sys.stdout.write("ERROR WHILE DOING FOLDER: %s\n" % e)
sys.exit(1)
try:
config = read_config()
except Exception, e:
if debug:
raise
sys.stdout.write("CANNOT READ CONFIG FILE: %s\n" % e)
sys.exit(1)
# Simply ignore errors in the status file. In case of a corrupted status file we simply begin
# with an empty status. That keeps the monitoring up and running - even if we might lose a
# message in the extreme case of a corrupted status file.
try:
status = read_status()
except Exception, e:
status = {}
logfile_patterns = {}
# The filename line may contain options like 'maxlines=100' or 'maxtime=10'
for filenames, patterns in config:
# Initialize options with default values
opt_maxlines = None
opt_maxtime = None
opt_maxlinesize = None
opt_maxfilesize = None
opt_regex = None
opt_overflow = 'C'
opt_overflow_level = 2
opt_nocontext = False
try:
options = [ o.split('=', 1) for o in filenames if '=' in o ]
for key, value in options:
if key == 'maxlines':
opt_maxlines = int(value)
elif key == 'maxtime':
opt_maxtime = float(value)
elif key == 'maxlinesize':
opt_maxlinesize = int(value)
elif key == 'maxfilesize':
opt_maxfilesize = int(value)
elif key == 'overflow':
if value not in [ 'C', 'I', 'W', 'O' ]:
raise Exception("Invalid value %s for overflow. Allowed are C, I, O and W" % value)
opt_overflow = value
opt_overflow_level = {'C':2, 'W':1, 'O':0, 'I':0}[value]
elif key == 'regex':
opt_regex = re.compile(value)
elif key == 'iregex':
opt_regex = re.compile(value, re.I)
elif key == 'nocontext':
opt_nocontext = True
else:
raise Exception("Invalid option %s" % key)
except Exception, e:
if debug:
raise
sys.stdout.write("INVALID CONFIGURATION: %s\n" % e)
sys.exit(1)
for glob_pattern in filenames:
if '=' in glob_pattern:
continue
logfiles = glob.glob(glob_pattern)
if opt_regex:
logfiles = [ f for f in logfiles if opt_regex.search(f) ]
if len(logfiles) == 0:
sys.stdout.write('[[[%s:missing]]]\n' % glob_pattern)
else:
for logfile in logfiles:
logfile_patterns[logfile] = logfile_patterns.get(logfile, []) + patterns
for logfile, patterns in logfile_patterns.items():
process_logfile(logfile, patterns)
if not debug:
save_status(status)

View File

@ -0,0 +1,13 @@
#!/usr/bin/ksh
# Logfile monitoring for AIX via errpt
# Beware: This Plugin clears the errors after each run,
# but it creates an detailed backup in /var/log/errpt_TIMESTAMP.log
echo "<<<logwatch>>>"
echo "[[[errorlog]]]"
OUT=$(errpt | awk 'NR>1 { printf "C %s\n", $0 }')
if [[ $OUT != '' ]];then
echo "$OUT"
errpt -a > /var/log/errpt_$(date +%s).log
errclear 0
fi

View File

@ -0,0 +1,205 @@
#!/usr/bin/python
# Monitor MongoDB on Linux
import sys
import time
import pprint
import os
import datetime
# This agent plugin creates various sections out of the MongoDB server status information.
# Important: 1) If MongoDB runs as single instance the agent data is assigned
# to the host same host where the plugin resides.
#
# 2) If MongoDB is deployed as replica set the agent data is piggybacked
# to a different hostname, name after the replica set name.
# You have to create a new host in the monitoring system matching the
# replica set name, or use the piggyback translation rule to modify the
# hostname according to your needs.
try:
import pymongo
except ImportError, e:
sys.stderr.write("ERROR: Unable to import pymongo module\n")
sys.exit(2)
# TODO: might be implemented in the future..
host = None
port = None
try:
con = pymongo.MongoClient(host, port)
try:
# pylint: disable=no-member
con = pymongo.database_names()
except:
con = pymongo.MongoClient(None, None, read_preference=pymongo.ReadPreference.SECONDARY)
con.admin.read_preference = pymongo.ReadPreference.SECONDARY
# if user and passwd:
# db = con["admin"]
# if not db.authenticate(user, passwd):
# sys.exit("Username/Password incorrect")
server_status = con.admin.command("serverStatus")
except:
sys.stdout.write("<<<mongodb_instance:sep(9)>>>\n")
sys.stdout.write("error\tInstance is down\n")
sys.exit(0)
server_version = tuple(con.server_info()['version'].split('.'))
repl_info = server_status.get("repl")
sys.stdout.write("<<<mongodb_instance:sep(9)>>>\n")
if not repl_info:
sys.stdout.write("mode\tSingle Instance\n")
else:
if repl_info.get("ismaster"):
sys.stdout.write("mode\tPrimary\n")
elif repl_info.get("secondary"):
sys.stdout.write("mode\tSecondary\n")
else:
sys.stdout.write("mode\tArbiter\n")
sys.stdout.write("address\t%s\n" % repl_info["me"])
sys.stdout.write("version\t%s\n" % server_status["version"])
sys.stdout.write("pid\t%s\n" % server_status["pid"])
if repl_info:
if not repl_info.get("ismaster"):
sys.exit(0)
sys.stdout.write("<<<<%s>>>>\n" % repl_info["setName"])
sys.stdout.write("<<<mongodb_replica:sep(9)>>>\n")
sys.stdout.write("primary\t%s\n" % repl_info.get("primary"))
sys.stdout.write("hosts\t%s\n" % " ".join(repl_info.get("hosts")))
sys.stdout.write("arbiters\t%s\n" % " ".join(repl_info.get("arbiters")))
sys.stdout.write("<<<mongodb_replstatus>>>\n")
sys.stdout.write(pprint.pformat(con.admin.command("replSetGetStatus")))
sys.stdout.write("<<<mongodb_asserts>>>\n")
for key, value in server_status.get("asserts", {}).items():
sys.stdout.write("%s %s\n" % (key, value))
sys.stdout.write("<<<mongodb_connections>>>\n")
sys.stdout.write("%s\n" % "\n".join(map(lambda x: "%s %s" % x, server_status["connections"].items())))
databases = dict(map(lambda x: (x, {}), con.database_names()))
for name in databases.keys():
databases[name]["collections"] = con[name].collection_names()
databases[name]["stats"] = con[name].command("dbstats")
databases[name]["collstats"] = {}
for collection in databases[name]["collections"]:
databases[name]["collstats"][collection] = con[name].command("collstats", collection)
sys.stdout.write("<<<mongodb_chunks>>>\n")
col = con.config.chunks
for db_name, db_data in databases.items():
shards = col.distinct("shard")
sys.stdout.write("shardcount %d\n" % len(shards))
for collection in db_data.get("collections"):
nsfilter = "%s.%s" % (db_name, collection)
sys.stdout.write("nscount %s %s\n" % (nsfilter, col.find({"ns": nsfilter}).count()))
for shard in shards:
sys.stdout.write("shardmatches %s#%s %s\n" % (nsfilter, shard, col.find({"ns": nsfilter, "shard": shard}).count()))
sys.stdout.write("<<<mongodb_locks>>>\n")
global_lock_info = server_status.get("globalLock")
if global_lock_info:
for what in [ "activeClients", "currentQueue" ]:
if what in global_lock_info:
for key, value in global_lock_info[what].items():
sys.stdout.write("%s %s %s\n" % (what, key, value))
sys.stdout.write("<<<mongodb_flushing>>>\n")
sys.stdout.write("average_ms %s\n" % server_status["backgroundFlushing"]["average_ms"])
sys.stdout.write("last_ms %s\n" % server_status["backgroundFlushing"]["last_ms"])
sys.stdout.write("flushed %s\n" % server_status["backgroundFlushing"]["flushes"])
# Unused
#try:
# if server_version >= tuple("2.4.0".split(".")):
# indexCounters = server_status['indexCounters']
# else:
# indexCounters = server_status['indexCounters']["btree"]
# print "<<<mongodb_indexcounters>>>"
# for key, value in indexCounters.items():
# print "%s %s" % (key, value)
#except:
# pass
sys.stdout.write("<<<mongodb_mem>>>\n")
for key, value in server_status["mem"].items():
sys.stdout.write("%s %s\n" % (key, value))
for key, value in server_status["extra_info"].items():
sys.stdout.write("%s %s\n" % (key, value))
sys.stdout.write("<<<mongodb_counters>>>\n")
for what in ["opcounters", "opcountersRepl"]:
for key, value in server_status.get(what, {}).items():
sys.stdout.write("%s %s %s\n" % (what, key, value))
sys.stdout.write("<<<mongodb_collections:sep(9)>>>\n")
for dbname, dbdata in databases.items():
for collname, colldata in dbdata.get("collstats", {}).items():
for what, value in colldata.items():
sys.stdout.write("%s\t%s\t%s\t%s\n" % (dbname, collname, what, value))
sys.stdout.write("<<<logwatch>>>\n")
sys.stdout.write("[[[MongoDB startupWarnings]]]\n")
startup_warnings = con.admin.command({"getLog": "startupWarnings"})
var_dir = os.environ.get("MK_VARDIR")
if var_dir:
state_file = "%s/mongodb.state" % var_dir
last_timestamp = None
output_all = False
# Supports: Nov 6 13:44:09
# 2015-10-17T05:35:24
def get_timestamp(text):
for pattern in [ "%a %b %d %H:%M:%S",
"%Y-%m-%dT%H:%M:%S" ]:
try:
result = time.mktime(time.strptime(text, pattern))
return result
except:
continue
year_available = False
if os.path.exists(state_file):
last_timestamp = int(file(state_file).read())
if time.localtime(last_timestamp).tm_year >= 2015:
year_available = True
# Note: there is no year information in these loglines
# As workaround we look at the creation date (year) of the last statefile
# If it differs and there are new messages we start from the beginning
if not year_available:
statefile_year = time.localtime(os.stat(state_file).st_ctime).tm_year
if time.localtime().tm_year != statefile_year:
output_all = True
for line in startup_warnings["log"]:
state = "C"
state_index = line.find("]")+2
if len(line) == state_index or line[state_index:].startswith("** "):
state = "."
if "** WARNING:" in line:
state = "W"
if output_all or get_timestamp(line.split(".")[0]) > last_timestamp:
sys.stdout.write("%s %s\n" % (state, line))
# update state file
if startup_warnings["log"]:
file(state_file, "w").write("%d" % get_timestamp(startup_warnings["log"][-1].split(".")[0]))
sys.stdout.write("<<<<>>>>\n")

View File

@ -0,0 +1,92 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# gets optional socket as argument
function do_query() {
INSTANCE=$(echo $1|awk -v FS="=" '{print $2}')
COUNT=$(ps -efww | grep [/]usr/sbin/mysqld | grep socket | wc -l)
if [ $COUNT -gt 1 ]
then
INSTANCE_NAME=$(ps -efww|grep socket|grep "${INSTANCE}"|grep "[u]ser" | sed -ne 's/.*socket=\([^.]*\).*/\1/p')
INSTANCE_NAME="[[${INSTANCE_NAME##*/}]]"
else
INSTANCE_NAME="[[$(ps -efww|grep socket|grep "${INSTANCE}"|grep "[u]ser" | sed -ne 's/.*user=\([^ ]*\).*/\1/p')]]"
fi
# Check if mysqld is running and root password setup
echo "<<<mysql_ping>>>"
echo $INSTANCE_NAME
mysqladmin --defaults-extra-file=$MK_CONFDIR/mysql.cfg $1 ping 2>&1
if [ $? -eq 0 ]; then
echo "<<<mysql>>>"
echo $INSTANCE_NAME
mysql --defaults-extra-file=$MK_CONFDIR/mysql.cfg $1 -sN \
-e "show global status ; show global variables ;"
echo "<<<mysql_capacity>>>"
echo $INSTANCE_NAME
mysql --defaults-extra-file=$MK_CONFDIR/mysql.cfg $1 -sN \
-e "SELECT table_schema, sum(data_length + index_length), sum(data_free)
FROM information_schema.TABLES GROUP BY table_schema"
echo "<<<mysql_slave>>>"
echo $INSTANCE_NAME
mysql --defaults-extra-file=$MK_CONFDIR/mysql.cfg $1 -s \
-e "show slave status\G"
fi
}
if which mysqladmin >/dev/null
then
mysql_sockets=$(fgrep socket $MK_CONFDIR/mysql.cfg|sed -ne 's/.*socket=\([^ ]*\).*/\1/p')
if [ -z "$mysql_sockets" ] ; then
mysql_sockets=$(ps -efww | grep mysqld | grep "[s]ocket" | sed -ne 's/.*socket=\([^ ]*\).*/\1/p')
fi
if [ -z "$mysql_sockets" ] ; then
do_query ""
else
for socket in $mysql_sockets ; do
do_query "--socket="$socket
done
fi
#echo "<<<mysql_version>>>"
#mysql -V
echo "<<<mysql_port>>>"
ps -efww|grep mysqld|while read LINE; do echo $LINE|grep "[u]ser" | sed -ne 's/.*user=\([^ ]*\).*/\1/p'; echo $LINE|grep mysqld | grep "[p]ort"|sed -ne 's/.*port=\([^ ]*\).*/\1/p' ; done|xargs -n2
#echo "<<<mysql_instances>>>"
#mysql --defaults-extra-file=$MK_CONFDIR/mysql.cfg $1 -s \
# -e "show INSTANCES"
fi

View File

@ -0,0 +1,30 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
if which omreport >/dev/null
then
echo "<<<dell_om_vdisks>>>"
omreport storage vdisk
fi

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Check_MK agent plugin for monitoring ORACLE ASM
if [ ! -e $MK_CONFDIR/asmcmd.sh ]; then
echo "$MK_CONFDIR/asmcmd.sh does not exist." >&2
exit 1
fi
function asmcmd () {
$MK_CONFDIR/asmcmd.sh "$@"
}
echo "<<<oracle_asm_diskgroup>>>"
asmcmd lsdg | grep -v State | grep -v "The Oracle"

View File

@ -0,0 +1,127 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# Developed by Thorsten Bruhns from OPITZ CONSULTING Deutschland GmbH
set -f
ocrcfgfile=/etc/oracle/ocr.loc
olrcfgfile=/etc/oracle/olr.loc
resourcefilter="^NAME=|^TYPE=|^STATE=|^TARGET=|^ENABLED="
# .--Functions-----------------------------------------------------------.
# | _____ _ _ |
# | | ___| _ _ __ ___| |_(_) ___ _ __ ___ |
# | | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __| |
# | | _|| |_| | | | | (__| |_| | (_) | | | \__ \ |
# | |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ |
# | |
# +----------------------------------------------------------------------+
# | |
# '----------------------------------------------------------------------'
function set_has_env(){
test -f ${ocrcfgfile} || exit 0
local_has_type=$(cat $ocrcfgfile | grep "^local_only=" | cut -d"=" -f2 | tr '[:lower:]' '[:upper:]')
local_has_type=${local_has_type:-"FALSE"}
if [ -f ${olrcfgfile} ] ; then
has_ORACLE_HOME=$(cat $olrcfgfile | grep "^crs_home=" | cut -d"=" -f2)
else
# There is no olr.cfg in 10.2 and 11.1
# we try to get the ORA_CRS_HOME from /etc/init.d/init.cssd
local_has_type=FALSE
INITCSSD=/etc/init.d/init.cssd
if [ ! -f ${INITCSSD} ] ; then
exit 0
else
has_ORACLE_HOME=$(grep "^ORA_CRS_HOME=" ${INITCSSD} | cut -d"=" -f2-)
fi
fi
CRSCTL=${has_ORACLE_HOME}/bin/crsctl
OLSNODES=${has_ORACLE_HOME}/bin/olsnodes
CRS_STAT=${has_ORACLE_HOME}/bin/crs_stat
}
function printhasdata() {
ps -e | grep cssd.bin > /dev/null || exit 0
echo "<<<oracle_crs_version:sep(124)>>>"
$CRSCTL query has releaseversion
echo "<<<oracle_crs_res>>>"
$CRSCTL stat res -f | grep -E $resourcefilter
}
function printcrsdata() {
ps -e | grep -e ohasd.bin -e crsd.bin > /dev/null || exit 0
echo "<<<oracle_crs_version:sep(124)>>>"
crs_version=$($CRSCTL query crs releaseversion)
echo $crs_version
echo "<<<oracle_crs_voting>>>"
$CRSCTL query css votedisk | grep "^ [0-9]"
ps -e | grep crsd.bin > /dev/null || exit 0
echo "<<<oracle_crs_res:sep(124)>>>"
OLS_NODENAME=$($OLSNODES -l)
echo "nodename|"$OLS_NODENAME
crs_version_short=$(echo $crs_version | cut -d"[" -f2- | cut -d"." -f-2 | sed 's/\.//')
if [ $(($crs_version_short)) -ge 112 ] ; then
$CRSCTL stat res -v -n $OLS_NODENAME -init | grep -E $resourcefilter | sed "s/^/csslocal\|/"
for nodelist in $($OLSNODES)
do
$CRSCTL stat res -v -n $nodelist | grep -E $resourcefilter | sed "s/^/$nodelist\|/"
done
else
$CRS_STAT -f -c $OLS_NODENAME | grep -E $resourcefilter | sed "s/^/$OLS_NODENAME\|/"
fi
}
# .--Main----------------------------------------------------------------.
# | __ __ _ |
# | | \/ | __ _(_)_ __ |
# | | |\/| |/ _` | | '_ \ |
# | | | | | (_| | | | | | |
# | |_| |_|\__,_|_|_| |_| |
# | |
# +----------------------------------------------------------------------+
# | |
# '----------------------------------------------------------------------'
set_has_env
echo "<<<oracle_crs_res>>>"
echo "<<<oracle_crs_version>>>"
echo "<<<oracle_crs_votedisk>>>"
if [ $local_has_type = 'FALSE' ] ; then
printcrsdata
else
printhasdata
fi

View File

@ -0,0 +1,485 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2015 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# TODO postgres_connections output format
# .--common funcs--------------------------------------------------------.
# | __ |
# | ___ ___ _ __ ___ _ __ ___ ___ _ __ / _|_ _ _ __ ___ ___ |
# | / __/ _ \| '_ ` _ \| '_ ` _ \ / _ \| '_ \ | |_| | | | '_ \ / __/ __| |
# || (_| (_) | | | | | | | | | | | (_) | | | || _| |_| | | | | (__\__ \ |
# | \___\___/|_| |_| |_|_| |_| |_|\___/|_| |_||_| \__,_|_| |_|\___|___/ |
# | |
# '----------------------------------------------------------------------'
function compare_version_greater_equal() {
local GREATER_ONE
GREATER_ONE=$(echo "$1 $2" | awk '{if ($1 >= $2) print $1; else print $2}')
if [ "$GREATER_ONE" == "$1" ] ; then
return 0
else
return 1
fi
}
#.
# .--section funcs-------------------------------------------------------.
# | _ _ __ |
# | ___ ___ ___| |_(_) ___ _ __ / _|_ _ _ __ ___ ___ |
# | / __|/ _ \/ __| __| |/ _ \| '_ \ | |_| | | | '_ \ / __/ __| |
# | \__ \ __/ (__| |_| | (_) | | | | | _| |_| | | | | (__\__ \ |
# | |___/\___|\___|\__|_|\___/|_| |_| |_| \__,_|_| |_|\___|___/ |
# | |
# '----------------------------------------------------------------------'
function postgres_instances() {
echo '<<<postgres_instances>>>'
# If we have no instances we take db id (pqsql/postgres) because
# ps output may be unreadable
# In case of instances ps output shows them readable
if [ ! -z "${1}" ]; then
echo "[[[${1}]]]"
fi
pgrep -laf bin/postgres
}
function postgres_sessions() {
# Postgres 9.2 uses 'query' instead of 'current_query'
local OUTPUT
OUTPUT="$(echo "\echo '<<<postgres_sessions>>>${INSTANCE_SECTION}'
SELECT (
SELECT column_name
FROM information_schema.columns
WHERE table_name='pg_stat_activity' AND column_name in ('query', 'current_query')
) = '<IDLE>' as query, count(*)
FROM pg_stat_activity
GROUP BY (query = '<IDLE>');" |\
su - "$DBUSER" -c "$export_PGPASSFILE $psql -X --variable ON_ERROR_STOP=1 -d $PGDATABASE ${EXTRA_ARGS} -A -t -F' '" 2>/dev/null)"
echo "$OUTPUT"
# line with number of idle sessions is sometimes missing on Postgres 8.x. This can lead
# to an altogether empty section and thus the check disappearing.
echo "$OUTPUT" | grep -q '^t ' || echo "t 0"
}
function postgres_simple_queries() {
# Querytime
# Supports versions >= 8.3, > 9.1
local QUERYTIME_QUERY
if compare_version_greater_equal "$POSTGRES_VERSION" "9.2" ; then
QUERYTIME_QUERY="SELECT datname, datid, usename, client_addr, state AS state, COALESCE(ROUND(EXTRACT(epoch FROM now()-query_start)),0) AS seconds,
pid, regexp_replace(query, E'[\\n\\r\\u2028]+', ' ', 'g' ) AS current_query FROM pg_stat_activity WHERE (query_start IS NOT NULL AND (state NOT LIKE 'idle%' OR state IS NULL)) ORDER BY query_start, pid DESC;"
else
QUERYTIME_QUERY="SELECT datname, datid, usename, client_addr, '' AS state, COALESCE(ROUND(EXTRACT(epoch FROM now()-query_start)),0) AS seconds,
procpid as pid, regexp_replace(current_query, E'[\\n\\r\\u2028]+', ' ', 'g' ) AS current_query FROM pg_stat_activity WHERE (query_start IS NOT NULL AND current_query NOT LIKE '<IDLE>%') ORDER BY query_start, procpid DESC;"
fi
# Number of current connections per database
# We need to output the databases, too.
# This query does not report databases without an active query
local CONNECTIONS_QUERY
if compare_version_greater_equal "$POSTGRES_VERSION" "9.2" ; then
CONNECTIONS_QUERY="SELECT COUNT(datid) AS current,
(SELECT setting AS mc FROM pg_settings WHERE name = 'max_connections') AS mc,
d.datname
FROM pg_database d
LEFT JOIN pg_stat_activity s ON (s.datid = d.oid) WHERE state <> 'idle'
GROUP BY 2,3
ORDER BY datname;"
else
CONNECTIONS_QUERY="SELECT COUNT(datid) AS current,
(SELECT setting AS mc FROM pg_settings WHERE name = 'max_connections') AS mc,
d.datname
FROM pg_database d
LEFT JOIN pg_stat_activity s ON (s.datid = d.oid) WHERE current_query <> '<IDLE>'
GROUP BY 2,3
ORDER BY datname;"
fi
echo "\pset footer off
\echo '<<<postgres_stat_database:sep(59)>>>${INSTANCE_SECTION}'
SELECT datid, datname, numbackends, xact_commit, xact_rollback, blks_read, blks_hit, tup_returned, tup_fetched, tup_inserted, tup_updated, tup_deleted, pg_database_size(datname) AS datsize FROM pg_stat_database;
\echo '<<<postgres_locks:sep(59)>>>${INSTANCE_SECTION}'
\echo '[databases_start]'
$ECHO_DATABASES
\echo '[databases_end]'
SELECT datname, granted, mode FROM pg_locks l RIGHT JOIN pg_database d ON (d.oid=l.database) WHERE d.datallowconn;
\echo '<<<postgres_query_duration:sep(59)>>>${INSTANCE_SECTION}'
\echo '[databases_start]'
$ECHO_DATABASES
\echo '[databases_end]'
$QUERYTIME_QUERY
\echo '<<<postgres_connections:sep(59)>>>${INSTANCE_SECTION}'
\echo '[databases_start]'
$ECHO_DATABASES
\echo '[databases_end]'
$CONNECTIONS_QUERY" \
| su - "$DBUSER" -c "$export_PGPASSFILE $psql -X -d $PGDATABASE ${EXTRA_ARGS} -q -A -F';'"
}
function postgres_stats() {
# Contains last vacuum time and analyze time
local LASTVACUUM="SELECT current_database() AS datname, nspname AS sname, relname AS tname,
CASE WHEN v IS NULL THEN -1 ELSE round(extract(epoch FROM v)) END AS vtime,
CASE WHEN g IS NULL THEN -1 ELSE round(extract(epoch FROM v)) END AS atime
FROM (SELECT nspname, relname, GREATEST(pg_stat_get_last_vacuum_time(c.oid), pg_stat_get_last_autovacuum_time(c.oid)) AS v,
GREATEST(pg_stat_get_last_analyze_time(c.oid), pg_stat_get_last_autoanalyze_time(c.oid)) AS g
FROM pg_class c, pg_namespace n
WHERE relkind = 'r' AND n.oid = c.relnamespace AND n.nspname <> 'information_schema'
ORDER BY 3) AS foo;"
local FIRST=
local QUERY="\pset footer off
BEGIN;
SET statement_timeout=30000;
COMMIT;
\echo '<<<postgres_stats:sep(59)>>>${INSTANCE_SECTION}'
\echo '[databases_start]'
$ECHO_DATABASES
\echo '[databases_end]'"
for db in $DATABASES ; do
QUERY="$QUERY
\c $db
$LASTVACUUM
"
if [ -z $FIRST ] ; then
FIRST=false
QUERY="$QUERY
\pset tuples_only on
"
fi
done
echo "$QUERY" | su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';'" | grep -v -e 'COMMIT$' -e 'SET$' -e 'BEGIN$'
}
function postgres_version() {
# Postgres version an connection time
echo -e "<<<postgres_version:sep(1)>>>${INSTANCE_SECTION}"
(TIMEFORMAT='%3R'; time echo "SELECT version() AS v" |\
su - "$DBUSER" -c "$export_PGPASSFILE $psql -X -d $PGDATABASE ${EXTRA_ARGS} -t -A -F';'; echo -e '<<<postgres_conn_time>>>${INSTANCE_SECTION}'") 2>&1
}
function postgres_bloat() {
# Bloat index and tables
# Supports versions <9.0, >=9.0
# This huge query has been gratefully taken from Greg Sabino Mullane's check_postgres.pl
local BLOAT_QUERY
if compare_version_greater_equal "$POSTGRES_VERSION" "9.0" ; then
BLOAT_QUERY="SELECT
current_database() AS db, schemaname, tablename, reltuples::bigint AS tups, relpages::bigint AS pages, otta,
ROUND(CASE WHEN otta=0 OR sml.relpages=0 OR sml.relpages=otta THEN 0.0 ELSE sml.relpages/otta::numeric END,1) AS tbloat,
CASE WHEN relpages < otta THEN 0 ELSE relpages::bigint - otta END AS wastedpages,
CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::bigint END AS wastedbytes,
CASE WHEN relpages < otta THEN 0 ELSE (bs*(relpages-otta))::bigint END AS wastedsize,
iname, ituples::bigint AS itups, ipages::bigint AS ipages, iotta,
ROUND(CASE WHEN iotta=0 OR ipages=0 OR ipages=iotta THEN 0.0 ELSE ipages/iotta::numeric END,1) AS ibloat,
CASE WHEN ipages < iotta THEN 0 ELSE ipages::bigint - iotta END AS wastedipages,
CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes,
CASE WHEN ipages < iotta THEN 0 ELSE (bs*(ipages-iotta))::bigint END AS wastedisize,
CASE WHEN relpages < otta THEN
CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta::bigint) END
ELSE CASE WHEN ipages < iotta THEN bs*(relpages-otta::bigint)
ELSE bs*(relpages-otta::bigint + ipages-iotta::bigint) END
END AS totalwastedbytes
FROM (
SELECT
nn.nspname AS schemaname,
cc.relname AS tablename,
COALESCE(cc.reltuples,0) AS reltuples,
COALESCE(cc.relpages,0) AS relpages,
COALESCE(bs,0) AS bs,
COALESCE(CEIL((cc.reltuples*((datahdr+ma-
(CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)),0) AS otta,
COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages,
COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols
FROM
pg_class cc
JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname <> 'information_schema'
LEFT JOIN
(
SELECT
ma,bs,foo.nspname,foo.relname,
(datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr,
(maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2
FROM (
SELECT
ns.nspname, tbl.relname, hdr, ma, bs,
SUM((1-coalesce(null_frac,0))*coalesce(avg_width, 2048)) AS datawidth,
MAX(coalesce(null_frac,0)) AS maxfracsum,
hdr+(
SELECT 1+count(*)/8
FROM pg_stats s2
WHERE null_frac<>0 AND s2.schemaname = ns.nspname AND s2.tablename = tbl.relname
) AS nullhdr
FROM pg_attribute att
JOIN pg_class tbl ON att.attrelid = tbl.oid
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
LEFT JOIN pg_stats s ON s.schemaname=ns.nspname
AND s.tablename = tbl.relname
AND s.inherited=false
AND s.attname=att.attname,
(
SELECT
(SELECT current_setting('block_size')::numeric) AS bs,
CASE WHEN SUBSTRING(SPLIT_PART(v, ' ', 2) FROM '#\[0-9]+.[0-9]+#\%' for '#')
IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr,
CASE WHEN v ~ 'mingw32' OR v ~ '64-bit' THEN 8 ELSE 4 END AS ma
FROM (SELECT version() AS v) AS foo
) AS constants
WHERE att.attnum > 0 AND tbl.relkind='r'
GROUP BY 1,2,3,4,5
) AS foo
) AS rs
ON cc.relname = rs.relname AND nn.nspname = rs.nspname
LEFT JOIN pg_index i ON indrelid = cc.oid
LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid
) AS sml
WHERE sml.relpages - otta > 0 OR ipages - iotta > 10 ORDER BY totalwastedbytes DESC LIMIT 10;"
else
BLOAT_QUERY="SELECT
current_database() AS db, schemaname, tablename, reltuples::bigint AS tups, relpages::bigint AS pages, otta,
ROUND(CASE WHEN otta=0 OR sml.relpages=0 OR sml.relpages=otta THEN 0.0 ELSE sml.relpages/otta::numeric END,1) AS tbloat,
CASE WHEN relpages < otta THEN 0 ELSE relpages::bigint - otta END AS wastedpages,
CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::bigint END AS wastedbytes,
CASE WHEN relpages < otta THEN '0 bytes'::text ELSE (bs*(relpages-otta))::bigint || ' bytes' END AS wastedsize,
iname, ituples::bigint AS itups, ipages::bigint AS ipages, iotta,
ROUND(CASE WHEN iotta=0 OR ipages=0 OR ipages=iotta THEN 0.0 ELSE ipages/iotta::numeric END,1) AS ibloat,
CASE WHEN ipages < iotta THEN 0 ELSE ipages::bigint - iotta END AS wastedipages,
CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes,
CASE WHEN ipages < iotta THEN '0 bytes' ELSE (bs*(ipages-iotta))::bigint || ' bytes' END AS wastedisize,
CASE WHEN relpages < otta THEN
CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta::bigint) END
ELSE CASE WHEN ipages < iotta THEN bs*(relpages-otta::bigint)
ELSE bs*(relpages-otta::bigint + ipages-iotta::bigint) END
END AS totalwastedbytes
FROM (
SELECT
nn.nspname AS schemaname,
cc.relname AS tablename,
COALESCE(cc.reltuples,0) AS reltuples,
COALESCE(cc.relpages,0) AS relpages,
COALESCE(bs,0) AS bs,
COALESCE(CEIL((cc.reltuples*((datahdr+ma-
(CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)),0) AS otta,
COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages,
COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols
FROM
pg_class cc
JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname <> 'information_schema'
LEFT JOIN
(
SELECT
ma,bs,foo.nspname,foo.relname,
(datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr,
(maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2
FROM (
SELECT
ns.nspname, tbl.relname, hdr, ma, bs,
SUM((1-coalesce(null_frac,0))*coalesce(avg_width, 2048)) AS datawidth,
MAX(coalesce(null_frac,0)) AS maxfracsum,
hdr+(
SELECT 1+count(*)/8
FROM pg_stats s2
WHERE null_frac<>0 AND s2.schemaname = ns.nspname AND s2.tablename = tbl.relname
) AS nullhdr
FROM pg_attribute att
JOIN pg_class tbl ON att.attrelid = tbl.oid
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
LEFT JOIN pg_stats s ON s.schemaname=ns.nspname
AND s.tablename = tbl.relname
AND s.attname=att.attname,
(
SELECT
(SELECT current_setting('block_size')::numeric) AS bs,
CASE WHEN SUBSTRING(SPLIT_PART(v, ' ', 2) FROM '#\"[0-9]+.[0-9]+#\"%' for '#')
IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr,
CASE WHEN v ~ 'mingw32' OR v ~ '64-bit' THEN 8 ELSE 4 END AS ma
FROM (SELECT version() AS v) AS foo
) AS constants
WHERE att.attnum > 0 AND tbl.relkind='r'
GROUP BY 1,2,3,4,5
) AS foo
) AS rs
ON cc.relname = rs.relname AND nn.nspname = rs.nspname
LEFT JOIN pg_index i ON indrelid = cc.oid
LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid
) AS sml
WHERE sml.relpages - otta > 0 OR ipages - iotta > 10 ORDER BY totalwastedbytes DESC LIMIT 10;"
fi
local FIRST=
local QUERY="\pset footer off
\echo '<<<postgres_bloat:sep(59)>>>${INSTANCE_SECTION}'
\echo '[databases_start]'
$ECHO_DATABASES
\echo '[databases_end]'"
for db in $DATABASES ; do
QUERY="$QUERY
\c $db
$BLOAT_QUERY
"
if [ -z $FIRST ] ; then
FIRST=false
QUERY="$QUERY
\pset tuples_only on
"
fi
done
echo "$QUERY" | su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';'"
}
#.
# .--main----------------------------------------------------------------.
# | _ |
# | _ __ ___ __ _(_)_ __ |
# | | '_ ` _ \ / _` | | '_ \ |
# | | | | | | | (_| | | | | | |
# | |_| |_| |_|\__,_|_|_| |_| |
# | |
# '----------------------------------------------------------------------'
### postgres.cfg ##
# DBUSER=OS_USER_NAME
# INSTANCE=/home/postgres/db1.env:USER_NAME:/PATH/TO/.pgpass
# INSTANCE=/home/postgres/db2.env:USER_NAME:/PATH/TO/.pgpass
# TODO @dba USERNAME in .pgpass ?
# INSTANCE=/home/postgres/db2.env:/PATH/TO/.pgpass
function postgres_main() {
if [ -z "$DBUSER" ] || [ -z "$PGDATABASE" ] ; then
exit 0
fi
EXTRA_ARGS=""
if [ ! -z "$PGUSER" ]; then
EXTRA_ARGS=$EXTRA_ARGS" -U $PGUSER"
fi
if [ ! -z "$PGPORT" ]; then
EXTRA_ARGS=$EXTRA_ARGS" -p $PGPORT"
fi
if [ ! -z "$PGPASSFILE" ]; then
export_PGPASSFILE="export PGPASSFILE=$PGPASSFILE; "
fi
DATABASES="$(echo "SELECT datname FROM pg_database WHERE datistemplate = false;" |\
su - "$DBUSER" -c "$export_PGPASSFILE $psql -X -d $PGDATABASE ${EXTRA_ARGS} -t -A -F';'")"
ECHO_DATABASES="$(echo "$DATABASES" | sed 's/^/\\echo /')"
POSTGRES_VERSION=$(su - "$DBUSER" -c "$psql -X -V -d $PGDATABASE ${EXTRA_ARGS} | egrep -o '[0-9]{1,}\.[0-9]{1,}'")
postgres_sessions
postgres_simple_queries
postgres_stats
postgres_version
postgres_bloat
}
MK_CONFFILE=$MK_CONFDIR/postgres.cfg
if [ -e "$MK_CONFFILE" ]; then
postgres_instances
DBUSER=$(grep DBUSER "$MK_CONFFILE" | sed 's/.*=//g')
cat "$MK_CONFFILE" | while read line
do
case $line in
INSTANCE*)
instance=$line
;;
*)
instance=
;;
esac
if [ ! -z "$instance" ]; then
instance_path=$(echo "$instance" | sed 's/.*=\(.*\):.*:.*$/\1/g')
instance_name=$(echo "$instance_path" | sed -e 's/.*\/\(.*\)/\1/g' -e 's/\.env$//g')
if [ ! -z "$instance_name" ]; then
INSTANCE_SECTION="\n[[[$instance_name]]]"
else
INSTANCE_SECTION=""
fi
psql="/$DBUSER/$(grep "^export PGVERSION=" "$instance_path" |
sed -e 's/.*=//g' -e 's/\s*#.*$//g')/bin/psql"
PGUSER=$(echo "$instance" | sed 's/.*=.*:\(.*\):.*$/\1/g')
PGPASSFILE="$(echo "$instance" | sed 's/.*=.*:.*:\(.*\)$/\1/g')"
PGDATABASE=$(grep "^export PGDATABASE=" "$instance_path" |
sed -e 's/.*=//g' -e 's/\s*#.*$//g')
PGPORT=$(grep "^export PGPORT=" "$instance_path" |
sed -e 's/.*=//g' -e 's/\s*#.*$//g')
# Fallback
if [ ! -f "$psql" ]; then
psql="$(cat $instance_path | grep "^export PGHOME=" |
sed -e 's/.*=//g' -e 's/\s*#.*$//g')/psql"
fi
postgres_main
fi
done
else
if id pgsql >/dev/null 2>&1; then
DBUSER=pgsql
elif id postgres >/dev/null 2>&1; then
DBUSER=postgres
else
exit 0
fi
INSTANCE_SECTION=""
postgres_instances "$DBUSER"
psql="psql"
PGDATABASE=postgres
postgres_main
fi

View File

@ -0,0 +1,504 @@
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# This agent plugin has been built to collect information from SAP R/3 systems
# using RFC calls. It needs the python module sapnwrfc (available in Check_MK
# git at agents/sap/sapnwrfc) and the nwrfcsdk (can be downloaded from SAP
# download portal) installed to be working. You can configure the agent plugin
# using the configuration file /etc/check_mk/sap.cfg (a sample file can be
# found in Check_MK git at agents/sap/sap.cfg) to tell it how to connect to
# your SAP instance and which values you want to fetch from your system to be
# forwarded to and checked by Check_MK.
#
# This current agent has been developed and tested with:
# python-sapnwrfc-0.19
#
# During development the "CCMS_Doku.pdf" was really helpful.
import os, sys, fcntl
import time, datetime
# sapnwrfc needs to know where the libs are located. During
# development the import failed, since the module did not
# find the libraries. So we preload the library to have it
# already loaded.
try:
import sapnwrfc
except ImportError, e:
if 'sapnwrfc.so' in str(e):
sys.stderr.write(
'Unable to find the library sapnwrfc.so. Maybe you need to put a file pointing to\n'
'the sapnwrfc library directory into the /etc/ld.so.conf.d directory. For example\n'
'create the file /etc/ld.so.conf.d/sapnwrfc.conf containing the path\n'
'"/usr/sap/nwrfcsdk/lib" and run "ldconfig" afterwards.\n'
)
sys.exit(1)
elif 'No module named sapnwrfc' in str(e):
sys.stderr.write("Missing the Python module sapnwfrc.\n")
sys.exit(1)
else:
raise
# #############################################################################
# This sign is used to separate the path parts given in the config
SEPARATOR = '/'
# This are the different classes of monitoring objects which
# can be found in the tree.
#
# Summarizs information from several subnodes
MTE_SUMMARY = '050'
# A monitoring object which has several subnodes which lead to the status
# of this object. For example it is the "CPU" object on a host
MTE_MON_OBJ = '070'
# Contains performance information (which can be used to create graphs from)
MTE_PERFORMANCE = '100'
# Might contain several messages
MTE_MSG_CONTAINER = '101'
# Contains a single status message
MTE_SINGLE_MSG = '102'
# This is a long text label without status
MTE_LONG_TXT = '110'
# This is a short text label without status
MTE_SHORT_TXT = '111'
# Is a "folder" which has no own state, just computed by its childs
MTE_VIRTUAL = '199'
# This map converts between the SAP color codes (key values) and the
# nagios state codes and strings
STATE_VALUE_MAP = {
0: (0, 'OK'), # GRAY (inactive or no current info available) -> OK
1: (0, 'OK'), # GREEN -> OK
2: (1, 'WARN'), # YELLOW -> WARNING
3: (2, 'CRIT'), # RED -> CRITICAL
}
STATE_LOGWATCH_MAP = [ 'O', 'O', 'W', 'C' ]
# Monitoring objects of these classes are skipped during processing
SKIP_MTCLASSES = [
MTE_VIRTUAL,
MTE_SUMMARY,
MTE_MON_OBJ,
MTE_SHORT_TXT,
MTE_LONG_TXT,
]
MK_CONFDIR = os.getenv("MK_CONFDIR") or "/etc/check_mk"
MK_VARDIR = os.getenv("MK_VARDIR") or "/var/lib/check_mk_agent"
STATE_FILE = MK_VARDIR + '/sap.state'
state_file_changed = False
# #############################################################################
# Settings to be used to connect to the SAP R/3 host.
local_cfg = {
'ashost': 'localhost',
'sysnr': '00',
'client': '100',
'user': '',
'passwd': '',
'trace': '3',
'loglevel': 'warn',
#'lang': 'EN',
}
# A list of strings, while the string must match the full path to one or
# several monitor objects. We use unix shell patterns during matching, so
# you can use several chars as placeholders:
#
# * matches everything
# ? matches any single character
# [seq] matches any character in seq
# [!seq] matches any character not in seq
#
# The * matches the whole following string and does not end on next "/".
# For examples, take a look at the default config file (/etc/check_mk/sap.cfg).
monitor_paths = [
'SAP CCMS Monitor Templates/Dialog Overview/*',
]
monitor_types = []
config_file = MK_CONFDIR + '/sap.cfg'
cfg = {}
if os.path.exists(config_file):
execfile(config_file)
if type(cfg) == dict:
cfg = [ cfg ]
else:
cfg = [ local_cfg ]
# Load the state file into memory
try:
states = eval(file(STATE_FILE).read())
except IOError:
states = {}
# index of all logfiles which have been found in a run. This is used to
# remove logfiles which are not available anymore from the states dict.
logfiles = []
# #############################################################################
#
# HELPERS
#
import fnmatch
def to_be_monitored(path, toplevel_match = False):
for rule in monitor_paths:
if toplevel_match and rule.count('/') > 1:
rule = '/'.join(rule.split('/')[:2])
if fnmatch.fnmatch(path, rule):
return True
return False
def node_path(tree, node, path = ''):
if path:
path = node['MTNAMESHRT'].rstrip() + SEPARATOR + path
else:
path = node['MTNAMESHRT'].rstrip()
if node['ALPARINTRE'] > 0:
parent_node = tree[node['ALPARINTRE'] - 1]
return node_path(tree, parent_node, path)
return path
#
# API ACCESS FUNCTIONS
#
def query(what, params, debug = False):
fd = conn.discover(what)
if debug:
sys.stdout.write("Name: %s Params: %s\n" % (fd.name, fd.handle.parameters))
sys.stdout.write("Given-Params: %s\n" % params)
f = fd.create_function_call()
for key, val in params.items():
getattr(f, key)(val)
f.invoke()
ret = f.RETURN.value
if ret['TYPE'] == 'E':
sys.stderr.write("ERROR: %s\n" % ret['MESSAGE'].strip())
return f
def login():
f = query('BAPI_XMI_LOGON', {
'EXTCOMPANY': 'Mathias Kettner GmbH',
'EXTPRODUCT': 'Check_MK SAP Agent',
'INTERFACE': 'XAL',
'VERSION': '1.0',
})
#sys.stdout.write("%s\n" % f.RETURN)
return f.SESSIONID.value
def logout():
query('BAPI_XMI_LOGOFF', {
'INTERFACE': 'XAL',
})
def mon_list(cfg):
f = query("BAPI_SYSTEM_MON_GETLIST", {
'EXTERNAL_USER_NAME': cfg['user'],
})
l = []
for mon in f.MONITOR_NAMES.value:
l.append((mon["MS_NAME"].rstrip(), mon["MONI_NAME"].rstrip()))
return l
#def ms_list( cfg ):
# f = query("BAPI_SYSTEM_MS_GETLIST", {
# 'EXTERNAL_USER_NAME': cfg['user'],
# })
# l = []
# for ms in f.MONITOR_SETS.value:
# l.append(ms['NAME'].rstrip())
# return l
def mon_tree(cfg, ms_name, mon_name):
f = query("BAPI_SYSTEM_MON_GETTREE", {
'EXTERNAL_USER_NAME': cfg['user'],
'MONITOR_NAME': {"MS_NAME": ms_name, "MONI_NAME": mon_name},
})
tree = f.TREE_NODES.value
for node in tree:
node['PATH'] = ms_name + SEPARATOR + node_path(tree, node)
return tree
def tid(node):
return {
'MTSYSID': node['MTSYSID'].strip(),
'MTMCNAME': node['MTMCNAME'].strip(),
'MTNUMRANGE': node['MTNUMRANGE'].strip(),
'MTUID': node['MTUID'].strip(),
'MTCLASS': node['MTCLASS'].strip(),
'MTINDEX': node['MTINDEX'].strip(),
'EXTINDEX': node['EXTINDEX'].strip(),
}
def mon_perfdata(cfg, node):
f = query('BAPI_SYSTEM_MTE_GETPERFCURVAL', {
'EXTERNAL_USER_NAME': cfg['user'],
'TID': tid(node),
})
value = f.CURRENT_VALUE.value['LASTPERVAL']
f = query('BAPI_SYSTEM_MTE_GETPERFPROP', {
'EXTERNAL_USER_NAME': cfg['user'],
'TID': tid(node),
})
if f.PROPERTIES.value['DECIMALS'] != 0:
value = (value + 0.0) / 10**f.PROPERTIES.value['DECIMALS']
uom = f.PROPERTIES.value['VALUNIT'].strip()
return value, uom
def mon_msg(cfg, node):
f = query('BAPI_SYSTEM_MTE_GETSMVALUE', {
'EXTERNAL_USER_NAME': cfg['user'],
'TID': tid(node),
})
data = f.VALUE.value
dt = parse_dt(data['SMSGDATE'], data['SMSGTIME'])
return (dt, data['MSG'].strip())
def parse_dt(d, t):
d = d.strip()
t = t.strip()
if not d or not t:
return None
else:
return datetime.datetime(*time.strptime(d + t, '%Y%m%d%H%M%S')[:6])
def mon_alerts(cfg, node):
f = query('BAPI_SYSTEM_MTE_GETALERTS', {
'EXTERNAL_USER_NAME': cfg['user'],
'TID': tid(node),
})
return f.ALERTS.value
def aid(alert):
return {
"ALSYSID": alert["ALSYSID"],
"MSEGNAME": alert["MSEGNAME"],
"ALUNIQNUM": alert["ALUNIQNUM"],
"ALINDEX": alert["ALINDEX"],
"ALERTDATE": alert["ALERTDATE"],
"ALERTTIME": alert["ALERTTIME"],
}
def alert_details(cfg, alert):
f = query('BAPI_SYSTEM_ALERT_GETDETAILS', {
'EXTERNAL_USER_NAME': cfg['user'],
'AID': aid(alert),
})
#prop = f.PROPERTIES.value
state = f.VALUE.value
msg = f.XMI_EXT_MSG.value['MSG'].strip()
return state, msg
def process_alerts(cfg, logs, ms_name, mon_name, node, alerts):
global state_file_changed
sid = node["MTSYSID"].strip() or 'Other'
context = node["MTMCNAME"].strip() or 'Other'
path = node["PATH"]
# Use the sid as hostname for the logs
hostname = sid
logfile = context + "/" + path
logfiles.append((hostname, logfile))
logs.setdefault(sid, {})
logs[hostname][logfile] = []
newest_log_dt = None
for alert in alerts:
dt = parse_dt(alert['ALERTDATE'], alert['ALERTTIME'])
if (hostname, logfile) in states and states[(hostname, logfile)] >= dt:
continue # skip log messages which are older than the last cached date
if not newest_log_dt or dt > newest_log_dt:
newest_log_dt = dt # store the newest log of this run
alert_state, alert_msg = alert_details(cfg, alert)
# Format lines to "logwatch" format
logs[hostname][logfile].append('%s %s %s' % (STATE_LOGWATCH_MAP[alert_state['VALUE']],
dt.strftime("%Y-%m-%d %H:%M:%S"), alert_msg))
if newest_log_dt:
# Write newest log age to cache to prevent double processing of logs
states[(hostname, logfile)] = newest_log_dt
state_file_changed = True
return logs
def check(cfg):
global conn
conn = sapnwrfc.base.rfc_connect(cfg)
login()
logs = {}
sap_data = {}
# This loop is used to collect all information from SAP
for ms_name, mon_name in mon_list(cfg):
path = ms_name + SEPARATOR + mon_name
if not to_be_monitored(path, True):
continue
tree = mon_tree(cfg, ms_name, mon_name)
for node in tree:
if not to_be_monitored(node['PATH']):
continue
#sys.stdout.write("%s\n" % node["PATH"])
status_details = ''
perfvalue = '-'
uom = '-'
# Use precalculated states
state = {
'VALUE': node['ACTUALVAL'],
'SEVERITY': node['ACTUALSEV'],
}
if state['VALUE'] not in STATE_VALUE_MAP:
sys.stdout.write('UNHANDLED STATE VALUE\n')
sys.exit(1)
#
# Handle different object classes individually
# to get details about them
#
if monitor_types and node['MTCLASS'] not in monitor_types:
continue # Skip unwanted classes if class filtering is enabled
if node['MTCLASS'] == MTE_PERFORMANCE:
perfvalue, this_uom = mon_perfdata(cfg, node)
uom = this_uom and this_uom or uom
elif node['MTCLASS'] == MTE_SINGLE_MSG:
status_details = "%s: %s" % mon_msg(cfg, node)
elif node['MTCLASS'] == MTE_MSG_CONTAINER:
alerts = mon_alerts(cfg, node)
logs = process_alerts(cfg, logs, ms_name, mon_name, node, alerts)
if len(alerts) > 0:
last_alert = alerts[-1]
dt = parse_dt(last_alert["ALERTDATE"], last_alert["ALERTTIME"])
alert_state, alert_msg = alert_details(cfg, last_alert)
last_msg = '%s: %s - %s' % (dt, STATE_VALUE_MAP[alert_state['VALUE']][1], alert_msg)
status_details = '%d Messages, Last: %s' % (len(alerts), last_msg)
else:
status_details = 'The log is empty'
elif node['MTCLASS'] not in SKIP_MTCLASSES:
# Add an error to output on unhandled classes
status_details = "UNHANDLED MTCLASS", node['MTCLASS']
if node['MTCLASS'] not in SKIP_MTCLASSES:
sid = node["MTSYSID"].strip() or 'Other'
context = node["MTMCNAME"].strip() or 'Other'
path = node["PATH"]
sap_data.setdefault(sid, [])
sap_data[sid].append("%s\t%d\t%3d\t%s\t%s\t%s\t%s" % (context, state['VALUE'],
state['SEVERITY'], path, perfvalue, uom, status_details))
for host, host_sap in sap_data.items():
sys.stdout.write('<<<<%s>>>>\n' % host)
sys.stdout.write('<<<sap:sep(9)>>>\n')
sys.stdout.write('%s\n' % '\n'.join(host_sap))
sys.stdout.write('<<<<>>>>\n')
for host, host_logs in logs.items():
sys.stdout.write('<<<<%s>>>>\n' % host)
sys.stdout.write('<<<logwatch>>>\n')
for log, lines in host_logs.items():
sys.stdout.write('[[[%s]]]\n' % log)
if lines:
sys.stdout.write('\n'.join(lines) + '\n')
sys.stdout.write('<<<<>>>>\n')
logout()
conn.close()
# It is possible to configure multiple SAP instances to monitor. Loop them all, but
# do not terminate when one connection failed
processed_all = True
try:
for entry in cfg:
try:
check(entry)
sys.stdout.write('<<<sap_state:sep(9)>>>\n%s\tOK\n' % entry['ashost'])
except sapnwrfc.RFCCommunicationError, e:
sys.stderr.write('ERROR: Unable to connect (%s)\n' % e)
sys.stdout.write('<<<sap_state:sep(9)>>>\n%s\tUnable to connect (%s)\n' %\
(entry['ashost'], e))
processed_all = False
except Exception, e:
sys.stderr.write('ERROR: Unhandled exception (%s)\n' % e)
sys.stdout.write('<<<sap_state:sep(9)>>>\n%s\tUnhandled exception (%s)\n' %\
(entry['ashost'], e))
processed_all = False
# Now check whether or not an old logfile needs to be removed. This can only
# be done this way, when all hosts have been reached. Otherwise the cleanup
# is skipped.
if processed_all:
for key in states.keys():
if key not in logfiles:
state_file_changed = True
del states[key]
# Only write the state file once per run. And only when it has been changed
if state_file_changed:
new_file = STATE_FILE + '.new'
fd = os.open(new_file, os.O_WRONLY | os.O_CREAT)
fcntl.flock(fd, fcntl.LOCK_EX)
os.write(fd, repr(states))
os.close(fd)
os.rename(STATE_FILE+'.new', STATE_FILE)
except Exception, e:
sys.stderr.write('ERROR: Unhandled exception (%s)\n' % e)
sys.exit(0)

View File

@ -0,0 +1,78 @@
#!/bin/bash
# +------------------------------------------------------------------+
# | ____ _ _ __ __ _ __ |
# | / ___| |__ ___ ___| | __ | \/ | |/ / |
# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
# | | |___| | | | __/ (__| < | | | | . \ |
# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
# | |
# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk 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 in version 2. check_mk is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# tails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
# cat sapservices
##!/bin/sh
#LIBPATH=/usr/sap/C01/DVEBMGS25/exe:$LIBPATH; export LIBPATH; /usr/sap/C01/DVEBMGS25/exe/sapstartsrv pf=/usr/sap/C01/SYS/profile/START_DVEBMGS25_sap10c1 -D -u c01adm
#LIBPATH=/usr/sap/DC1/SMDA98/exe:$LIBPATH; export LIBPATH; /usr/sap/DC1/SMDA98/exe/sapstartsrv pf=/usr/sap/DC1/SYS/profile/DC1_SMDA98_sap10c1 -D -u dc1adm
#LIBPATH=/usr/sap/C02/DVEBMGS37/exe:$LIBPATH; export LIBPATH; /usr/sap/C02/DVEBMGS37/exe/sapstartsrv pf=/usr/sap/C02/SYS/profile/START_DVEBMGS37_sap10c1 -D -u c02adm
#LIBPATH=/usr/sap/DAA/SMDA97/exe:$LIBPATH; export LIBPATH; /usr/sap/DAA/SMDA97/exe/sapstartsrv pf=/usr/sap/DAA/SYS/profile/DAA_SMDA97_sap10c1 -D -u daaadm
#LIBPATH=/usr/sap/DDB/SMDA96/exe:$LIBPATH; export LIBPATH; /usr/sap/DDB/SMDA96/exe/sapstartsrv pf=/usr/sap/DDB/SYS/profile/DDB_SMDA96_sap10c1d -D -u ddbadm
# <<<aix_sap_processlist:sep(44)>>>
# [69]
# 05.06.2015 05:44:36
# GetProcessList
# OK
# name, description, dispstatus, textstatus, starttime, elapsedtime, pid
# msg_server, MessageServer, GREEN, Running, 2015 06 01 02:28:51, 99:15:45, 17563810
# enserver, EnqueueServer, GREEN, Running, 2015 06 01 02:28:51, 99:15:45, 15466710
# gwrd, Gateway, GREEN, Running, 2015 06 01 02:28:51, 99:15:45, 25428046
# [68]
# 05.06.2015 05:44:36
# GetProcessList
# OK
# name, description, dispstatus, textstatus, starttime, elapsedtime, pid
# jstart, J2EE Server, GREEN, All processes running, 2015 06 01 02:29:06, 99:15:30, 18087996
# igswd_mt, IGS Watchdog, GREEN, Running, 2015 06 01 02:29:06, 99:15:30, 9765042
#
# <<<aix_sap_processlist:sep(44)>>>
# GetProcessList
# FAIL: NIECONN_REFUSED (Connection refused), NiRawConnect failed in plugin_fopen()
sapservices="/usr/sap/sapservices"
if [ -r "$sapservices" ]; then
echo "<<<aix_sap_processlist:sep(44)>>>"
# loop over ids
cat $sapservices | while read LINE
do
command=$(echo $LINE | grep "^LIBPATH=/usr/sap/" | grep -v "^LIBPATH=/usr/sap/D" | awk -F" " '{print $5}')
if [ -n "$command" ]; then
id2=$(echo $command | awk -F"/" '{print $4}')
path=$(echo "/sapmnt/$id2/exe")
sappfpar=$(find $path -name sappfpar | head -1)
sapcontrol=$(find $path -name sapcontrol | head -1)
libpath=$(find $path -name libicuuc\*.a | head -1 | sed -e 's,/[^ /]*$,,')
id=$(LIBPATH=$LIBPATH:$libpath $sappfpar SAPSYSTEM $command)
echo -n "[$id]"
LIBPATH=$LIBPATH:$libpath $sapcontrol -nr $id -function GetProcessList
fi
done
fi

Some files were not shown because too many files have changed in this diff Show More