Compare commits
432 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9546949945 | ||
|
|
8ff048d055 | ||
|
|
95a1c6e7fb | ||
|
|
0b1a4a0f30 | ||
|
|
22b48e296a | ||
|
|
c696ebf53c | ||
|
|
a0686b7d2b | ||
|
|
8d94be8924 | ||
|
|
e97ac5033f | ||
|
|
44771a0049 | ||
|
|
32aae8f57a | ||
|
|
8207e23cd9 | ||
|
|
a469029698 | ||
|
|
203d866643 | ||
|
|
1488e5ec4d | ||
|
|
af66138a17 | ||
|
|
5f060d60a7 | ||
|
|
73ccbb69ea | ||
|
|
be60440b20 | ||
|
|
837efb78e6 | ||
|
|
4a62a290d8 | ||
|
|
018399cb1f | ||
|
|
646a576358 | ||
|
|
d8e19cd79a | ||
|
|
757cb0cf23 | ||
|
|
7d92ab335a | ||
|
|
46c6d6f656 | ||
|
|
46260749c1 | ||
|
|
50664fe115 | ||
|
|
c480bd94db | ||
|
|
79923a939b | ||
|
|
327b22113a | ||
|
|
12160ab539 | ||
|
|
2462ea0892 | ||
|
|
8be09eadd4 | ||
|
|
98bc96c911 | ||
|
|
b0fce6a80d | ||
|
|
53b8a21d1e | ||
|
|
1346492d72 | ||
|
|
e5bb8d7992 | ||
|
|
49594b8435 | ||
|
|
3bd37a7906 | ||
|
|
e070a85ae0 | ||
|
|
c189278e24 | ||
|
|
2a8606bd98 | ||
|
|
18ea05c837 | ||
|
|
86c3072515 | ||
|
|
fccf508dde | ||
|
|
2da21f90f4 | ||
|
|
bec7f1726f | ||
|
|
74dfb9d88d | ||
|
|
02dddfc227 | ||
|
|
545016b38f | ||
|
|
0ccceaf226 | ||
|
|
a601115650 | ||
|
|
ae6267c906 | ||
|
|
ac142694f5 | ||
|
|
69b0913315 | ||
|
|
421bacd7dc | ||
|
|
573a76eedb | ||
|
|
b7948c7f40 | ||
|
|
2647d09b8f | ||
|
|
57e919d7e5 | ||
|
|
f456aa1407 | ||
|
|
d0d62892c8 | ||
|
|
a981cfa053 | ||
|
|
55290dd1e3 | ||
|
|
9c4e255994 | ||
|
|
f9c7d5f7bc | ||
|
|
49baea5f6a | ||
|
|
6209cf3933 | ||
|
|
d170a523c3 | ||
|
|
be5040e7a8 | ||
|
|
ecbaa5bfc1 | ||
|
|
25e2af7c89 | ||
|
|
605688426d | ||
|
|
0e069f1e75 | ||
|
|
e9adbf18d3 | ||
|
|
610202097a | ||
|
|
8c2c552164 | ||
|
|
b9976cf693 | ||
|
|
3261c405bd | ||
|
|
35d3328e3e | ||
|
|
e96041d76f | ||
|
|
c2034bc0c0 | ||
|
|
e8855f7621 | ||
|
|
bdb8368e89 | ||
|
|
f160db2032 | ||
|
|
de9a32a273 | ||
|
|
6ba7422c3b | ||
|
|
5cbb0ceb80 | ||
|
|
5b29358b37 | ||
|
|
90147f3dfb | ||
|
|
72873abe05 | ||
|
|
de1810ba68 | ||
|
|
7b7c765d78 | ||
|
|
806d4660cf | ||
|
|
5ae5d364bb | ||
|
|
1af67e72d4 | ||
|
|
ed268ad683 | ||
|
|
5bdd2ca02f | ||
|
|
eb59861d4d | ||
|
|
427e46a2aa | ||
|
|
68a8649292 | ||
|
|
2aff8709a5 | ||
|
|
62c3add888 | ||
|
|
3ac878db62 | ||
|
|
c247cd8fea | ||
|
|
b6772b7280 | ||
|
|
807a3df9d1 | ||
|
|
491d60e267 | ||
|
|
4811eafd67 | ||
|
|
8dedbb9620 | ||
|
|
dd8454161f | ||
|
|
9421f2cddd | ||
|
|
d8c4f78ec1 | ||
|
|
54296da647 | ||
|
|
357102fdb5 | ||
|
|
7e15a9e181 | ||
|
|
12e0b2d6f7 | ||
|
|
11b40bf32f | ||
|
|
8d2b53373f | ||
|
|
9ecc49e592 | ||
|
|
4f34f7083b | ||
|
|
2a6df875ec | ||
|
|
51c83116a2 | ||
|
|
74435aac76 | ||
|
|
5dfdb5b5f9 | ||
|
|
ac892a3f3d | ||
|
|
1a2e99f559 | ||
|
|
e97bba0524 | ||
|
|
0538f0c524 | ||
|
|
fc3e35868d | ||
|
|
f1e0cfea1c | ||
|
|
56efef69ba | ||
|
|
668ec8a248 | ||
|
|
60912bd01c | ||
|
|
0b416e44f8 | ||
|
|
ecc4aa09d3 | ||
|
|
b921aabbed | ||
|
|
6ad8ac0b6b | ||
|
|
44e7e0e970 | ||
|
|
45820b4ce3 | ||
|
|
3a098377cb | ||
|
|
35875485ee | ||
|
|
19760be0bc | ||
|
|
b3ea33f88d | ||
|
|
5b3425a689 | ||
|
|
a3d157bde6 | ||
|
|
2c8c9264a4 | ||
|
|
0009d9b20e | ||
|
|
dd8d17232f | ||
|
|
6312b9225f | ||
|
|
68cc09fef2 | ||
|
|
0651c9de65 | ||
|
|
38261ec809 | ||
|
|
067932aebf | ||
|
|
af47511d58 | ||
|
|
36b916f27f | ||
|
|
e519811893 | ||
|
|
4803be1987 | ||
|
|
1f415db44f | ||
|
|
0e316b1d55 | ||
|
|
eb545e75fb | ||
|
|
6edb5c30d5 | ||
|
|
597ed6eaa0 | ||
|
|
2b47d7494e | ||
|
|
213a88f62f | ||
|
|
07fd2e88a2 | ||
|
|
639afe881c | ||
|
|
2e718c0e9d | ||
|
|
b0a8fc165c | ||
|
|
ba6044e9e8 | ||
|
|
7f1c13a576 | ||
|
|
63c5e35e2b | ||
|
|
62e6a7d7fa | ||
|
|
e5a3dae332 | ||
|
|
b45a7663b3 | ||
|
|
6ef904f62b | ||
|
|
6d21cf3084 | ||
|
|
32bd96b6e2 | ||
|
|
fb5da76247 | ||
|
|
e588f51824 | ||
|
|
3e419c4955 | ||
|
|
606d2bafac | ||
|
|
8ac3c49286 | ||
|
|
534aa84ed0 | ||
|
|
04d17cb580 | ||
|
|
d039006eb4 | ||
|
|
fb04f62115 | ||
|
|
3bffccc48e | ||
|
|
eef9abf0bf | ||
|
|
de5ada30b7 | ||
|
|
12f7d0a516 | ||
|
|
0aa9c7c592 | ||
|
|
2216c8dc1c | ||
|
|
984270ebe1 | ||
|
|
2e2658ab6f | ||
|
|
1370f2a76b | ||
|
|
75dedf391a | ||
|
|
7b5c640d05 | ||
|
|
aa9a21b4d0 | ||
|
|
71de8014d5 | ||
|
|
80476d19f9 | ||
|
|
15103d18ef | ||
|
|
0dbd2004ad | ||
|
|
8c92566889 | ||
|
|
fb9449038b | ||
|
|
e06c4a873d | ||
|
|
c4c28c6c82 | ||
|
|
42ff9b803a | ||
|
|
3831e9739c | ||
|
|
f196e5cca2 | ||
|
|
d3af9105ee | ||
|
|
6d685ae4d6 | ||
|
|
8381d8246a | ||
|
|
b26322fc20 | ||
|
|
1c1e8127d8 | ||
|
|
1b3b4406ff | ||
|
|
cf0b77518a | ||
|
|
afdbf44e23 | ||
|
|
ec87781956 | ||
|
|
a6ae958be7 | ||
|
|
312103ef1b | ||
|
|
c2911bb2b7 | ||
|
|
8ca5e38121 | ||
|
|
4b8ad3a8a7 | ||
|
|
f219c2649d | ||
|
|
cfde54261b | ||
|
|
71a82b0a34 | ||
|
|
b7bd2d2664 | ||
|
|
cd26a0770d | ||
|
|
46893e84c3 | ||
|
|
567dcaf79d | ||
|
|
9368c7e05f | ||
|
|
654b3e9dbe | ||
|
|
f09db490f0 | ||
|
|
30d93cfde7 | ||
|
|
41b3db7d6b | ||
|
|
2a60debceb | ||
|
|
eb30642b6f | ||
|
|
ea85e2af6b | ||
|
|
ef979a0839 | ||
|
|
e0107b1dda | ||
|
|
ccc00f913d | ||
|
|
ad3c6bdc88 | ||
|
|
8fe3891ea9 | ||
|
|
63f21952f4 | ||
|
|
361d643ce7 | ||
|
|
abe1ffaab6 | ||
|
|
fc24c91dde | ||
|
|
53cabd5ee4 | ||
|
|
2b1e8cdbee | ||
|
|
9715146495 | ||
|
|
22b0b89949 | ||
|
|
2ebc23a777 | ||
|
|
0199285319 | ||
|
|
277ab2fe44 | ||
|
|
8a96dfdc8a | ||
|
|
66fbbb940a | ||
|
|
716ea1bb3c | ||
|
|
3d701d3daa | ||
|
|
598c74657c | ||
|
|
4bd53d5ab0 | ||
|
|
70f8d54a31 | ||
|
|
4ef25a33fc | ||
|
|
f5dd90a8dd | ||
|
|
a84defd689 | ||
|
|
1cf88d9540 | ||
|
|
644a0ee8c8 | ||
|
|
e9d5dc8fee | ||
|
|
8003202beb | ||
|
|
b46432b5b6 | ||
|
|
5e3f03df06 | ||
|
|
8ab8e6679a | ||
|
|
786b896018 | ||
|
|
40723f8705 | ||
|
|
2a0721bddf | ||
|
|
ff01e4a5e7 | ||
|
|
6794aff77c | ||
|
|
636f2a36b1 | ||
|
|
eee652cefe | ||
|
|
6d45cd45d1 | ||
|
|
f5fb135793 | ||
|
|
6bf32c978a | ||
|
|
8d3011fb9c | ||
|
|
9260066fa3 | ||
|
|
5e45c5805b | ||
|
|
db4de12767 | ||
|
|
d429795737 | ||
|
|
276219a691 | ||
|
|
03c1df98f4 | ||
|
|
79ba750dd5 | ||
|
|
1d0e187838 | ||
|
|
ad1e48aa2d | ||
|
|
7032eea045 | ||
|
|
bdb970203c | ||
|
|
fa4f5abc78 | ||
|
|
0c7b05b233 | ||
|
|
4ca98b5f17 | ||
|
|
4e00c78410 | ||
|
|
17adb19c0d | ||
|
|
1db936e253 | ||
|
|
7194ba7e0e | ||
|
|
59b9b6f091 | ||
|
|
c1ec8d15f3 | ||
|
|
24ba6abc6b | ||
|
|
f6c1bba3b6 | ||
|
|
a606961a22 | ||
|
|
cafe0e4ec2 | ||
|
|
e28c1266cf | ||
|
|
c1605a4f22 | ||
|
|
7aeb55de70 | ||
|
|
8ca65f9fda | ||
|
|
94524d1156 | ||
|
|
a1ed03478b | ||
|
|
402a6379b9 | ||
|
|
5d45bcd552 | ||
|
|
f1fa64c170 | ||
|
|
50fc78564c | ||
|
|
3e5863dc8a | ||
|
|
94b447a9c5 | ||
|
|
78d769797f | ||
|
|
672baae126 | ||
|
|
e942d71ed2 | ||
|
|
f5d24cf86c | ||
|
|
f63b1cd56d | ||
|
|
66719b3cda | ||
|
|
a5e9f6a6fc | ||
|
|
f821afdf3e | ||
|
|
2c61de83c6 | ||
|
|
6da6f75b88 | ||
|
|
a55807a708 | ||
|
|
fce86b0d08 | ||
|
|
d26b503dca | ||
|
|
5363839ac8 | ||
|
|
715a4bf393 | ||
|
|
8f83ecee65 | ||
|
|
2eed4bda42 | ||
|
|
f4e1e24ca7 | ||
|
|
05c540e6cc | ||
|
|
9656390c87 | ||
|
|
4b6470d1e1 | ||
|
|
56471c2fe4 | ||
|
|
9f56e4a582 | ||
|
|
12ea860eba | ||
|
|
b876c29862 | ||
|
|
6bbce039aa | ||
|
|
1584f20220 | ||
|
|
dcad5abc1c | ||
|
|
ab73261fd4 | ||
|
|
05b75c0a44 | ||
|
|
ba7ef0788e | ||
|
|
3aaa80974e | ||
|
|
995ca32eee | ||
|
|
bf5f48b85b | ||
|
|
d6e386a555 | ||
|
|
a0a71f683c | ||
|
|
7adf88b55b | ||
|
|
8a9d47fc4b | ||
|
|
2a0a69c917 | ||
|
|
aeab8f55bd | ||
|
|
9407050598 | ||
|
|
b99da63306 | ||
|
|
f0d6cfaae4 | ||
|
|
3120628d8a | ||
|
|
2654384461 | ||
|
|
eac3b25dc9 | ||
|
|
7788f91dd5 | ||
|
|
d0c9b7170c | ||
|
|
d84caa5528 | ||
|
|
2ab72bdf94 | ||
|
|
f6833fde29 | ||
|
|
fa8a50b525 | ||
|
|
d80c6bbf1d | ||
|
|
6f3ac4bf2a | ||
|
|
a6dc81a38e | ||
|
|
81c5ce40d4 | ||
|
|
c59f45a37b | ||
|
|
1b01f908e3 | ||
|
|
9720812a78 | ||
|
|
05b4066ba6 | ||
|
|
50c458b6cc | ||
|
|
2ab6d61a61 | ||
|
|
b77a39bdff | ||
|
|
d3f7432861 | ||
|
|
7f3ef5bf85 | ||
|
|
659fb3eb82 | ||
|
|
d1315bb092 | ||
|
|
b4ac0e2e7c | ||
|
|
bfe619272e | ||
|
|
963f025011 | ||
|
|
b8cdcaeb75 | ||
|
|
6b6dc75152 | ||
|
|
23647445d7 | ||
|
|
e60dda5027 | ||
|
|
f39551952f | ||
|
|
a9538052bf | ||
|
|
267d5179f5 | ||
|
|
10b8c93da4 | ||
|
|
c999f0c2cd | ||
|
|
54615dc03b | ||
|
|
9aea95ce85 | ||
|
|
80f48291f3 | ||
|
|
1a164cee3e | ||
|
|
da494cdc7c | ||
|
|
06635dfa75 | ||
|
|
a56fb3c8cd | ||
|
|
2dc3c62bbd | ||
|
|
0339d0caa8 | ||
|
|
3b5678dd91 | ||
|
|
82ff34234d | ||
|
|
f3d1369764 | ||
|
|
ed61444d82 | ||
|
|
ce0d68a8ba | ||
|
|
74aadbadb8 | ||
|
|
58f41eddd9 | ||
|
|
4726445ec4 | ||
|
|
3a85384377 | ||
|
|
d20b529508 | ||
|
|
7199f558e8 | ||
|
|
674cb24a1a | ||
|
|
02c7336315 | ||
|
|
cde052d819 | ||
|
|
989cc8d236 | ||
|
|
1186d63653 | ||
|
|
6e68d6dda0 | ||
|
|
7d876701b3 | ||
|
|
dbbb483853 | ||
|
|
85e9473d56 | ||
|
|
427d424707 | ||
|
|
f90c5fafa4 |
1
.ackrc
@@ -1,2 +1,3 @@
|
|||||||
--ignore-dir=old/
|
--ignore-dir=old/
|
||||||
--ignore-dir=tmp/
|
--ignore-dir=tmp/
|
||||||
|
--ignore-dir=vendor/
|
||||||
|
|||||||
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
docker
|
||||||
19
.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
; This file is for unifying the coding style for different editors and IDEs.
|
||||||
|
; Plugins are available for notepad++, emacs, vim, gedit,
|
||||||
|
; textmate, visual studio, and more.
|
||||||
|
;
|
||||||
|
; See http://editorconfig.org for details.
|
||||||
|
|
||||||
|
# Top-most EditorConfig file.
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
9
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## Versions:
|
||||||
|
|
||||||
|
* mgmt version (eg: `mgmt --version`):
|
||||||
|
|
||||||
|
* operating system/distribution (eg: `uname -a`):
|
||||||
|
|
||||||
|
* golang version (eg: `go version`):
|
||||||
|
|
||||||
|
## Description:
|
||||||
4
.gitignore
vendored
@@ -1,9 +1,11 @@
|
|||||||
|
.idea/
|
||||||
.omv/
|
.omv/
|
||||||
.ssh/
|
.ssh/
|
||||||
.vagrant/
|
.vagrant/
|
||||||
mgmt-documentation.pdf
|
|
||||||
old/
|
old/
|
||||||
tmp/
|
tmp/
|
||||||
*_stringer.go
|
*_stringer.go
|
||||||
mgmt
|
mgmt
|
||||||
|
mgmt.static
|
||||||
|
mgmt.iml
|
||||||
rpmbuild/
|
rpmbuild/
|
||||||
|
|||||||
18
.gitmodules
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[submodule "vendor/github.com/coreos/etcd"]
|
||||||
|
path = vendor/github.com/coreos/etcd
|
||||||
|
url = https://github.com/coreos/etcd/
|
||||||
|
[submodule "vendor/google.golang.org/grpc"]
|
||||||
|
path = vendor/google.golang.org/grpc
|
||||||
|
url = https://github.com/grpc/grpc-go
|
||||||
|
[submodule "vendor/github.com/grpc-ecosystem/grpc-gateway"]
|
||||||
|
path = vendor/github.com/grpc-ecosystem/grpc-gateway
|
||||||
|
url = https://github.com/grpc-ecosystem/grpc-gateway
|
||||||
|
[submodule "vendor/gopkg.in/fsnotify.v1"]
|
||||||
|
path = vendor/gopkg.in/fsnotify.v1
|
||||||
|
url = https://gopkg.in/fsnotify.v1
|
||||||
|
[submodule "vendor/github.com/purpleidea/go-systemd"]
|
||||||
|
path = vendor/github.com/purpleidea/go-systemd
|
||||||
|
url = https://github.com/purpleidea/go-systemd
|
||||||
|
[submodule "vendor/honnef.co/go/augeas"]
|
||||||
|
path = vendor/honnef.co/go/augeas
|
||||||
|
url = https://github.com/dominikh/go-augeas/
|
||||||
16
.travis.yml
@@ -1,15 +1,22 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.4.3
|
- 1.6.x
|
||||||
- 1.5.2
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
- tip
|
- tip
|
||||||
|
go_import_path: github.com/purpleidea/mgmt
|
||||||
|
sudo: true
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: required
|
before_install:
|
||||||
|
- sudo apt update
|
||||||
|
- git fetch --unshallow
|
||||||
install: 'make deps'
|
install: 'make deps'
|
||||||
script: 'make test'
|
script: 'make test'
|
||||||
matrix:
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: tip
|
||||||
|
- go: 1.8.x
|
||||||
notifications:
|
notifications:
|
||||||
irc:
|
irc:
|
||||||
channels:
|
channels:
|
||||||
@@ -22,4 +29,7 @@ notifications:
|
|||||||
use_notice: false
|
use_notice: false
|
||||||
skip_join: false
|
skip_join: false
|
||||||
email:
|
email:
|
||||||
|
recipients:
|
||||||
- travis-ci@shubin.ca
|
- travis-ci@shubin.ca
|
||||||
|
on_failure: change
|
||||||
|
on_success: change
|
||||||
|
|||||||
3
AUTHORS
@@ -4,4 +4,7 @@ If you appreciate the work of one of the contributors, thank them a beverage!
|
|||||||
For a more exhaustive list please run: git log --format='%aN' | sort -u
|
For a more exhaustive list please run: git log --format='%aN' | sort -u
|
||||||
This list is sorted alphabetically by first name.
|
This list is sorted alphabetically by first name.
|
||||||
|
|
||||||
|
Felix Frank
|
||||||
James Shubin
|
James Shubin
|
||||||
|
Julien Pivotto
|
||||||
|
Paul Morgan
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Mgmt
|
Mgmt
|
||||||
Copyright (C) 2013-2016+ James Shubin and the project contributors
|
Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
Written by James Shubin <james@shubin.ca> and the project contributors
|
Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
|||||||
143
DOCUMENTATION.md
@@ -1,143 +0,0 @@
|
|||||||
#mgmt
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Mgmt
|
|
||||||
Copyright (C) 2013-2016+ James Shubin and the project contributors
|
|
||||||
Written by James Shubin <james@shubin.ca> and the project contributors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
##mgmt by [James](https://ttboj.wordpress.com/)
|
|
||||||
####Available from:
|
|
||||||
####[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/)
|
|
||||||
|
|
||||||
####This documentation is available in: [Markdown](https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md) or [PDF](https://pdfdoc-purpleidea.rhcloud.com/pdf/https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md) format.
|
|
||||||
|
|
||||||
####Table of Contents
|
|
||||||
|
|
||||||
1. [Overview](#overview)
|
|
||||||
2. [Project description - What the project does](#project-description)
|
|
||||||
3. [Setup - Getting started with mgmt](#setup)
|
|
||||||
4. [Usage/FAQ - Notes on usage and frequently asked questions](#usage-and-frequently-asked-questions)
|
|
||||||
5. [Reference - Detailed reference](#reference)
|
|
||||||
* [graph.yaml](#graph.yaml)
|
|
||||||
* [Command line](#command-line)
|
|
||||||
6. [Examples - Example configurations](#examples)
|
|
||||||
7. [Development - Background on module development and reporting bugs](#development)
|
|
||||||
8. [Authors - Authors and contact information](#authors)
|
|
||||||
|
|
||||||
##Overview
|
|
||||||
|
|
||||||
The `mgmt` tool is a research prototype to demonstrate next generation config
|
|
||||||
management techniques. Hopefully it will evolve into a useful, robust tool.
|
|
||||||
|
|
||||||
##Project Description
|
|
||||||
|
|
||||||
The mgmt tool is a distributed, event driven, config management tool, that
|
|
||||||
supports parallel execution, and librarification to be used as the management
|
|
||||||
foundation in and for, new and existing software.
|
|
||||||
|
|
||||||
##Setup
|
|
||||||
|
|
||||||
During this prototype phase, the tool can be run out of the source directory.
|
|
||||||
You'll probably want to use ```./run.sh run --file examples/graph1.yaml``` to
|
|
||||||
get started. Beware that this _can_ cause data loss. Understand what you're
|
|
||||||
doing first, or perform these actions in a virtual environment such as the one
|
|
||||||
provided by [Oh-My-Vagrant](https://github.com/purpleidea/oh-my-vagrant).
|
|
||||||
|
|
||||||
##Usage and frequently asked questions
|
|
||||||
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
|
||||||
respond by commit with the answer.)
|
|
||||||
|
|
||||||
###Why did you start this project?
|
|
||||||
|
|
||||||
I wanted a next generation config management solution that didn't have all of
|
|
||||||
the design flaws or limitations that the current generation of tools do, and no
|
|
||||||
tool existed!
|
|
||||||
|
|
||||||
###Why did you use etcd? What about consul?
|
|
||||||
|
|
||||||
Etcd and consul are both written in golang, which made them the top two
|
|
||||||
contenders for my prototype. Ultimately a choice had to be made, and etcd was
|
|
||||||
chosen, but it was also somewhat arbitrary. If there is available interest,
|
|
||||||
good reasoning, *and* patches, then we would consider either switching or
|
|
||||||
supporting both, but this is not a high priority at this time.
|
|
||||||
|
|
||||||
###You didn't answer my question, or I have a question!
|
|
||||||
|
|
||||||
It's best to ask on [IRC](https://webchat.freenode.net/?channels=#mgmtconfig)
|
|
||||||
to see if someone can help you. Once we get a big enough community going, we'll
|
|
||||||
add a mailing list. If you don't get any response from the above, you can
|
|
||||||
contact me through my [technical blog](https://ttboj.wordpress.com/contact/)
|
|
||||||
and I'll do my best to help. If you have a good question, please add it as a
|
|
||||||
patch to this documentation. I'll merge your question, and add a patch with the
|
|
||||||
answer!
|
|
||||||
|
|
||||||
##Reference
|
|
||||||
Please note that there are a number of undocumented options. For more
|
|
||||||
information on these options, please view the source at:
|
|
||||||
[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/).
|
|
||||||
If you feel that a well used option needs documenting here, please patch it!
|
|
||||||
|
|
||||||
###Overview of reference
|
|
||||||
* [graph.yaml](#graph.yaml): Main graph definition file.
|
|
||||||
* [Command line](#command-line): Command line parameters.
|
|
||||||
|
|
||||||
###graph.yaml
|
|
||||||
This is the compiled graph definition file. The format is currently
|
|
||||||
undocumented, but by looking through the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples)
|
|
||||||
you can probably figure out most of it, as it's fairly intuitive.
|
|
||||||
|
|
||||||
###Command line
|
|
||||||
The main interface to the `mgmt` tool is the command line. For the most recent
|
|
||||||
documentation, please run `mgmt --help`.
|
|
||||||
|
|
||||||
####`--file <graph.yaml>`
|
|
||||||
Point to a graph file to run.
|
|
||||||
|
|
||||||
####`--converged-timeout <seconds>`
|
|
||||||
Exit if the machine has converged for approximately this many seconds.
|
|
||||||
|
|
||||||
####`--max-runtime <seconds>`
|
|
||||||
Exit when the agent has run for approximately this many seconds. This is not
|
|
||||||
generally recommended, but may be useful for users who know what they're doing.
|
|
||||||
|
|
||||||
##Examples
|
|
||||||
For example configurations, please consult the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples) directory in the git
|
|
||||||
source repository. It is available from:
|
|
||||||
|
|
||||||
[https://github.com/purpleidea/mgmt/tree/master/examples](https://github.com/purpleidea/mgmt/tree/master/examples)
|
|
||||||
|
|
||||||
##Development
|
|
||||||
|
|
||||||
This is a project that I started in my free time in 2013. Development is driven
|
|
||||||
by all of our collective patches! Dive right in, and start hacking!
|
|
||||||
Please contact me if you'd like to invite me to speak about this at your event.
|
|
||||||
|
|
||||||
You can follow along [on my technical blog](https://ttboj.wordpress.com/).
|
|
||||||
|
|
||||||
To report any bugs, please file a ticket at: [https://github.com/purpleidea/mgmt/issues](https://github.com/purpleidea/mgmt/issues).
|
|
||||||
|
|
||||||
##Authors
|
|
||||||
|
|
||||||
Copyright (C) 2013-2016+ James Shubin and the project contributors
|
|
||||||
|
|
||||||
Please see the
|
|
||||||
[AUTHORS](https://github.com/purpleidea/mgmt/tree/master/AUTHORS) file
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
* [github](https://github.com/purpleidea/)
|
|
||||||
* [@purpleidea](https://twitter.com/#!/purpleidea)
|
|
||||||
* [https://ttboj.wordpress.com/](https://ttboj.wordpress.com/)
|
|
||||||
141
Makefile
@@ -1,5 +1,5 @@
|
|||||||
# Mgmt
|
# Mgmt
|
||||||
# Copyright (C) 2013-2016+ James Shubin and the project contributors
|
# Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
# Written by James Shubin <james@shubin.ca> and the project contributors
|
# Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
@@ -15,29 +15,71 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
SHELL = /bin/bash
|
SHELL = /usr/bin/env bash
|
||||||
.PHONY: all version program path deps run race build clean test format docs rpmbuild rpm srpm spec tar upload upload-sources upload-srpms upload-rpms copr
|
.PHONY: all art cleanart version program path deps run race generate build clean test gofmt yamlfmt format docs rpmbuild mkdirs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms copr
|
||||||
.SILENT: clean
|
.SILENT: clean
|
||||||
|
|
||||||
SVERSION := $(shell git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --tags --dirty --always)
|
SVERSION := $(or $(SVERSION),$(shell git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --tags --dirty --always))
|
||||||
VERSION := $(shell git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --tags --abbrev=0)
|
VERSION := $(or $(VERSION),$(shell git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --tags --abbrev=0))
|
||||||
PROGRAM := $(shell basename --suffix=-$(VERSION) $(notdir $(CURDIR)))
|
PROGRAM := $(shell echo $(notdir $(CURDIR)) | cut -f1 -d"-")
|
||||||
|
OLDGOLANG := $(shell go version | grep -E 'go1.3|go1.4')
|
||||||
ifeq ($(VERSION),$(SVERSION))
|
ifeq ($(VERSION),$(SVERSION))
|
||||||
RELEASE = 1
|
RELEASE = 1
|
||||||
else
|
else
|
||||||
RELEASE = untagged
|
RELEASE = untagged
|
||||||
endif
|
endif
|
||||||
ARCH = $(shell arch)
|
ARCH = $(uname -m)
|
||||||
SPEC = rpmbuild/SPECS/mgmt.spec
|
SPEC = rpmbuild/SPECS/$(PROGRAM).spec
|
||||||
SOURCE = rpmbuild/SOURCES/mgmt-$(VERSION).tar.bz2
|
SOURCE = rpmbuild/SOURCES/$(PROGRAM)-$(VERSION).tar.bz2
|
||||||
SRPM = rpmbuild/SRPMS/mgmt-$(VERSION)-$(RELEASE).src.rpm
|
SRPM = rpmbuild/SRPMS/$(PROGRAM)-$(VERSION)-$(RELEASE).src.rpm
|
||||||
SRPM_BASE = mgmt-$(VERSION)-$(RELEASE).src.rpm
|
SRPM_BASE = $(PROGRAM)-$(VERSION)-$(RELEASE).src.rpm
|
||||||
RPM = rpmbuild/RPMS/mgmt-$(VERSION)-$(RELEASE).$(ARCH).rpm
|
RPM = rpmbuild/RPMS/$(PROGRAM)-$(VERSION)-$(RELEASE).$(ARCH).rpm
|
||||||
USERNAME := $(shell cat ~/.config/copr 2>/dev/null | grep username | awk -F '=' '{print $$2}' | tr -d ' ')
|
USERNAME := $(shell cat ~/.config/copr 2>/dev/null | grep username | awk -F '=' '{print $$2}' | tr -d ' ')
|
||||||
SERVER = 'dl.fedoraproject.org'
|
SERVER = 'dl.fedoraproject.org'
|
||||||
REMOTE_PATH = 'pub/alt/$(USERNAME)/mgmt'
|
REMOTE_PATH = 'pub/alt/$(USERNAME)/$(PROGRAM)'
|
||||||
|
ifneq ($(GOTAGS),)
|
||||||
|
BUILD_FLAGS = -tags '$(GOTAGS)'
|
||||||
|
endif
|
||||||
|
|
||||||
all: docs
|
default: build
|
||||||
|
|
||||||
|
#
|
||||||
|
# art
|
||||||
|
#
|
||||||
|
art: art/mgmt_logo_default_symbol.png art/mgmt_logo_default_tall.png art/mgmt_logo_default_wide.png art/mgmt_logo_reversed_symbol.png art/mgmt_logo_reversed_tall.png art/mgmt_logo_reversed_wide.png art/mgmt_logo_white_symbol.png art/mgmt_logo_white_tall.png art/mgmt_logo_white_wide.png
|
||||||
|
|
||||||
|
cleanart:
|
||||||
|
rm -f art/mgmt_logo_default_symbol.png art/mgmt_logo_default_tall.png art/mgmt_logo_default_wide.png art/mgmt_logo_reversed_symbol.png art/mgmt_logo_reversed_tall.png art/mgmt_logo_reversed_wide.png art/mgmt_logo_white_symbol.png art/mgmt_logo_white_tall.png art/mgmt_logo_white_wide.png
|
||||||
|
|
||||||
|
# NOTE: the widths are arbitrary
|
||||||
|
art/mgmt_logo_default_symbol.png: art/mgmt_logo_default_symbol.svg
|
||||||
|
inkscape --export-background='#ffffff' --without-gui --export-png "$@" --export-width 300 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_default_tall.png: art/mgmt_logo_default_tall.svg
|
||||||
|
inkscape --export-background='#ffffff' --without-gui --export-png "$@" --export-width 400 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_default_wide.png: art/mgmt_logo_default_wide.svg
|
||||||
|
inkscape --export-background='#ffffff' --without-gui --export-png "$@" --export-width 800 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_reversed_symbol.png: art/mgmt_logo_reversed_symbol.svg
|
||||||
|
inkscape --export-background='#231f20' --without-gui --export-png "$@" --export-width 300 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_reversed_tall.png: art/mgmt_logo_reversed_tall.svg
|
||||||
|
inkscape --export-background='#231f20' --without-gui --export-png "$@" --export-width 400 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_reversed_wide.png: art/mgmt_logo_reversed_wide.svg
|
||||||
|
inkscape --export-background='#231f20' --without-gui --export-png "$@" --export-width 800 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_white_symbol.png: art/mgmt_logo_white_symbol.svg
|
||||||
|
inkscape --export-background='#231f20' --without-gui --export-png "$@" --export-width 300 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_white_tall.png: art/mgmt_logo_white_tall.svg
|
||||||
|
inkscape --export-background='#231f20' --without-gui --export-png "$@" --export-width 400 $(@:png=svg)
|
||||||
|
|
||||||
|
art/mgmt_logo_white_wide.png: art/mgmt_logo_white_wide.svg
|
||||||
|
inkscape --export-background='#231f20' --without-gui --export-png "$@" --export-width 800 $(@:png=svg)
|
||||||
|
|
||||||
|
all: docs $(PROGRAM).static
|
||||||
|
|
||||||
# show the current version
|
# show the current version
|
||||||
version:
|
version:
|
||||||
@@ -53,39 +95,56 @@ deps:
|
|||||||
./misc/make-deps.sh
|
./misc/make-deps.sh
|
||||||
|
|
||||||
run:
|
run:
|
||||||
find -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' | xargs go run -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)"
|
find . -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' | xargs go run -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)"
|
||||||
|
|
||||||
# include race flag
|
# include race flag
|
||||||
race:
|
race:
|
||||||
find -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' | xargs go run -race -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)"
|
find . -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' | xargs go run -race -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)"
|
||||||
|
|
||||||
build: mgmt
|
generate:
|
||||||
|
|
||||||
mgmt: main.go
|
|
||||||
@echo "Building: $(PROGRAM), version: $(SVERSION)."
|
|
||||||
go generate
|
go generate
|
||||||
# avoid equals sign in old golang versions eg in: -X foo=bar
|
|
||||||
if go version | grep -qE 'go1.3|go1.4'; then \
|
build: $(PROGRAM)
|
||||||
go build -ldflags "-X main.program $(PROGRAM) -X main.version $(SVERSION)" -o mgmt; \
|
|
||||||
else \
|
$(PROGRAM): main.go
|
||||||
go build -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)" -o mgmt; \
|
@echo "Building: $(PROGRAM), version: $(SVERSION)..."
|
||||||
fi
|
ifneq ($(OLDGOLANG),)
|
||||||
|
@# avoid equals sign in old golang versions eg in: -X foo=bar
|
||||||
|
time go build -ldflags "-X main.program $(PROGRAM) -X main.version $(SVERSION)" -o $(PROGRAM) $(BUILD_FLAGS);
|
||||||
|
else
|
||||||
|
time go build -i -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)" -o $(PROGRAM) $(BUILD_FLAGS);
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(PROGRAM).static: main.go
|
||||||
|
@echo "Building: $(PROGRAM).static, version: $(SVERSION)..."
|
||||||
|
go generate
|
||||||
|
ifneq ($(OLDGOLANG),)
|
||||||
|
@# avoid equals sign in old golang versions eg in: -X foo=bar
|
||||||
|
go build -a -installsuffix cgo -tags netgo -ldflags '-extldflags "-static" -X main.program $(PROGRAM) -X main.version $(SVERSION)' -o $(PROGRAM).static $(BUILD_FLAGS);
|
||||||
|
else
|
||||||
|
go build -a -installsuffix cgo -tags netgo -ldflags '-extldflags "-static" -X main.program=$(PROGRAM) -X main.version=$(SVERSION)' -o $(PROGRAM).static $(BUILD_FLAGS);
|
||||||
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
[ ! -e mgmt ] || rm mgmt
|
[ ! -e $(PROGRAM) ] || rm $(PROGRAM)
|
||||||
rm -f *_stringer.go # generated by `go generate`
|
rm -f *_stringer.go # generated by `go generate`
|
||||||
|
rm -f *_mock.go # generated by `go generate`
|
||||||
|
|
||||||
test:
|
test:
|
||||||
./test.sh
|
./test.sh
|
||||||
|
|
||||||
format:
|
gofmt:
|
||||||
find -type f -name '*.go' -not -path './old/*' -not -path './tmp/*' -exec gofmt -w {} \;
|
find . -maxdepth 3 -type f -name '*.go' -not -path './old/*' -not -path './tmp/*' -exec gofmt -w {} \;
|
||||||
find -type f -name '*.yaml' -not -path './old/*' -not -path './tmp/*' -not -path './omv.yaml' -exec ruby -e "require 'yaml'; x=YAML.load_file('{}').to_yaml.each_line.map(&:rstrip).join(10.chr)+10.chr; File.open('{}', 'w').write x" \;
|
|
||||||
|
|
||||||
docs: mgmt-documentation.pdf
|
yamlfmt:
|
||||||
|
find . -maxdepth 3 -type f -name '*.yaml' -not -path './old/*' -not -path './tmp/*' -not -path './omv.yaml' -exec ruby -e "require 'yaml'; x=YAML.load_file('{}').to_yaml.each_line.map(&:rstrip).join(10.chr)+10.chr; File.open('{}', 'w').write x" \;
|
||||||
|
|
||||||
mgmt-documentation.pdf: DOCUMENTATION.md
|
format: gofmt yamlfmt
|
||||||
pandoc DOCUMENTATION.md -o 'mgmt-documentation.pdf'
|
|
||||||
|
docs: $(PROGRAM)-documentation.pdf
|
||||||
|
|
||||||
|
$(PROGRAM)-documentation.pdf: docs/documentation.md
|
||||||
|
pandoc docs/documentation.md -o docs/'$(PROGRAM)-documentation.pdf'
|
||||||
|
|
||||||
#
|
#
|
||||||
# build aliases
|
# build aliases
|
||||||
@@ -116,21 +175,21 @@ upload: upload-sources upload-srpms upload-rpms
|
|||||||
$(RPM): $(SPEC) $(SOURCE)
|
$(RPM): $(SPEC) $(SOURCE)
|
||||||
@echo Running rpmbuild -bb...
|
@echo Running rpmbuild -bb...
|
||||||
rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \
|
rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \
|
||||||
mv rpmbuild/RPMS/$(ARCH)/mgmt-$(VERSION)-$(RELEASE).*.rpm $(RPM)
|
mv rpmbuild/RPMS/$(ARCH)/$(PROGRAM)-$(VERSION)-$(RELEASE).*.rpm $(RPM)
|
||||||
|
|
||||||
$(SRPM): $(SPEC) $(SOURCE)
|
$(SRPM): $(SPEC) $(SOURCE)
|
||||||
@echo Running rpmbuild -bs...
|
@echo Running rpmbuild -bs...
|
||||||
rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC)
|
rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC)
|
||||||
# renaming is not needed because we aren't using the dist variable
|
# renaming is not needed because we aren't using the dist variable
|
||||||
#mv rpmbuild/SRPMS/mgmt-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM)
|
#mv rpmbuild/SRPMS/$(PROGRAM)-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM)
|
||||||
|
|
||||||
#
|
#
|
||||||
# spec
|
# spec
|
||||||
#
|
#
|
||||||
$(SPEC): rpmbuild/ mgmt.spec.in
|
$(SPEC): rpmbuild/ spec.in
|
||||||
@echo Running templater...
|
@echo Running templater...
|
||||||
#cat mgmt.spec.in > $(SPEC)
|
cat spec.in > $(SPEC)
|
||||||
sed -e s/__VERSION__/$(VERSION)/ -e s/__RELEASE__/$(RELEASE)/ < mgmt.spec.in > $(SPEC)
|
sed -e s/__PROGRAM__/$(PROGRAM)/g -e s/__VERSION__/$(VERSION)/g -e s/__RELEASE__/$(RELEASE)/g < spec.in > $(SPEC)
|
||||||
# append a changelog to the .spec file
|
# append a changelog to the .spec file
|
||||||
git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC)
|
git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC)
|
||||||
|
|
||||||
@@ -140,7 +199,7 @@ $(SPEC): rpmbuild/ mgmt.spec.in
|
|||||||
$(SOURCE): rpmbuild/
|
$(SOURCE): rpmbuild/
|
||||||
@echo Running git archive...
|
@echo Running git archive...
|
||||||
# use HEAD if tag doesn't exist yet, so that development is easier...
|
# use HEAD if tag doesn't exist yet, so that development is easier...
|
||||||
git archive --prefix=mgmt-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist. Using HEAD instead.' && git archive --prefix=mgmt-$(VERSION)/ -o $(SOURCE) HEAD)
|
git archive --prefix=$(PROGRAM)-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist. Using HEAD instead.' && git archive --prefix=$(PROGRAM)-$(VERSION)/ -o $(SOURCE) HEAD)
|
||||||
# TODO: if git archive had a --submodules flag this would easier!
|
# TODO: if git archive had a --submodules flag this would easier!
|
||||||
@echo Running git archive submodules...
|
@echo Running git archive submodules...
|
||||||
# i thought i would need --ignore-zeros, but it doesn't seem necessary!
|
# i thought i would need --ignore-zeros, but it doesn't seem necessary!
|
||||||
@@ -149,14 +208,14 @@ $(SOURCE): rpmbuild/
|
|||||||
temp="$${temp#\'}"; \
|
temp="$${temp#\'}"; \
|
||||||
path=$$temp; \
|
path=$$temp; \
|
||||||
[ "$$path" = "" ] && continue; \
|
[ "$$path" = "" ] && continue; \
|
||||||
(cd $$path && git archive --prefix=mgmt-$(VERSION)/$$path/ HEAD > $$p/rpmbuild/tmp.tar && tar --concatenate --file=$$p/$(SOURCE) $$p/rpmbuild/tmp.tar && rm $$p/rpmbuild/tmp.tar); \
|
(cd $$path && git archive --prefix=$(PROGRAM)-$(VERSION)/$$path/ HEAD > $$p/rpmbuild/tmp.tar && tar --concatenate --file=$$p/$(SOURCE) $$p/rpmbuild/tmp.tar && rm $$p/rpmbuild/tmp.tar); \
|
||||||
done
|
done
|
||||||
|
|
||||||
# TODO: ensure that each sub directory exists
|
# TODO: ensure that each sub directory exists
|
||||||
rpmbuild/:
|
rpmbuild/:
|
||||||
mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
|
||||||
rpmbuild:
|
mkdirs:
|
||||||
mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
102
README.md
@@ -1,65 +1,83 @@
|
|||||||
# *mgmt*: This is: mgmt!
|
# *mgmt*: next generation config management!
|
||||||
|
|
||||||
[](http://travis-ci.org/purpleidea/mgmt)
|
[](art/)
|
||||||
[](DOCUMENTATION.md)
|
|
||||||
[](https://webchat.freenode.net/?channels=#mgmtconfig)
|
[](https://goreportcard.com/report/github.com/purpleidea/mgmt)
|
||||||
[](https://ci.centos.org/job/purpleidea-mgmt/)
|
[](http://travis-ci.org/purpleidea/mgmt)
|
||||||
[](https://copr.fedoraproject.org/coprs/purpleidea/mgmt/)
|
[](https://godoc.org/github.com/purpleidea/mgmt)
|
||||||
|
[](https://webchat.freenode.net/?channels=#mgmtconfig)
|
||||||
|
[](https://ci.centos.org/job/purpleidea-mgmt/)
|
||||||
|
|
||||||
## Community:
|
## Community:
|
||||||
Come join us on IRC in [#mgmtconfig](https://webchat.freenode.net/?channels=#mgmtconfig) on Freenode!
|
Come join us in the `mgmt` community!
|
||||||
You may like the [#mgmtconfig](https://twitter.com/hashtag/mgmtconfig) hashtag if you're on [Twitter](https://twitter.com/#!/purpleidea).
|
|
||||||
|
|
||||||
## Questions:
|
| Medium | Link |
|
||||||
Please join the [#mgmtconfig](https://webchat.freenode.net/?channels=#mgmtconfig) IRC community!
|
|---|---|---|
|
||||||
If you have a well phrased question that might benefit others, consider asking it by sending a patch to the documentation [FAQ](https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md#usage-and-frequently-asked-questions) section. I'll merge your question, and a patch with the answer!
|
| IRC | [#mgmtconfig](https://webchat.freenode.net/?channels=#mgmtconfig) on Freenode |
|
||||||
|
| Twitter | [@mgmtconfig](https://twitter.com/mgmtconfig) & [#mgmtconfig](https://twitter.com/hashtag/mgmtconfig) |
|
||||||
|
| Mailing list | [mgmtconfig-list@redhat.com](https://www.redhat.com/mailman/listinfo/mgmtconfig-list) |
|
||||||
|
|
||||||
## Quick start:
|
## Status:
|
||||||
* Either get the golang dependencies on your own, or run `make deps` if you're comfortable with how we install them.
|
Mgmt is a fairly new project.
|
||||||
* Run `make build` to get a fresh built `mgmt` binary.
|
We're working towards being minimally useful for production environments.
|
||||||
* Run `cd $(mktemp --tmpdir -d tmp.XXX) && etcd` to get etcd running. The `mgmt` software will do this automatically for you in the future.
|
We aren't feature complete for what we'd consider a 1.x release yet.
|
||||||
* Run `time ./mgmt run --file examples/graph0.yaml --converged-timeout=1` to try out a very simple example!
|
With your help you'll be able to influence our design and get us there sooner!
|
||||||
* To run continuously in the default mode of operation, omit the `--converged-timeout` option.
|
|
||||||
* Have fun hacking on our future technology!
|
|
||||||
|
|
||||||
## Examples:
|
|
||||||
Please look in the [examples/](examples/) folder for more examples!
|
|
||||||
|
|
||||||
## Documentation:
|
## Documentation:
|
||||||
Please see: [DOCUMENTATION.md](DOCUMENTATION.md) or [PDF](https://pdfdoc-purpleidea.rhcloud.com/pdf/https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md).
|
Please read, enjoy and help improve our documentation!
|
||||||
|
|
||||||
|
| Documentation | Additional Notes |
|
||||||
|
|---|---|
|
||||||
|
| [general documentation](docs/documentation.md) | for everyone |
|
||||||
|
| [quick start guide](docs/quick-start-guide.md) | for mgmt developers |
|
||||||
|
| [resource guide](docs/resource-guide.md) | for mgmt developers |
|
||||||
|
| [godoc API reference](https://godoc.org/github.com/purpleidea/mgmt) | for mgmt developers |
|
||||||
|
| [prometheus guide](docs/prometheus.md) | for everyone |
|
||||||
|
| [puppet guide](docs/puppet-guide.md) | for puppet sysadmins |
|
||||||
|
|
||||||
|
## Questions:
|
||||||
|
Please ask in the [community](#community)!
|
||||||
|
If you have a well phrased question that might benefit others, consider asking it by sending a patch to the documentation [FAQ](https://github.com/purpleidea/mgmt/blob/master/docs/documentation.md#usage-and-frequently-asked-questions) section. I'll merge your question, and a patch with the answer!
|
||||||
|
|
||||||
## Roadmap:
|
## Roadmap:
|
||||||
Please see: [TODO.md](TODO.md) for a list of upcoming work and TODO items.
|
Please see: [TODO.md](TODO.md) for a list of upcoming work and TODO items.
|
||||||
Please get involved by working on one of these items or by suggesting something else!
|
Please get involved by working on one of these items or by suggesting something else!
|
||||||
|
Feel free to grab one of the straightforward [#mgmtlove](https://github.com/purpleidea/mgmt/labels/mgmtlove) issues if you're a first time contributor to the project or if you're unsure about what to hack on!
|
||||||
|
|
||||||
## Bugs:
|
## Bugs:
|
||||||
Please set the `DEBUG` constant in [main.go](https://github.com/purpleidea/mgmt/blob/master/main.go) to `true`, and post the logs when you report the [issue](https://github.com/purpleidea/mgmt/issues).
|
Please set the `DEBUG` constant in [main.go](https://github.com/purpleidea/mgmt/blob/master/main.go) to `true`, and post the logs when you report the [issue](https://github.com/purpleidea/mgmt/issues).
|
||||||
Bonus points if you provide a [shell](https://github.com/purpleidea/mgmt/tree/master/test/shell) or [OMV](https://github.com/purpleidea/mgmt/tree/master/test/omv) reproducible test case.
|
Bonus points if you provide a [shell](https://github.com/purpleidea/mgmt/tree/master/test/shell) or [OMV](https://github.com/purpleidea/mgmt/tree/master/test/omv) reproducible test case.
|
||||||
|
Feel free to read my article on [debugging golang programs](https://ttboj.wordpress.com/2016/02/15/debugging-golang-programs/).
|
||||||
## Dependencies:
|
|
||||||
* golang 1.4 or higher (required, available in most distros)
|
|
||||||
* golang libraries (required, available with `go get`)
|
|
||||||
|
|
||||||
go get github.com/coreos/etcd/client
|
|
||||||
go get gopkg.in/yaml.v2
|
|
||||||
go get gopkg.in/fsnotify.v1
|
|
||||||
go get github.com/codegangsta/cli
|
|
||||||
go get github.com/coreos/go-systemd/dbus
|
|
||||||
go get github.com/coreos/go-systemd/util
|
|
||||||
|
|
||||||
* stringer (required for building), available as a package on some platforms, otherwise via `go get`
|
|
||||||
|
|
||||||
go get golang.org/x/tools/cmd/stringer
|
|
||||||
|
|
||||||
* pandoc (optional, for building a pdf of the documentation)
|
|
||||||
* graphviz (optional, for building a visual representation of the graph)
|
|
||||||
|
|
||||||
## Patches:
|
## Patches:
|
||||||
We'd love to have your patches! Please send them by email, or as a pull request.
|
We'd love to have your patches! Please send them by email, or as a pull request.
|
||||||
|
|
||||||
## On the web:
|
## On the web:
|
||||||
* Introductory blog post: [https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/)
|
| Author | Format | Subject |
|
||||||
* Julian Dunn at Cfgmgmtcamp 2016 [https://www.youtube.com/watch?v=kfF9IATUask&t=1949&html5=1](https://www.youtube.com/watch?v=kfF9IATUask&t=1949&html5=1)
|
|---|---|---|
|
||||||
|
| James Shubin | blog | [Next generation configuration mgmt](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/) |
|
||||||
|
| James Shubin | video | [Introductory recording from DevConf.cz 2016](https://www.youtube.com/watch?v=GVhpPF0j-iE&html5=1) |
|
||||||
|
| James Shubin | video | [Introductory recording from CfgMgmtCamp.eu 2016](https://www.youtube.com/watch?v=fNeooSiIRnA&html5=1) |
|
||||||
|
| Julian Dunn | video | [On mgmt at CfgMgmtCamp.eu 2016](https://www.youtube.com/watch?v=kfF9IATUask&t=1949&html5=1) |
|
||||||
|
| Walter Heck | slides | [On mgmt at CfgMgmtCamp.eu 2016](http://www.slideshare.net/olindata/configuration-management-time-for-a-4th-generation/3) |
|
||||||
|
| Marco Marongiu | blog | [On mgmt](http://syslog.me/2016/02/15/leap-or-die/) |
|
||||||
|
| Felix Frank | blog | [From Catalog To Mgmt (on puppet to mgmt "transpiling")](https://ffrank.github.io/features/2016/02/18/from-catalog-to-mgmt/) |
|
||||||
|
| James Shubin | blog | [Automatic edges in mgmt (...and the pkg resource)](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/) |
|
||||||
|
| James Shubin | blog | [Automatic grouping in mgmt](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/) |
|
||||||
|
| John Arundel | tweet | [“Puppet’s days are numbered.”](https://twitter.com/bitfield/status/732157519142002688) |
|
||||||
|
| Felix Frank | blog | [Puppet, Meet Mgmt (on puppet to mgmt internals)](https://ffrank.github.io/features/2016/06/12/puppet,-meet-mgmt/) |
|
||||||
|
| Felix Frank | blog | [Puppet Powered Mgmt (puppet to mgmt tl;dr)](https://ffrank.github.io/features/2016/06/19/puppet-powered-mgmt/) |
|
||||||
|
| James Shubin | blog | [Automatic clustering in mgmt](https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/) |
|
||||||
|
| James Shubin | video | [Recording from CoreOSFest 2016](https://www.youtube.com/watch?v=KVmDCUA42wc&html5=1) |
|
||||||
|
| James Shubin | video | [Recording from DebConf16](http://meetings-archive.debian.net/pub/debian-meetings/2016/debconf16/Next_Generation_Config_Mgmt.webm) ([Slides](https://annex.debconf.org//debconf-share/debconf16/slides/15-next-generation-config-mgmt.pdf)) |
|
||||||
|
| Felix Frank | blog | [Edging It All In (puppet and mgmt edges)](https://ffrank.github.io/features/2016/07/12/edging-it-all-in/) |
|
||||||
|
| Felix Frank | blog | [Translating All The Things (puppet to mgmt translation warnings)](https://ffrank.github.io/features/2016/08/19/translating-all-the-things/) |
|
||||||
|
| James Shubin | video | [Recording from systemd.conf 2016](https://www.youtube.com/watch?v=jB992Zb3nH0&html5=1) |
|
||||||
|
| James Shubin | blog | [Remote execution in mgmt](https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/) |
|
||||||
|
| James Shubin | video | [Recording from High Load Strategy 2016](https://vimeo.com/191493409) |
|
||||||
|
| James Shubin | video | [Recording from NLUUG 2016](https://www.youtube.com/watch?v=MmpwOQAb_SE&html5=1) |
|
||||||
|
| James Shubin | blog | [Send/Recv in mgmt](https://ttboj.wordpress.com/2016/12/07/sendrecv-in-mgmt/) |
|
||||||
|
| James Shubin | blog | [Metaparameters in mgmt](https://ttboj.wordpress.com/2017/03/01/metaparameters-in-mgmt/) |
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|||||||
6
THANKS
@@ -9,10 +9,16 @@ Chris Wright - For encouraging me to continue work on my prototype.
|
|||||||
|
|
||||||
Daniel Riek - For supporting and sheltering this project from bureaucracy.
|
Daniel Riek - For supporting and sheltering this project from bureaucracy.
|
||||||
|
|
||||||
|
Diego Ongaro - For good chats, particularly around distributed systems.
|
||||||
|
|
||||||
|
Felix Frank - For taking a difficult problem and building an inspiring solution.
|
||||||
|
|
||||||
Ira Cooper - For having an algorithmic design discussion with me.
|
Ira Cooper - For having an algorithmic design discussion with me.
|
||||||
|
|
||||||
Jeff Darcy - For some algorithm recommendations, and NACKing my TopoSort idea!
|
Jeff Darcy - For some algorithm recommendations, and NACKing my TopoSort idea!
|
||||||
|
|
||||||
Red Hat, inc. - For paying my salary, thus financially supporting my hacking.
|
Red Hat, inc. - For paying my salary, thus financially supporting my hacking.
|
||||||
|
|
||||||
|
Samuel Gélineau - For help with programming language theory and design.
|
||||||
|
|
||||||
And many others...
|
And many others...
|
||||||
|
|||||||
48
TODO.md
@@ -3,27 +3,54 @@ If you're looking for something to do, look here!
|
|||||||
Let us know if you're working on one of the items.
|
Let us know if you're working on one of the items.
|
||||||
|
|
||||||
## Package resource
|
## Package resource
|
||||||
- [ ] base type [bug](https://github.com/purpleidea/mgmt/issues/11)
|
- [ ] getfiles support on debian [bug](https://github.com/hughsie/PackageKit/issues/118)
|
||||||
|
- [ ] directory info on fedora [bug](https://github.com/hughsie/PackageKit/issues/117)
|
||||||
- [ ] dnf blocker [bug](https://github.com/hughsie/PackageKit/issues/110)
|
- [ ] dnf blocker [bug](https://github.com/hughsie/PackageKit/issues/110)
|
||||||
- [ ] install signal blocker [bug](https://github.com/hughsie/PackageKit/issues/109)
|
- [ ] install signal blocker [bug](https://github.com/hughsie/PackageKit/issues/109)
|
||||||
|
|
||||||
## File resource
|
## File resource [bug](https://github.com/purpleidea/mgmt/issues/64) [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
- [ ] ability to make/delete folders
|
- [ ] chown/chmod support [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
- [ ] recursive argument (can recursively watch/modify contents)
|
- [ ] user/group support [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
- [ ] force argument (can cause switch from file <-> folder)
|
- [ ] recurse limit support [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
- [ ] fanotify support [bug](https://github.com/go-fsnotify/fsnotify/issues/114)
|
- [ ] fanotify support [bug](https://github.com/go-fsnotify/fsnotify/issues/114)
|
||||||
|
|
||||||
|
## Svc resource
|
||||||
|
- [ ] base resource improvements
|
||||||
|
|
||||||
## Exec resource
|
## Exec resource
|
||||||
- [ ] base resource improvements
|
- [ ] base resource improvements
|
||||||
|
|
||||||
## Timer resource
|
## Timer resource
|
||||||
|
- [ ] increment algorithm (linear, exponential, etc...) [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
|
## User/Group resource
|
||||||
|
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
- [ ] automatic edges to file resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
|
## Virt (libvirt) resource
|
||||||
|
- [ ] base resource improvements [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
- [ ] port to upstream https://github.com/libvirt/libvirt-go [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
|
## Net (systemd-networkd) resource
|
||||||
- [ ] base resource
|
- [ ] base resource
|
||||||
- [ ] reset on recompile
|
|
||||||
- [ ] increment algorithm (linear, exponential, etc...)
|
## Nspawn (systemd-nspawn) resource
|
||||||
|
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
|
## Mount (systemd-mount) resource
|
||||||
|
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
|
## Cron (systemd-timer) resource
|
||||||
|
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
|
## Http resource
|
||||||
|
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||||
|
|
||||||
## Etcd improvements
|
## Etcd improvements
|
||||||
- [ ] embedded etcd master
|
- [ ] fix embedded etcd master race
|
||||||
- [ ] capnslog fixes [bug](https://github.com/coreos/etcd/issues/4115)
|
|
||||||
|
## Torrent/dht file transfer
|
||||||
|
- [ ] base plumbing
|
||||||
|
|
||||||
## Language improvements
|
## Language improvements
|
||||||
- [ ] language design
|
- [ ] language design
|
||||||
@@ -35,9 +62,6 @@ Let us know if you're working on one of the items.
|
|||||||
|
|
||||||
## Other
|
## Other
|
||||||
- [ ] better error/retry handling
|
- [ ] better error/retry handling
|
||||||
- [ ] resource grouping
|
|
||||||
- [ ] automatic dependency adding (eg: packagekit file dependencies)
|
|
||||||
- [ ] rpm package target in Makefile
|
|
||||||
- [ ] deb package target in Makefile
|
- [ ] deb package target in Makefile
|
||||||
- [ ] reproducible builds
|
- [ ] reproducible builds
|
||||||
- [ ] add your suggestions!
|
- [ ] add your suggestions!
|
||||||
|
|||||||
38
Vagrantfile
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
Vagrant.configure(2) do |config|
|
||||||
|
config.ssh.forward_agent = true
|
||||||
|
config.ssh.username = 'vagrant'
|
||||||
|
config.vm.network "private_network", ip: "192.168.219.2"
|
||||||
|
|
||||||
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
|
|
||||||
|
config.vm.define "mgmt-dev" do |instance|
|
||||||
|
instance.vm.box = "fedora/24-cloud-base"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provider "virtualbox" do |v|
|
||||||
|
v.memory = 1536
|
||||||
|
v.cpus = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provision "file", source: "vagrant/motd", destination: ".motd"
|
||||||
|
config.vm.provision "shell", inline: "cp ~vagrant/.motd /etc/motd"
|
||||||
|
|
||||||
|
config.vm.provision "file", source: "vagrant/mgmt.bashrc", destination: ".mgmt.bashrc"
|
||||||
|
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
|
||||||
|
|
||||||
|
# copied from make-deps.sh (with added git)
|
||||||
|
config.vm.provision "shell", inline: "dnf install -y libvirt-devel golang golang-googlecode-tools-stringer hg git"
|
||||||
|
|
||||||
|
# set up vagrant home
|
||||||
|
script = <<-SCRIPT
|
||||||
|
grep -q 'mgmt\.bashrc' ~/.bashrc || echo '. ~/.mgmt.bashrc' >>~/.bashrc
|
||||||
|
. ~/.mgmt.bashrc
|
||||||
|
go get -u github.com/purpleidea/mgmt
|
||||||
|
cd ~/gopath/src/github.com/purpleidea/mgmt
|
||||||
|
make deps
|
||||||
|
SCRIPT
|
||||||
|
config.vm.provision "shell" do |shell|
|
||||||
|
shell.privileged = false
|
||||||
|
shell.inline = script
|
||||||
|
end
|
||||||
|
end
|
||||||
2
art/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.png
|
||||||
|
misc/
|
||||||
BIN
art/mgmt.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
94
art/mgmt_logo_default_symbol.svg
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 120 107.1" style="enable-background:new 0 0 120 107.1;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="29.2" y1="24.1" x2="52.1" y2="42.8"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="27.7,27.8 24.9,20.5 32.6,21.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st5" cx="16.1" cy="12.2" r="12.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="52.1" y1="42.1" x2="74.1" y2="80"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="70.1,80.9 76.9,84.8 76.8,77.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="69.4" y1="46.7" x2="95.8" y2="52.4"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="69.7,50.7 63.9,45.6 71.3,43.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="52.1" y1="42.8" x2="71.9" y2="30.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="73.1,34 76.6,27.1 68.9,27.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="16.8" y1="49.6" x2="34.8" y2="46.5"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="34.3,50.5 40.3,45.6 33,42.9 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="92.3" y1="79.5" x2="107.8" y2="54.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="96.1,80.6 89.3,84.3 89.5,76.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="97.3" y1="36.5" x2="107.8" y2="54.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="94.6,39.4 94.5,31.7 101.2,35.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st3" cx="52.1" cy="42.8" r="12.1"/>
|
||||||
|
<circle class="st4" cx="12.2" cy="50.8" r="12.1"/>
|
||||||
|
<circle class="st7" cx="87.5" cy="21.7" r="12.1"/>
|
||||||
|
<circle class="st8" cx="83.5" cy="95" r="12.1"/>
|
||||||
|
<circle class="st6" cx="107.8" cy="54.2" r="12.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
132
art/mgmt_logo_default_tall.svg
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 168.3 131.6" style="enable-background:new 0 0 168.3 131.6;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M4.7,105l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9V124h-5v-12.1c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6V124H9v-12.1
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4V124H0v-19H4.7z"/>
|
||||||
|
<path class="st3" d="M26.4,113.9c0-3.1,0.6-5.4,1.7-7s2.7-2.3,4.7-2.3c1.7,0,3.1,0.7,4,2L37,105h4.5v19c0,2.4-0.7,4.3-2,5.6
|
||||||
|
c-1.4,1.3-3.3,1.9-5.9,1.9c-1,0-2.1-0.2-3.3-0.6s-2-0.9-2.6-1.6l1.7-3.4c0.5,0.5,1.1,0.9,1.8,1.2c0.7,0.3,1.5,0.5,2.1,0.5
|
||||||
|
c1.1,0,1.9-0.3,2.4-0.8s0.7-1.4,0.7-2.6v-1.6c-0.9,1.2-2.2,1.9-3.7,1.9c-2,0-3.6-0.8-4.7-2.4s-1.7-3.8-1.7-6.7V113.9z
|
||||||
|
M31.4,115.2c0,1.8,0.2,3,0.7,3.8s1.2,1.2,2.2,1.2c1,0,1.8-0.4,2.3-1.1V110c-0.5-0.8-1.3-1.2-2.2-1.2c-1,0-1.7,0.4-2.2,1.2
|
||||||
|
s-0.7,2.1-0.7,3.9V115.2z"/>
|
||||||
|
<path class="st3" d="M50.1,105l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9V124h-5v-12.1c0-1.1-0.2-1.9-0.5-2.4s-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6V124h-5v-12.1
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4s-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4V124h-5v-19H50.1z"/>
|
||||||
|
<path class="st3" d="M78.2,100.3v4.7h2.5v3.7h-2.5v9.5c0,0.8,0.1,1.3,0.3,1.5c0.2,0.3,0.6,0.4,1.2,0.4c0.5,0,0.9,0,1.2-0.1
|
||||||
|
l0,3.9c-0.8,0.3-1.8,0.5-2.7,0.5c-3.2,0-4.8-1.8-4.9-5.5v-10.1h-2.2V105h2.2v-4.7H78.2z"/>
|
||||||
|
<path class="st4" d="M90.6,122.6c1.4,0,2.4-0.4,3.1-1.1c0.7-0.8,1.1-1.9,1.2-3.3h1.9c-0.1,1.9-0.7,3.5-1.9,4.6
|
||||||
|
c-1.1,1.1-2.6,1.7-4.3,1.7c-2.3,0-4-0.7-5.1-2.2s-1.7-3.6-1.8-6.4v-2.3c0-2.9,0.6-5.1,1.7-6.6s2.9-2.2,5.1-2.2
|
||||||
|
c1.9,0,3.4,0.6,4.5,1.8s1.7,2.9,1.7,5H95c-0.1-1.6-0.5-2.8-1.2-3.6s-1.8-1.3-3.1-1.3c-1.7,0-2.9,0.6-3.7,1.7s-1.2,2.9-1.2,5.2
|
||||||
|
v2.2c0,2.4,0.4,4.2,1.2,5.3S89,122.6,90.6,122.6z"/>
|
||||||
|
<path class="st4" d="M100.5,113.6c0-2.7,0.6-4.9,1.9-6.5s2.9-2.4,5.1-2.4c2.2,0,3.9,0.8,5.1,2.4s1.9,3.7,1.9,6.5v2
|
||||||
|
c0,2.8-0.6,5-1.9,6.5s-2.9,2.3-5.1,2.3c-2.1,0-3.8-0.8-5-2.3s-1.9-3.6-1.9-6.3V113.6z M102.5,115.5c0,2.2,0.4,3.9,1.3,5.2
|
||||||
|
c0.9,1.3,2.1,1.9,3.7,1.9c1.6,0,2.8-0.6,3.7-1.8s1.3-2.9,1.3-5.2v-2c0-2.2-0.4-3.9-1.3-5.2c-0.9-1.3-2.1-1.9-3.7-1.9
|
||||||
|
c-1.5,0-2.7,0.6-3.6,1.8s-1.3,2.9-1.4,5.1V115.5z"/>
|
||||||
|
<path class="st4" d="M121.1,105l0.1,3c0.6-1,1.3-1.9,2.2-2.5s1.9-0.9,3-0.9c3.3,0,5,2.2,5.1,6.6V124h-1.9v-12.5
|
||||||
|
c0-1.7-0.3-3-0.9-3.8c-0.6-0.8-1.5-1.2-2.8-1.2c-1,0-1.9,0.4-2.8,1.2s-1.4,1.8-1.9,3.2V124h-2v-19H121.1z"/>
|
||||||
|
<path class="st4" d="M138.2,124v-17.3h-2.6V105h2.6v-2.5c0-1.9,0.5-3.3,1.3-4.3s2-1.5,3.5-1.5c0.7,0,1.3,0.1,1.9,0.3l-0.1,1.8
|
||||||
|
c-0.5-0.1-1-0.2-1.6-0.2c-0.9,0-1.7,0.4-2.2,1.1s-0.8,1.7-0.8,3v2.3h3.7v1.8h-3.7V124H138.2z"/>
|
||||||
|
<path class="st4" d="M148,99.5c0-0.4,0.1-0.7,0.3-1s0.5-0.4,0.9-0.4s0.7,0.1,1,0.4s0.3,0.6,0.3,1s-0.1,0.7-0.3,1s-0.5,0.4-1,0.4
|
||||||
|
s-0.7-0.1-0.9-0.4S148,99.9,148,99.5z M150.2,124h-2v-19h2V124z"/>
|
||||||
|
<path class="st4" d="M155.3,113.6c0-3,0.5-5.2,1.5-6.7s2.6-2.2,4.6-2.2c2.2,0,3.8,1,4.9,3l0.1-2.7h1.8v19.6c0,2.3-0.6,4-1.6,5.2
|
||||||
|
c-1.1,1.2-2.7,1.8-4.7,1.8c-1,0-2.1-0.3-3.2-0.8s-1.9-1.1-2.4-1.9l0.9-1.4c1.3,1.5,2.8,2.2,4.5,2.2c1.6,0,2.7-0.4,3.5-1.3
|
||||||
|
c0.7-0.8,1.1-2.1,1.1-3.8v-3c-1.1,1.8-2.7,2.7-4.9,2.7c-2,0-3.5-0.7-4.5-2.2s-1.6-3.6-1.6-6.5V113.6z M157.2,115.4
|
||||||
|
c0,2.4,0.4,4.2,1.1,5.4s1.9,1.7,3.5,1.7c2.1,0,3.6-1,4.5-3v-9.7c-0.9-2.2-2.4-3.3-4.4-3.3c-1.6,0-2.8,0.6-3.5,1.7
|
||||||
|
s-1.1,2.9-1.1,5.3V115.4z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="58.9" y1="20.1" x2="77.9" y2="35.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="57.6,23.2 55.3,17.1 61.7,18.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st5" cx="48" cy="10.1" r="10.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="77.9" y1="35.1" x2="96.2" y2="66.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="93,67.5 98.6,70.7 98.6,64.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="92.3" y1="38.9" x2="114.4" y2="43.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="92.6,42.3 87.8,38 93.9,36 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="77.9" y1="35.6" x2="94.5" y2="25.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="95.4,28.3 98.4,22.6 91.9,22.9 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="48.5" y1="41.3" x2="63.5" y2="38.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="63.1,42.1 68.1,38 62,35.7 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="111.4" y1="66.3" x2="124.4" y2="45.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="114.6,67.2 109,70.2 109.1,63.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="115.6" y1="30.4" x2="124.4" y2="45.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="113.3,32.8 113.3,26.4 118.9,29.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st3" cx="77.9" cy="35.6" r="10.1"/>
|
||||||
|
<circle class="st4" cx="44.6" cy="42.4" r="10.1"/>
|
||||||
|
<circle class="st7" cx="107.4" cy="18.1" r="10.1"/>
|
||||||
|
<circle class="st8" cx="104.1" cy="79.1" r="10.1"/>
|
||||||
|
<circle class="st6" cx="124.4" cy="45.2" r="10.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
132
art/mgmt_logo_default_wide.svg
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 260.4 71.4" style="enable-background:new 0 0 260.4 71.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M96.7,25.7l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5V32.6c0-1.1-0.2-1.9-0.5-2.4s-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9h-5V32.6
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4s-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8h-5v-19H96.7z"/>
|
||||||
|
<path class="st3" d="M118.5,34.6c0-3.1,0.6-5.4,1.7-7s2.7-2.3,4.7-2.3c1.7,0,3.1,0.7,4,2l0.2-1.7h4.5v19c0,2.4-0.7,4.3-2,5.6
|
||||||
|
c-1.4,1.3-3.3,1.9-5.9,1.9c-1,0-2.1-0.2-3.3-0.6s-2-0.9-2.6-1.6l1.7-3.4c0.5,0.5,1.1,0.9,1.8,1.2c0.7,0.3,1.5,0.5,2.1,0.5
|
||||||
|
c1.1,0,1.9-0.3,2.4-0.8s0.7-1.4,0.7-2.6v-1.6c-0.9,1.2-2.2,1.9-3.7,1.9c-2,0-3.6-0.8-4.7-2.4s-1.7-3.8-1.7-6.7V34.6z
|
||||||
|
M123.5,35.9c0,1.8,0.2,3,0.7,3.8s1.2,1.2,2.2,1.2c1,0,1.8-0.4,2.3-1.1v-9.1c-0.5-0.8-1.3-1.2-2.2-1.2c-1,0-1.7,0.4-2.2,1.2
|
||||||
|
s-0.7,2.1-0.7,3.9V35.9z"/>
|
||||||
|
<path class="st3" d="M142.2,25.7l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5V32.6c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9h-5V32.6
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8h-5v-19H142.2z"/>
|
||||||
|
<path class="st3" d="M170.3,21v4.7h2.5v3.7h-2.5v9.5c0,0.8,0.1,1.3,0.3,1.5c0.2,0.3,0.6,0.4,1.2,0.4c0.5,0,0.9,0,1.2-0.1l0,3.9
|
||||||
|
c-0.8,0.3-1.8,0.5-2.7,0.5c-3.2,0-4.8-1.8-4.9-5.5V29.4h-2.2v-3.7h2.2V21H170.3z"/>
|
||||||
|
<path class="st4" d="M182.7,43.2c1.4,0,2.4-0.4,3.1-1.1s1.1-1.8,1.2-3.3h1.9c-0.1,1.9-0.7,3.5-1.9,4.6s-2.6,1.7-4.3,1.7
|
||||||
|
c-2.3,0-4-0.7-5.1-2.2s-1.7-3.6-1.8-6.4v-2.3c0-2.9,0.6-5.1,1.7-6.6s2.9-2.2,5.1-2.2c1.9,0,3.4,0.6,4.5,1.8s1.7,2.9,1.7,5H187
|
||||||
|
c-0.1-1.6-0.5-2.8-1.2-3.6s-1.8-1.3-3.1-1.3c-1.7,0-2.9,0.6-3.7,1.7c-0.8,1.1-1.2,2.9-1.2,5.2v2.2c0,2.4,0.4,4.2,1.2,5.3
|
||||||
|
S181,43.2,182.7,43.2z"/>
|
||||||
|
<path class="st4" d="M192.6,34.2c0-2.7,0.6-4.9,1.9-6.5s2.9-2.4,5.1-2.4c2.2,0,3.9,0.8,5.1,2.4c1.2,1.6,1.9,3.7,1.9,6.5v2
|
||||||
|
c0,2.8-0.6,5-1.9,6.5s-2.9,2.3-5.1,2.3s-3.8-0.8-5-2.3s-1.9-3.6-1.9-6.3V34.2z M194.6,36.2c0,2.2,0.4,3.9,1.3,5.2
|
||||||
|
c0.9,1.3,2.1,1.9,3.7,1.9c1.6,0,2.8-0.6,3.7-1.8c0.8-1.2,1.3-2.9,1.3-5.2v-2c0-2.2-0.4-3.9-1.3-5.2c-0.9-1.3-2.1-1.9-3.7-1.9
|
||||||
|
c-1.5,0-2.7,0.6-3.6,1.8c-0.9,1.2-1.3,2.9-1.4,5.1V36.2z"/>
|
||||||
|
<path class="st4" d="M213.2,25.7l0.1,3c0.6-1,1.3-1.9,2.2-2.5s1.9-0.9,3-0.9c3.3,0,5,2.2,5.1,6.6v12.7h-1.9V32.2
|
||||||
|
c0-1.7-0.3-3-0.9-3.8c-0.6-0.8-1.5-1.2-2.8-1.2c-1,0-1.9,0.4-2.8,1.2s-1.4,1.8-1.9,3.2v13.2h-2v-19H213.2z"/>
|
||||||
|
<path class="st4" d="M230.3,44.7V27.4h-2.6v-1.8h2.6v-2.5c0-1.9,0.5-3.3,1.3-4.3s2-1.5,3.5-1.5c0.7,0,1.3,0.1,1.9,0.3l-0.1,1.8
|
||||||
|
c-0.5-0.1-1-0.2-1.6-0.2c-0.9,0-1.7,0.4-2.2,1.1s-0.8,1.7-0.8,3v2.3h3.7v1.8h-3.7v17.3H230.3z"/>
|
||||||
|
<path class="st4" d="M240.1,20.2c0-0.4,0.1-0.7,0.3-1s0.5-0.4,0.9-0.4s0.7,0.1,1,0.4s0.3,0.6,0.3,1s-0.1,0.7-0.3,1
|
||||||
|
s-0.5,0.4-1,0.4s-0.7-0.1-0.9-0.4S240.1,20.6,240.1,20.2z M242.3,44.7h-2v-19h2V44.7z"/>
|
||||||
|
<path class="st4" d="M247.4,34.3c0-3,0.5-5.2,1.5-6.7s2.6-2.2,4.6-2.2c2.2,0,3.8,1,4.9,3l0.1-2.7h1.8v19.6c0,2.3-0.6,4-1.6,5.2
|
||||||
|
s-2.7,1.8-4.7,1.8c-1,0-2.1-0.3-3.2-0.8s-1.9-1.1-2.4-1.9l0.9-1.4c1.3,1.5,2.8,2.2,4.5,2.2c1.6,0,2.7-0.4,3.5-1.3
|
||||||
|
c0.7-0.8,1.1-2.1,1.1-3.8v-3c-1.1,1.8-2.7,2.7-4.9,2.7c-2,0-3.5-0.7-4.5-2.2s-1.6-3.6-1.6-6.5V34.3z M249.3,36.1
|
||||||
|
c0,2.4,0.4,4.2,1.1,5.4s1.9,1.7,3.5,1.7c2.1,0,3.6-1,4.5-3v-9.7c-0.9-2.2-2.4-3.3-4.4-3.3c-1.6,0-2.8,0.6-3.5,1.7
|
||||||
|
s-1.1,2.9-1.1,5.3V36.1z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="19.5" y1="16" x2="34.7" y2="28.5"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="18.4,18.5 16.6,13.7 21.7,14.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st5" cx="10.8" cy="8.1" r="8.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="34.7" y1="28" x2="49.4" y2="53.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="46.8,54 51.2,56.5 51.2,51.4 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="46.2" y1="31.1" x2="63.9" y2="34.9"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="46.4,33.8 42.6,30.4 47.5,28.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="34.7" y1="28.5" x2="47.9" y2="20.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="48.7,22.7 51.1,18.1 45.9,18.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="11.2" y1="33.1" x2="23.2" y2="31"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="22.9,33.7 26.8,30.4 22,28.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="61.5" y1="53" x2="71.9" y2="36.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="64.1,53.7 59.5,56.2 59.7,51 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="64.2" y1="24.2" x2="70.7" y2="33.9"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st9" points="62.5,26.3 62.2,21.1 66.8,23.4 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st3" cx="34.7" cy="28.5" r="8.1"/>
|
||||||
|
<circle class="st4" cx="8.1" cy="33.9" r="8.1"/>
|
||||||
|
<circle class="st7" cx="58.3" cy="14.5" r="8.1"/>
|
||||||
|
<circle class="st8" cx="55.7" cy="63.3" r="8.1"/>
|
||||||
|
<circle class="st6" cx="71.9" cy="36.1" r="8.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
94
art/mgmt_logo_reversed_symbol.svg
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 120 107.1" style="enable-background:new 0 0 120 107.1;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="29.2" y1="24.1" x2="52.1" y2="42.8"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="27.7,27.8 24.9,20.5 32.6,21.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st3" cx="16.1" cy="12.2" r="12.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="52.1" y1="42.1" x2="74.1" y2="80"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="70.1,80.9 76.9,84.8 76.8,77.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="69.4" y1="46.7" x2="95.8" y2="52.4"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="69.7,50.7 63.9,45.6 71.3,43.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="52.1" y1="42.8" x2="71.9" y2="30.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="73.1,34 76.6,27.1 68.9,27.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="16.8" y1="49.6" x2="34.8" y2="46.5"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="34.3,50.5 40.3,45.6 33,42.9 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="92.3" y1="79.5" x2="107.8" y2="54.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="96.1,80.6 89.3,84.3 89.5,76.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="97.2" y1="36.3" x2="107.8" y2="54.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="94.4,39.3 94.3,31.5 101.1,35.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st5" cx="52.1" cy="42.8" r="12.1"/>
|
||||||
|
<circle class="st4" cx="12.2" cy="50.8" r="12.1"/>
|
||||||
|
<circle class="st7" cx="87.5" cy="21.7" r="12.1"/>
|
||||||
|
<circle class="st8" cx="83.5" cy="95" r="12.1"/>
|
||||||
|
<circle class="st6" cx="107.8" cy="54.2" r="12.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
132
art/mgmt_logo_reversed_tall.svg
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 168.3 133" style="enable-background:new 0 0 168.3 133;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M4.7,106.4l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5v-12.1c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9H9v-12.1
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8H0v-19H4.7z"/>
|
||||||
|
<path class="st0" d="M26.4,115.3c0-3.1,0.6-5.4,1.7-7s2.7-2.3,4.7-2.3c1.7,0,3.1,0.7,4,2l0.2-1.7h4.5v19c0,2.4-0.7,4.3-2,5.6
|
||||||
|
c-1.4,1.3-3.3,1.9-5.9,1.9c-1,0-2.1-0.2-3.3-0.6s-2-0.9-2.6-1.6l1.7-3.4c0.5,0.5,1.1,0.9,1.8,1.2c0.7,0.3,1.5,0.5,2.1,0.5
|
||||||
|
c1.1,0,1.9-0.3,2.4-0.8c0.5-0.5,0.7-1.4,0.7-2.6v-1.6c-0.9,1.2-2.2,1.9-3.7,1.9c-2,0-3.6-0.8-4.7-2.4s-1.7-3.8-1.7-6.7V115.3z
|
||||||
|
M31.4,116.6c0,1.8,0.2,3,0.7,3.8s1.2,1.2,2.2,1.2c1,0,1.8-0.4,2.3-1.1v-9.1c-0.5-0.8-1.3-1.2-2.2-1.2c-1,0-1.7,0.4-2.2,1.2
|
||||||
|
s-0.7,2.1-0.7,3.9V116.6z"/>
|
||||||
|
<path class="st0" d="M50.1,106.4l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5v-12.1c0-1.1-0.2-1.9-0.5-2.4s-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9h-5v-12.1
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4s-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8h-5v-19H50.1z"/>
|
||||||
|
<path class="st0" d="M78.2,101.7v4.7h2.5v3.7h-2.5v9.5c0,0.8,0.1,1.3,0.3,1.5c0.2,0.3,0.6,0.4,1.2,0.4c0.5,0,0.9,0,1.2-0.1
|
||||||
|
l0,3.9c-0.8,0.3-1.8,0.5-2.7,0.5c-3.2,0-4.8-1.8-4.9-5.5v-10.1h-2.2v-3.7h2.2v-4.7H78.2z"/>
|
||||||
|
<path class="st4" d="M90.6,124c1.4,0,2.4-0.4,3.1-1.1c0.7-0.8,1.1-1.9,1.2-3.3h1.9c-0.1,1.9-0.7,3.5-1.9,4.6
|
||||||
|
c-1.1,1.1-2.6,1.7-4.3,1.7c-2.3,0-4-0.7-5.1-2.2s-1.7-3.6-1.8-6.4v-2.3c0-2.9,0.6-5.1,1.7-6.6s2.9-2.2,5.1-2.2
|
||||||
|
c1.9,0,3.4,0.6,4.5,1.8s1.7,2.9,1.7,5H95c-0.1-1.6-0.5-2.8-1.2-3.6s-1.8-1.3-3.1-1.3c-1.7,0-2.9,0.6-3.7,1.7s-1.2,2.9-1.2,5.2
|
||||||
|
v2.2c0,2.4,0.4,4.2,1.2,5.3S89,124,90.6,124z"/>
|
||||||
|
<path class="st4" d="M100.5,115c0-2.7,0.6-4.9,1.9-6.5s2.9-2.4,5.1-2.4c2.2,0,3.9,0.8,5.1,2.4s1.9,3.7,1.9,6.5v2
|
||||||
|
c0,2.8-0.6,5-1.9,6.5s-2.9,2.3-5.1,2.3c-2.1,0-3.8-0.8-5-2.3s-1.9-3.6-1.9-6.3V115z M102.5,116.9c0,2.2,0.4,3.9,1.3,5.2
|
||||||
|
c0.9,1.3,2.1,1.9,3.7,1.9c1.6,0,2.8-0.6,3.7-1.8s1.3-2.9,1.3-5.2v-2c0-2.2-0.4-3.9-1.3-5.2c-0.9-1.3-2.1-1.9-3.7-1.9
|
||||||
|
c-1.5,0-2.7,0.6-3.6,1.8s-1.3,2.9-1.4,5.1V116.9z"/>
|
||||||
|
<path class="st4" d="M121.1,106.4l0.1,3c0.6-1,1.3-1.9,2.2-2.5s1.9-0.9,3-0.9c3.3,0,5,2.2,5.1,6.6v12.7h-1.9v-12.5
|
||||||
|
c0-1.7-0.3-3-0.9-3.8c-0.6-0.8-1.5-1.2-2.8-1.2c-1,0-1.9,0.4-2.8,1.2s-1.4,1.8-1.9,3.2v13.2h-2v-19H121.1z"/>
|
||||||
|
<path class="st4" d="M138.2,125.4v-17.3h-2.6v-1.8h2.6v-2.5c0-1.9,0.5-3.3,1.3-4.3s2-1.5,3.5-1.5c0.7,0,1.3,0.1,1.9,0.3
|
||||||
|
l-0.1,1.8c-0.5-0.1-1-0.2-1.6-0.2c-0.9,0-1.7,0.4-2.2,1.1s-0.8,1.7-0.8,3v2.3h3.7v1.8h-3.7v17.3H138.2z"/>
|
||||||
|
<path class="st4" d="M148,100.9c0-0.4,0.1-0.7,0.3-1s0.5-0.4,0.9-0.4s0.7,0.1,1,0.4s0.3,0.6,0.3,1s-0.1,0.7-0.3,1
|
||||||
|
s-0.5,0.4-1,0.4s-0.7-0.1-0.9-0.4S148,101.3,148,100.9z M150.2,125.4h-2v-19h2V125.4z"/>
|
||||||
|
<path class="st4" d="M155.3,115c0-3,0.5-5.2,1.5-6.7s2.6-2.2,4.6-2.2c2.2,0,3.8,1,4.9,3l0.1-2.7h1.8V126c0,2.3-0.6,4-1.6,5.2
|
||||||
|
c-1.1,1.2-2.7,1.8-4.7,1.8c-1,0-2.1-0.3-3.2-0.8s-1.9-1.1-2.4-1.9l0.9-1.4c1.3,1.5,2.8,2.2,4.5,2.2c1.6,0,2.7-0.4,3.5-1.3
|
||||||
|
c0.7-0.8,1.1-2.1,1.1-3.8v-3c-1.1,1.8-2.7,2.7-4.9,2.7c-2,0-3.5-0.7-4.5-2.2s-1.6-3.6-1.6-6.5V115z M157.2,116.8
|
||||||
|
c0,2.4,0.4,4.2,1.1,5.4s1.9,1.7,3.5,1.7c2.1,0,3.6-1,4.5-3v-9.7c-0.9-2.2-2.4-3.3-4.4-3.3c-1.6,0-2.8,0.6-3.5,1.7
|
||||||
|
s-1.1,2.9-1.1,5.3V116.8z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="58.9" y1="20.1" x2="77.9" y2="35.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="57.6,23.2 55.3,17.1 61.7,18.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st3" cx="48" cy="10.1" r="10.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="77.9" y1="35.1" x2="96.2" y2="66.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="93,67.5 98.6,70.7 98.6,64.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="92.3" y1="38.9" x2="114.4" y2="43.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="92.6,42.3 87.8,38 93.9,36 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="77.9" y1="35.6" x2="94.5" y2="25.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="95.4,28.3 98.4,22.6 91.9,22.9 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="48.5" y1="41.3" x2="63.5" y2="38.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="63.1,42.1 68.1,38 62,35.7 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="111.4" y1="66.3" x2="124.4" y2="45.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="114.6,67.2 109,70.2 109.1,63.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st16" x1="115.5" y1="30.5" x2="124.4" y2="45.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="113.2,32.9 113.1,26.5 118.8,29.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st5" cx="77.9" cy="35.6" r="10.1"/>
|
||||||
|
<circle class="st4" cx="44.6" cy="42.4" r="10.1"/>
|
||||||
|
<circle class="st7" cx="107.4" cy="18.1" r="10.1"/>
|
||||||
|
<circle class="st8" cx="104.1" cy="79.1" r="10.1"/>
|
||||||
|
<circle class="st6" cx="124.4" cy="45.2" r="10.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
132
art/mgmt_logo_reversed_wide.svg
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 260.4 71.4" style="enable-background:new 0 0 260.4 71.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M96.7,27.6l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5V34.4c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9h-5V34.5
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8h-5v-19H96.7z"/>
|
||||||
|
<path class="st0" d="M118.5,36.5c0-3.1,0.6-5.4,1.7-7s2.7-2.3,4.7-2.3c1.7,0,3.1,0.7,4,2l0.2-1.7h4.5v19c0,2.4-0.7,4.3-2,5.6
|
||||||
|
c-1.4,1.3-3.3,1.9-5.9,1.9c-1,0-2.1-0.2-3.3-0.6s-2-0.9-2.6-1.6l1.7-3.4c0.5,0.5,1.1,0.9,1.8,1.2c0.7,0.3,1.5,0.5,2.1,0.5
|
||||||
|
c1.1,0,1.9-0.3,2.4-0.8s0.7-1.4,0.7-2.6v-1.6c-0.9,1.2-2.2,1.9-3.7,1.9c-2,0-3.6-0.8-4.7-2.4s-1.7-3.8-1.7-6.7V36.5z
|
||||||
|
M123.5,37.7c0,1.8,0.2,3,0.7,3.8s1.2,1.2,2.2,1.2c1,0,1.8-0.4,2.3-1.1v-9.1c-0.5-0.8-1.3-1.2-2.2-1.2c-1,0-1.7,0.4-2.2,1.2
|
||||||
|
c-0.5,0.8-0.7,2.1-0.7,3.9V37.7z"/>
|
||||||
|
<path class="st0" d="M142.2,27.6l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5V34.4c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9h-5V34.5
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8h-5v-19H142.2z"/>
|
||||||
|
<path class="st0" d="M170.3,22.9v4.7h2.5v3.7h-2.5v9.5c0,0.8,0.1,1.3,0.3,1.5c0.2,0.3,0.6,0.4,1.2,0.4c0.5,0,0.9,0,1.2-0.1
|
||||||
|
l0,3.9c-0.8,0.3-1.8,0.5-2.7,0.5c-3.2,0-4.8-1.8-4.9-5.5V31.3h-2.2v-3.7h2.2v-4.7H170.3z"/>
|
||||||
|
<path class="st4" d="M182.7,45.1c1.4,0,2.4-0.4,3.1-1.1c0.7-0.8,1.1-1.9,1.2-3.3h1.9c-0.1,1.9-0.7,3.5-1.9,4.6s-2.6,1.7-4.3,1.7
|
||||||
|
c-2.3,0-4-0.7-5.1-2.2s-1.7-3.6-1.8-6.4V36c0-2.9,0.6-5.1,1.7-6.6s2.9-2.2,5.1-2.2c1.9,0,3.4,0.6,4.5,1.8s1.7,2.9,1.7,5H187
|
||||||
|
c-0.1-1.6-0.5-2.8-1.2-3.6s-1.8-1.3-3.1-1.3c-1.7,0-2.9,0.6-3.7,1.7s-1.2,2.9-1.2,5.2v2.2c0,2.4,0.4,4.2,1.2,5.3
|
||||||
|
S181,45.1,182.7,45.1z"/>
|
||||||
|
<path class="st4" d="M192.6,36.1c0-2.7,0.6-4.9,1.9-6.5s2.9-2.4,5.1-2.4c2.2,0,3.9,0.8,5.1,2.4c1.2,1.6,1.9,3.7,1.9,6.5v2
|
||||||
|
c0,2.8-0.6,5-1.9,6.5s-2.9,2.3-5.1,2.3s-3.8-0.8-5-2.3s-1.9-3.6-1.9-6.3V36.1z M194.6,38.1c0,2.2,0.4,3.9,1.3,5.2
|
||||||
|
c0.9,1.3,2.1,1.9,3.7,1.9c1.6,0,2.8-0.6,3.7-1.8c0.8-1.2,1.3-2.9,1.3-5.2v-2c0-2.2-0.4-3.9-1.3-5.2c-0.9-1.3-2.1-1.9-3.7-1.9
|
||||||
|
c-1.5,0-2.7,0.6-3.6,1.8c-0.9,1.2-1.3,2.9-1.4,5.1V38.1z"/>
|
||||||
|
<path class="st4" d="M213.2,27.6l0.1,3c0.6-1,1.3-1.9,2.2-2.5s1.9-0.9,3-0.9c3.3,0,5,2.2,5.1,6.6v12.7h-1.9V34.1
|
||||||
|
c0-1.7-0.3-3-0.9-3.8c-0.6-0.8-1.5-1.2-2.8-1.2c-1,0-1.9,0.4-2.8,1.2s-1.4,1.8-1.9,3.2v13.2h-2v-19H213.2z"/>
|
||||||
|
<path class="st4" d="M230.3,46.6V29.3h-2.6v-1.8h2.6v-2.5c0-1.9,0.5-3.3,1.3-4.3s2-1.5,3.5-1.5c0.7,0,1.3,0.1,1.9,0.3l-0.1,1.8
|
||||||
|
c-0.5-0.1-1-0.2-1.6-0.2c-0.9,0-1.7,0.4-2.2,1.1s-0.8,1.7-0.8,3v2.3h3.7v1.8h-3.7v17.3H230.3z"/>
|
||||||
|
<path class="st4" d="M240.1,22.1c0-0.4,0.1-0.7,0.3-1s0.5-0.4,0.9-0.4s0.7,0.1,1,0.4s0.3,0.6,0.3,1s-0.1,0.7-0.3,1
|
||||||
|
s-0.5,0.4-1,0.4s-0.7-0.1-0.9-0.4S240.1,22.5,240.1,22.1z M242.3,46.6h-2v-19h2V46.6z"/>
|
||||||
|
<path class="st4" d="M247.4,36.2c0-3,0.5-5.2,1.5-6.7s2.6-2.2,4.6-2.2c2.2,0,3.8,1,4.9,3l0.1-2.7h1.8v19.6c0,2.3-0.6,4-1.6,5.2
|
||||||
|
s-2.7,1.8-4.7,1.8c-1,0-2.1-0.3-3.2-0.8s-1.9-1.1-2.4-1.9l0.9-1.4c1.3,1.5,2.8,2.2,4.5,2.2c1.6,0,2.7-0.4,3.5-1.3
|
||||||
|
c0.7-0.8,1.1-2.1,1.1-3.8v-3c-1.1,1.8-2.7,2.7-4.9,2.7c-2,0-3.5-0.7-4.5-2.2s-1.6-3.6-1.6-6.5V36.2z M249.3,38
|
||||||
|
c0,2.4,0.4,4.2,1.1,5.4s1.9,1.7,3.5,1.7c2.1,0,3.6-1,4.5-3v-9.7c-0.9-2.2-2.4-3.3-4.4-3.3c-1.6,0-2.8,0.6-3.5,1.7
|
||||||
|
s-1.1,2.9-1.1,5.3V38z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="19.5" y1="16" x2="34.7" y2="28.5"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="18.4,18.5 16.6,13.7 21.7,14.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st3" cx="10.8" cy="8.1" r="8.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="34.7" y1="28" x2="49.4" y2="53.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="46.8,54 51.2,56.5 51.2,51.4 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="46.2" y1="31.1" x2="63.9" y2="34.9"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="46.4,33.8 42.6,30.4 47.5,28.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="34.7" y1="28.5" x2="47.9" y2="20.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="48.7,22.7 51.1,18.1 45.9,18.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="11.2" y1="33.1" x2="23.2" y2="31"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="22.9,33.7 26.8,30.4 22,28.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="61.5" y1="53" x2="71.9" y2="36.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="64.1,53.7 59.5,56.2 59.7,51 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="64.8" y1="24.4" x2="71.9" y2="36.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="63,26.4 62.9,21.2 67.4,23.7 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st5" cx="34.7" cy="28.5" r="8.1"/>
|
||||||
|
<circle class="st4" cx="8.1" cy="33.9" r="8.1"/>
|
||||||
|
<circle class="st7" cx="58.3" cy="14.5" r="8.1"/>
|
||||||
|
<circle class="st8" cx="55.7" cy="63.3" r="8.1"/>
|
||||||
|
<circle class="st6" cx="71.9" cy="36.1" r="8.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
94
art/mgmt_logo_white_symbol.svg
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 120 107.1" style="enable-background:new 0 0 120 107.1;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="29.2" y1="24.1" x2="52.1" y2="42.8"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="27.7,27.8 24.9,20.5 32.6,21.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st0" cx="16.1" cy="12.2" r="12.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="52.1" y1="42.1" x2="74.1" y2="80"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="70.1,80.9 76.9,84.8 76.8,77.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="69.4" y1="46.7" x2="95.8" y2="52.4"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="69.7,50.7 63.9,45.6 71.3,43.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="52.1" y1="42.8" x2="71.9" y2="30.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="73.1,34 76.6,27.1 68.9,27.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="16.8" y1="49.6" x2="34.8" y2="46.5"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="34.3,50.5 40.3,45.6 33,42.9 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="92.3" y1="79.5" x2="107.8" y2="54.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="96.1,80.6 89.3,84.3 89.5,76.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st17" x1="97.2" y1="36.6" x2="107.8" y2="54.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="94.4,39.5 94.3,31.8 101.1,35.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st0" cx="52.1" cy="42.8" r="12.1"/>
|
||||||
|
<circle class="st0" cx="12.2" cy="50.8" r="12.1"/>
|
||||||
|
<circle class="st0" cx="87.5" cy="21.7" r="12.1"/>
|
||||||
|
<circle class="st0" cx="83.5" cy="95" r="12.1"/>
|
||||||
|
<circle class="st0" cx="107.8" cy="54.2" r="12.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
132
art/mgmt_logo_white_tall.svg
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 168.3 131.4" style="enable-background:new 0 0 168.3 131.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M4.7,104.8l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5v-12.1c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9H9v-12.1
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8H0v-19H4.7z"/>
|
||||||
|
<path class="st0" d="M26.4,113.8c0-3.1,0.6-5.4,1.7-7s2.7-2.3,4.7-2.3c1.7,0,3.1,0.7,4,2l0.2-1.7h4.5v19c0,2.4-0.7,4.3-2,5.6
|
||||||
|
c-1.4,1.3-3.3,1.9-5.9,1.9c-1,0-2.1-0.2-3.3-0.6s-2-0.9-2.6-1.6l1.7-3.4c0.5,0.5,1.1,0.9,1.8,1.2c0.7,0.3,1.5,0.5,2.1,0.5
|
||||||
|
c1.1,0,1.9-0.3,2.4-0.8s0.7-1.4,0.7-2.6v-1.6c-0.9,1.2-2.2,1.9-3.7,1.9c-2,0-3.6-0.8-4.7-2.4s-1.7-3.8-1.7-6.7V113.8z M31.4,115
|
||||||
|
c0,1.8,0.2,3,0.7,3.8s1.2,1.2,2.2,1.2c1,0,1.8-0.4,2.3-1.1v-9.1c-0.5-0.8-1.3-1.2-2.2-1.2c-1,0-1.7,0.4-2.2,1.2
|
||||||
|
s-0.7,2.1-0.7,3.9V115z"/>
|
||||||
|
<path class="st0" d="M50.1,104.8l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9v12.5h-5v-12.1c0-1.1-0.2-1.9-0.5-2.4s-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6v12.9h-5v-12.1
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4s-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4v13.8h-5v-19H50.1z"/>
|
||||||
|
<path class="st0" d="M78.2,100.2v4.7h2.5v3.7h-2.5v9.5c0,0.8,0.1,1.3,0.3,1.5c0.2,0.3,0.6,0.4,1.2,0.4c0.5,0,0.9,0,1.2-0.1
|
||||||
|
l0,3.9c-0.8,0.3-1.8,0.5-2.7,0.5c-3.2,0-4.8-1.8-4.9-5.5v-10.1h-2.2v-3.7h2.2v-4.7H78.2z"/>
|
||||||
|
<path class="st0" d="M90.6,122.4c1.4,0,2.4-0.4,3.1-1.1c0.7-0.8,1.1-1.8,1.2-3.3h1.9c-0.1,1.9-0.7,3.5-1.9,4.6
|
||||||
|
c-1.1,1.1-2.6,1.7-4.3,1.7c-2.3,0-4-0.7-5.1-2.2s-1.7-3.6-1.8-6.4v-2.3c0-2.9,0.6-5.1,1.7-6.6s2.9-2.2,5.1-2.2
|
||||||
|
c1.9,0,3.4,0.6,4.5,1.8s1.7,2.9,1.7,5H95c-0.1-1.6-0.5-2.8-1.2-3.6s-1.8-1.3-3.1-1.3c-1.7,0-2.9,0.6-3.7,1.7
|
||||||
|
c-0.8,1.1-1.2,2.9-1.2,5.2v2.2c0,2.4,0.4,4.2,1.2,5.3S89,122.4,90.6,122.4z"/>
|
||||||
|
<path class="st0" d="M100.5,113.4c0-2.7,0.6-4.9,1.9-6.5s2.9-2.4,5.1-2.4c2.2,0,3.9,0.8,5.1,2.4s1.9,3.7,1.9,6.5v2
|
||||||
|
c0,2.8-0.6,5-1.9,6.5s-2.9,2.3-5.1,2.3c-2.1,0-3.8-0.8-5-2.3s-1.9-3.6-1.9-6.3V113.4z M102.5,115.3c0,2.2,0.4,3.9,1.3,5.2
|
||||||
|
c0.9,1.3,2.1,1.9,3.7,1.9c1.6,0,2.8-0.6,3.7-1.8c0.8-1.2,1.3-2.9,1.3-5.2v-2c0-2.2-0.4-3.9-1.3-5.2c-0.9-1.3-2.1-1.9-3.7-1.9
|
||||||
|
c-1.5,0-2.7,0.6-3.6,1.8c-0.9,1.2-1.3,2.9-1.4,5.1V115.3z"/>
|
||||||
|
<path class="st0" d="M121.1,104.8l0.1,3c0.6-1,1.3-1.9,2.2-2.5s1.9-0.9,3-0.9c3.3,0,5,2.2,5.1,6.6v12.7h-1.9v-12.5
|
||||||
|
c0-1.7-0.3-3-0.9-3.8c-0.6-0.8-1.5-1.2-2.8-1.2c-1,0-1.9,0.4-2.8,1.2s-1.4,1.8-1.9,3.2v13.2h-2v-19H121.1z"/>
|
||||||
|
<path class="st0" d="M138.2,123.8v-17.3h-2.6v-1.8h2.6v-2.5c0-1.9,0.5-3.3,1.3-4.3s2-1.5,3.5-1.5c0.7,0,1.3,0.1,1.9,0.3
|
||||||
|
l-0.1,1.8c-0.5-0.1-1-0.2-1.6-0.2c-0.9,0-1.7,0.4-2.2,1.1s-0.8,1.7-0.8,3v2.3h3.7v1.8h-3.7v17.3H138.2z"/>
|
||||||
|
<path class="st0" d="M148,99.3c0-0.4,0.1-0.7,0.3-1s0.5-0.4,0.9-0.4s0.7,0.1,1,0.4s0.3,0.6,0.3,1s-0.1,0.7-0.3,1s-0.5,0.4-1,0.4
|
||||||
|
s-0.7-0.1-0.9-0.4S148,99.7,148,99.3z M150.2,123.8h-2v-19h2V123.8z"/>
|
||||||
|
<path class="st0" d="M155.3,113.5c0-3,0.5-5.2,1.5-6.7s2.6-2.2,4.6-2.2c2.2,0,3.8,1,4.9,3l0.1-2.7h1.8v19.6c0,2.3-0.6,4-1.6,5.2
|
||||||
|
c-1.1,1.2-2.7,1.8-4.7,1.8c-1,0-2.1-0.3-3.2-0.8s-1.9-1.1-2.4-1.9l0.9-1.4c1.3,1.5,2.8,2.2,4.5,2.2c1.6,0,2.7-0.4,3.5-1.3
|
||||||
|
c0.7-0.8,1.1-2.1,1.1-3.8v-3c-1.1,1.8-2.7,2.7-4.9,2.7c-2,0-3.5-0.7-4.5-2.2s-1.6-3.6-1.6-6.5V113.5z M157.2,115.2
|
||||||
|
c0,2.4,0.4,4.2,1.1,5.4s1.9,1.7,3.5,1.7c2.1,0,3.6-1,4.5-3v-9.7c-0.9-2.2-2.4-3.3-4.4-3.3c-1.6,0-2.8,0.6-3.5,1.7
|
||||||
|
s-1.1,2.9-1.1,5.3V115.2z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="58.9" y1="20.1" x2="77.9" y2="35.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="57.6,23.2 55.3,17.1 61.7,18.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st0" cx="48" cy="10.1" r="10.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="77.9" y1="35.1" x2="96.2" y2="66.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="93,67.5 98.6,70.7 98.6,64.2 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="92.3" y1="38.9" x2="114.4" y2="43.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="92.6,42.3 87.8,38 93.9,36 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="77.9" y1="35.6" x2="94.5" y2="25.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="95.4,28.3 98.4,22.6 91.9,22.9 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="48.5" y1="41.3" x2="63.5" y2="38.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="63.1,42.1 68.1,38 62,35.7 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="111.4" y1="66.3" x2="124.4" y2="45.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="114.6,67.2 109,70.2 109.1,63.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st19" x1="115.4" y1="30.4" x2="124.4" y2="45.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="113.2,32.8 113,26.4 118.7,29.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st0" cx="77.9" cy="35.6" r="10.1"/>
|
||||||
|
<circle class="st0" cx="44.6" cy="42.4" r="10.1"/>
|
||||||
|
<circle class="st0" cx="107.4" cy="18.1" r="10.1"/>
|
||||||
|
<circle class="st0" cx="104.1" cy="79.1" r="10.1"/>
|
||||||
|
<circle class="st0" cx="124.4" cy="45.2" r="10.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
132
art/mgmt_logo_white_wide.svg
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 260.4 71.4" style="enable-background:new 0 0 260.4 71.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#E22434;}
|
||||||
|
.st2{display:none;}
|
||||||
|
.st3{fill:#1B3663;}
|
||||||
|
.st4{fill:#00B1D1;}
|
||||||
|
.st5{fill:#BFE6EF;}
|
||||||
|
.st6{fill:#69CBE0;}
|
||||||
|
.st7{fill:#0080BD;}
|
||||||
|
.st8{fill:#005DAB;}
|
||||||
|
.st9{fill:#183660;}
|
||||||
|
.st10{fill:none;stroke:#183660;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st11{fill:none;stroke:#183660;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st12{fill:none;stroke:#183660;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st13{fill:none;stroke:#C0E6EF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st14{fill:#C0E6EF;}
|
||||||
|
.st15{fill:none;stroke:#C0E6EF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st16{fill:none;stroke:#C0E6EF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
.st17{fill:none;stroke:#FFFFFF;stroke-width:1.9441;stroke-miterlimit:10;}
|
||||||
|
.st18{fill:none;stroke:#FFFFFF;stroke-width:1.2961;stroke-miterlimit:10;}
|
||||||
|
.st19{fill:none;stroke:#FFFFFF;stroke-width:1.6201;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g id="Layer_1">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M96.7,26l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9V45h-5V32.9c0-1.1-0.2-1.9-0.5-2.4s-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6V45h-5V32.9
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4s-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4V45h-5V26H96.7z"/>
|
||||||
|
<path class="st0" d="M118.5,34.9c0-3.1,0.6-5.4,1.7-7s2.7-2.3,4.7-2.3c1.7,0,3.1,0.7,4,2l0.2-1.7h4.5v19c0,2.4-0.7,4.3-2,5.6
|
||||||
|
c-1.4,1.3-3.3,1.9-5.9,1.9c-1,0-2.1-0.2-3.3-0.6s-2-0.9-2.6-1.6l1.7-3.4c0.5,0.5,1.1,0.9,1.8,1.2c0.7,0.3,1.5,0.5,2.1,0.5
|
||||||
|
c1.1,0,1.9-0.3,2.4-0.8s0.7-1.4,0.7-2.6v-1.6c-0.9,1.2-2.2,1.9-3.7,1.9c-2,0-3.6-0.8-4.7-2.4s-1.7-3.8-1.7-6.7V34.9z
|
||||||
|
M123.5,36.2c0,1.8,0.2,3,0.7,3.8s1.2,1.2,2.2,1.2c1,0,1.8-0.4,2.3-1.1V31c-0.5-0.8-1.3-1.2-2.2-1.2c-1,0-1.7,0.4-2.2,1.2
|
||||||
|
s-0.7,2.1-0.7,3.9V36.2z"/>
|
||||||
|
<path class="st0" d="M142.2,26l0.1,1.8c1.1-1.4,2.6-2.1,4.4-2.1c1.9,0,3.2,0.9,4,2.6c1.1-1.7,2.6-2.6,4.7-2.6
|
||||||
|
c3.3,0,5,2.3,5.1,6.9V45h-5V32.9c0-1.1-0.2-1.9-0.5-2.4c-0.3-0.5-0.8-0.7-1.5-0.7c-0.9,0-1.6,0.6-2.1,1.7l0,0.6V45h-5V32.9
|
||||||
|
c0-1.1-0.1-1.9-0.4-2.4c-0.3-0.5-0.8-0.7-1.6-0.7c-0.9,0-1.5,0.5-2,1.4V45h-5V26H142.2z"/>
|
||||||
|
<path class="st0" d="M170.3,21.3V26h2.5v3.7h-2.5v9.5c0,0.8,0.1,1.3,0.3,1.5c0.2,0.3,0.6,0.4,1.2,0.4c0.5,0,0.9,0,1.2-0.1l0,3.9
|
||||||
|
c-0.8,0.3-1.8,0.5-2.7,0.5c-3.2,0-4.8-1.8-4.9-5.5V29.7h-2.2V26h2.2v-4.7H170.3z"/>
|
||||||
|
<path class="st0" d="M182.7,43.5c1.4,0,2.4-0.4,3.1-1.1c0.7-0.8,1.1-1.9,1.2-3.3h1.9c-0.1,1.9-0.7,3.5-1.9,4.6s-2.6,1.7-4.3,1.7
|
||||||
|
c-2.3,0-4-0.7-5.1-2.2s-1.7-3.6-1.8-6.4v-2.3c0-2.9,0.6-5.1,1.7-6.6s2.9-2.2,5.1-2.2c1.9,0,3.4,0.6,4.5,1.8s1.7,2.8,1.7,5H187
|
||||||
|
c-0.1-1.6-0.5-2.8-1.2-3.6s-1.8-1.3-3.1-1.3c-1.7,0-2.9,0.6-3.7,1.7c-0.8,1.1-1.2,2.9-1.2,5.2v2.2c0,2.4,0.4,4.2,1.2,5.3
|
||||||
|
C179.8,43,181,43.5,182.7,43.5z"/>
|
||||||
|
<path class="st0" d="M192.6,34.5c0-2.7,0.6-4.9,1.9-6.5s2.9-2.4,5.1-2.4c2.2,0,3.9,0.8,5.1,2.4c1.2,1.6,1.9,3.7,1.9,6.5v2
|
||||||
|
c0,2.8-0.6,5-1.9,6.5s-2.9,2.3-5.1,2.3s-3.8-0.8-5-2.3s-1.9-3.6-1.9-6.3V34.5z M194.6,36.5c0,2.2,0.4,3.9,1.3,5.2
|
||||||
|
c0.9,1.3,2.1,1.9,3.7,1.9c1.6,0,2.8-0.6,3.7-1.8c0.8-1.2,1.3-2.9,1.3-5.2v-2c0-2.2-0.4-3.9-1.3-5.2c-0.9-1.3-2.1-1.9-3.7-1.9
|
||||||
|
c-1.5,0-2.7,0.6-3.6,1.8c-0.9,1.2-1.3,2.9-1.4,5.1V36.5z"/>
|
||||||
|
<path class="st0" d="M213.2,26l0.1,3c0.6-1,1.3-1.9,2.2-2.5s1.9-0.9,3-0.9c3.3,0,5,2.2,5.1,6.6V45h-1.9V32.5
|
||||||
|
c0-1.7-0.3-3-0.9-3.8c-0.6-0.8-1.5-1.2-2.8-1.2c-1,0-1.9,0.4-2.8,1.2s-1.4,1.8-1.9,3.2V45h-2V26H213.2z"/>
|
||||||
|
<path class="st0" d="M230.3,45V27.7h-2.6V26h2.6v-2.5c0-1.9,0.5-3.3,1.3-4.3s2-1.5,3.5-1.5c0.7,0,1.3,0.1,1.9,0.3l-0.1,1.8
|
||||||
|
c-0.5-0.1-1-0.2-1.6-0.2c-0.9,0-1.7,0.4-2.2,1.1s-0.8,1.7-0.8,3V26h3.7v1.8h-3.7V45H230.3z"/>
|
||||||
|
<path class="st0" d="M240.1,20.5c0-0.4,0.1-0.7,0.3-1s0.5-0.4,0.9-0.4s0.7,0.1,1,0.4s0.3,0.6,0.3,1s-0.1,0.7-0.3,1
|
||||||
|
s-0.5,0.4-1,0.4s-0.7-0.1-0.9-0.4S240.1,20.9,240.1,20.5z M242.3,45h-2V26h2V45z"/>
|
||||||
|
<path class="st0" d="M247.4,34.6c0-3,0.5-5.2,1.5-6.7s2.6-2.2,4.6-2.2c2.2,0,3.8,1,4.9,3l0.1-2.7h1.8v19.6c0,2.3-0.6,4-1.6,5.2
|
||||||
|
s-2.7,1.8-4.7,1.8c-1,0-2.1-0.3-3.2-0.8s-1.9-1.1-2.4-1.9l0.9-1.4c1.3,1.5,2.8,2.2,4.5,2.2c1.6,0,2.7-0.4,3.5-1.3
|
||||||
|
c0.7-0.8,1.1-2.1,1.1-3.8v-3c-1.1,1.8-2.7,2.7-4.9,2.7c-2,0-3.5-0.7-4.5-2.2s-1.6-3.6-1.6-6.5V34.6z M249.3,36.4
|
||||||
|
c0,2.4,0.4,4.2,1.1,5.4s1.9,1.7,3.5,1.7c2.1,0,3.6-1,4.5-3v-9.7c-0.9-2.2-2.4-3.3-4.4-3.3c-1.6,0-2.8,0.6-3.5,1.7
|
||||||
|
s-1.1,2.9-1.1,5.3V36.4z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="19.5" y1="16" x2="34.7" y2="28.5"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="18.4,18.5 16.6,13.7 21.7,14.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st0" cx="10.8" cy="8.1" r="8.1"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="34.7" y1="28" x2="49.4" y2="53.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="46.8,54 51.2,56.5 51.2,51.4 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="46.2" y1="31.1" x2="63.9" y2="34.9"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="46.4,33.8 42.6,30.4 47.5,28.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="34.7" y1="28.5" x2="47.9" y2="20.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="48.7,22.7 51.1,18.1 45.9,18.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="11.2" y1="33.1" x2="23.2" y2="31"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="22.9,33.7 26.8,30.4 22,28.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="61.5" y1="53" x2="71.9" y2="36.1"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="64.1,53.7 59.5,56.2 59.7,51 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st18" x1="64.7" y1="24.4" x2="71.9" y2="36.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="62.9,26.4 62.9,21.2 67.3,23.7 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle class="st0" cx="34.7" cy="28.5" r="8.1"/>
|
||||||
|
<circle class="st0" cx="8.1" cy="33.9" r="8.1"/>
|
||||||
|
<circle class="st0" cx="58.3" cy="14.5" r="8.1"/>
|
||||||
|
<circle class="st0" cx="55.7" cy="63.3" r="8.1"/>
|
||||||
|
<circle class="st0" cx="71.9" cy="36.1" r="8.1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
229
config.go
@@ -1,229 +0,0 @@
|
|||||||
// Mgmt
|
|
||||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
|
||||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type collectorTypeConfig struct {
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
Pattern string `yaml:"pattern"` // XXX: Not Implemented
|
|
||||||
}
|
|
||||||
|
|
||||||
type vertexConfig struct {
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type edgeConfig struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
From vertexConfig `yaml:"from"`
|
|
||||||
To vertexConfig `yaml:"to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GraphConfig struct {
|
|
||||||
Graph string `yaml:"graph"`
|
|
||||||
Types struct {
|
|
||||||
Noop []NoopType `yaml:"noop"`
|
|
||||||
File []FileType `yaml:"file"`
|
|
||||||
Service []ServiceType `yaml:"service"`
|
|
||||||
Exec []ExecType `yaml:"exec"`
|
|
||||||
} `yaml:"types"`
|
|
||||||
Collector []collectorTypeConfig `yaml:"collect"`
|
|
||||||
Edges []edgeConfig `yaml:"edges"`
|
|
||||||
Comment string `yaml:"comment"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *GraphConfig) Parse(data []byte) error {
|
|
||||||
if err := yaml.Unmarshal(data, c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c.Graph == "" {
|
|
||||||
return errors.New("Graph config: invalid `graph`")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseConfigFromFile(filename string) *GraphConfig {
|
|
||||||
data, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error: Config: ParseConfigFromFile: File: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var config GraphConfig
|
|
||||||
if err := config.Parse(data); err != nil {
|
|
||||||
log.Printf("Error: Config: ParseConfigFromFile: Parse: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &config
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: we need to fix this function so that it either fails without modifying
|
|
||||||
// the graph, passes successfully and modifies it, or basically panics i guess
|
|
||||||
// this way an invalid compilation can leave the old graph running, and we we
|
|
||||||
// don't modify a partial graph. so we really need to validate, and then perform
|
|
||||||
// whatever actions are necessary
|
|
||||||
// finding some way to do this on a copy of the graph, and then do a graph diff
|
|
||||||
// and merge the new data into the old graph would be more appropriate, in
|
|
||||||
// particular if we can ensure the graph merge can't fail. As for the putting
|
|
||||||
// of stuff into etcd, we should probably store the operations to complete in
|
|
||||||
// the new graph, and keep retrying until it succeeds, thus blocking any new
|
|
||||||
// etcd operations until that time.
|
|
||||||
func UpdateGraphFromConfig(config *GraphConfig, hostname string, g *Graph, etcdO *EtcdWObject) bool {
|
|
||||||
|
|
||||||
var NoopMap = make(map[string]*Vertex)
|
|
||||||
var FileMap = make(map[string]*Vertex)
|
|
||||||
var ServiceMap = make(map[string]*Vertex)
|
|
||||||
var ExecMap = make(map[string]*Vertex)
|
|
||||||
|
|
||||||
var lookup = make(map[string]map[string]*Vertex)
|
|
||||||
lookup["noop"] = NoopMap
|
|
||||||
lookup["file"] = FileMap
|
|
||||||
lookup["service"] = ServiceMap
|
|
||||||
lookup["exec"] = ExecMap
|
|
||||||
|
|
||||||
//log.Printf("%+v", config) // debug
|
|
||||||
|
|
||||||
g.SetName(config.Graph) // set graph name
|
|
||||||
|
|
||||||
var keep []*Vertex // list of vertex which are the same in new graph
|
|
||||||
|
|
||||||
for _, t := range config.Types.Noop {
|
|
||||||
obj := NewNoopType(t.Name)
|
|
||||||
v := g.GetVertexMatch(obj)
|
|
||||||
if v == nil { // no match found
|
|
||||||
v = NewVertex(obj)
|
|
||||||
g.AddVertex(v) // call standalone in case not part of an edge
|
|
||||||
}
|
|
||||||
NoopMap[obj.Name] = v // used for constructing edges
|
|
||||||
keep = append(keep, v) // append
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range config.Types.File {
|
|
||||||
// XXX: should we export based on a @@ prefix, or a metaparam
|
|
||||||
// like exported => true || exported => (host pattern)||(other pattern?)
|
|
||||||
if strings.HasPrefix(t.Name, "@@") { // exported resource
|
|
||||||
// add to etcd storage...
|
|
||||||
t.Name = t.Name[2:] //slice off @@
|
|
||||||
if !etcdO.EtcdPut(hostname, t.Name, "file", t) {
|
|
||||||
log.Printf("Problem exporting file resource %v.", t.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj := NewFileType(t.Name, t.Path, t.Dirname, t.Basename, t.Content, t.State)
|
|
||||||
v := g.GetVertexMatch(obj)
|
|
||||||
if v == nil { // no match found
|
|
||||||
v = NewVertex(obj)
|
|
||||||
g.AddVertex(v) // call standalone in case not part of an edge
|
|
||||||
}
|
|
||||||
FileMap[obj.Name] = v // used for constructing edges
|
|
||||||
keep = append(keep, v) // append
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range config.Types.Service {
|
|
||||||
obj := NewServiceType(t.Name, t.State, t.Startup)
|
|
||||||
v := g.GetVertexMatch(obj)
|
|
||||||
if v == nil { // no match found
|
|
||||||
v = NewVertex(obj)
|
|
||||||
g.AddVertex(v) // call standalone in case not part of an edge
|
|
||||||
}
|
|
||||||
ServiceMap[obj.Name] = v // used for constructing edges
|
|
||||||
keep = append(keep, v) // append
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range config.Types.Exec {
|
|
||||||
obj := NewExecType(t.Name, t.Cmd, t.Shell, t.Timeout, t.WatchCmd, t.WatchShell, t.IfCmd, t.IfShell, t.PollInt, t.State)
|
|
||||||
v := g.GetVertexMatch(obj)
|
|
||||||
if v == nil { // no match found
|
|
||||||
v = NewVertex(obj)
|
|
||||||
g.AddVertex(v) // call standalone in case not part of an edge
|
|
||||||
}
|
|
||||||
ExecMap[obj.Name] = v // used for constructing edges
|
|
||||||
keep = append(keep, v) // append
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup from etcd graph
|
|
||||||
// do all the graph look ups in one single step, so that if the etcd
|
|
||||||
// database changes, we don't have a partial state of affairs...
|
|
||||||
nodes, ok := etcdO.EtcdGet()
|
|
||||||
if ok {
|
|
||||||
for _, t := range config.Collector {
|
|
||||||
// XXX: use t.Type and optionally t.Pattern to collect from etcd storage
|
|
||||||
log.Printf("Collect: %v; Pattern: %v", t.Type, t.Pattern)
|
|
||||||
|
|
||||||
for _, x := range etcdO.EtcdGetProcess(nodes, "file") {
|
|
||||||
var obj *FileType
|
|
||||||
if B64ToObj(x, &obj) != true {
|
|
||||||
log.Printf("Collect: File: %v not collected!", x)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if t.Pattern != "" { // XXX: currently the pattern for files can only override the Dirname variable :P
|
|
||||||
obj.Dirname = t.Pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Collect: File: %v collected!", obj.GetName())
|
|
||||||
|
|
||||||
// XXX: similar to file add code:
|
|
||||||
v := g.GetVertexMatch(obj)
|
|
||||||
if v == nil { // no match found
|
|
||||||
obj.Init() // initialize go channels or things won't work!!!
|
|
||||||
v = NewVertex(obj)
|
|
||||||
g.AddVertex(v) // call standalone in case not part of an edge
|
|
||||||
}
|
|
||||||
FileMap[obj.GetName()] = v // used for constructing edges
|
|
||||||
keep = append(keep, v) // append
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get rid of any vertices we shouldn't "keep" (that aren't in new graph)
|
|
||||||
for _, v := range g.GetVertices() {
|
|
||||||
if !HasVertex(v, keep) {
|
|
||||||
// wait for exit before starting new graph!
|
|
||||||
v.Type.SendEvent(eventExit, true, false)
|
|
||||||
g.DeleteVertex(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range config.Edges {
|
|
||||||
if _, ok := lookup[e.From.Type]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if _, ok := lookup[e.To.Type]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if _, ok := lookup[e.From.Type][e.From.Name]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if _, ok := lookup[e.To.Type][e.To.Name]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
g.AddEdge(lookup[e.From.Type][e.From.Name], lookup[e.To.Type][e.To.Name], NewEdge(e.Name))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
155
configwatch.go
@@ -1,155 +0,0 @@
|
|||||||
// Mgmt
|
|
||||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
|
||||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gopkg.in/fsnotify.v1"
|
|
||||||
//"github.com/go-fsnotify/fsnotify" // git master of "gopkg.in/fsnotify.v1"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// XXX: it would be great if we could reuse code between this and the file type
|
|
||||||
// XXX: patch this to submit it as part of go-fsnotify if they're interested...
|
|
||||||
func ConfigWatch(file string) chan bool {
|
|
||||||
ch := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
var safename = path.Clean(file) // no trailing slash
|
|
||||||
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer watcher.Close()
|
|
||||||
|
|
||||||
patharray := PathSplit(safename) // tokenize the path
|
|
||||||
var index = len(patharray) // starting index
|
|
||||||
var current string // current "watcher" location
|
|
||||||
var deltaDepth int // depth delta between watcher and event
|
|
||||||
var send = false // send event?
|
|
||||||
|
|
||||||
for {
|
|
||||||
current = strings.Join(patharray[0:index], "/")
|
|
||||||
if current == "" { // the empty string top is the root dir ("/")
|
|
||||||
current = "/"
|
|
||||||
}
|
|
||||||
log.Printf("Watching: %v", current) // attempting to watch...
|
|
||||||
|
|
||||||
// initialize in the loop so that we can reset on rm-ed handles
|
|
||||||
err = watcher.Add(current)
|
|
||||||
if err != nil {
|
|
||||||
if err == syscall.ENOENT {
|
|
||||||
index-- // usually not found, move up one dir
|
|
||||||
} else if err == syscall.ENOSPC {
|
|
||||||
// XXX: occasionally: no space left on device,
|
|
||||||
// XXX: probably due to lack of inotify watches
|
|
||||||
log.Printf("Lack of watches for config(%v) error: %+v", file, err.Error) // 0x408da0
|
|
||||||
log.Fatal(err)
|
|
||||||
} else {
|
|
||||||
log.Printf("Unknown config(%v) error:", file)
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
index = int(math.Max(1, float64(index)))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case event := <-watcher.Events:
|
|
||||||
// the deeper you go, the bigger the deltaDepth is...
|
|
||||||
// this is the difference between what we're watching,
|
|
||||||
// and the event... doesn't mean we can't watch deeper
|
|
||||||
if current == event.Name {
|
|
||||||
deltaDepth = 0 // i was watching what i was looking for
|
|
||||||
|
|
||||||
} else if HasPathPrefix(event.Name, current) {
|
|
||||||
deltaDepth = len(PathSplit(current)) - len(PathSplit(event.Name)) // -1 or less
|
|
||||||
|
|
||||||
} else if HasPathPrefix(current, event.Name) {
|
|
||||||
deltaDepth = len(PathSplit(event.Name)) - len(PathSplit(current)) // +1 or more
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// TODO different watchers get each others events!
|
|
||||||
// https://github.com/go-fsnotify/fsnotify/issues/95
|
|
||||||
// this happened with two values such as:
|
|
||||||
// event.Name: /tmp/mgmt/f3 and current: /tmp/mgmt/f2
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//log.Printf("The delta depth is: %v", deltaDepth)
|
|
||||||
|
|
||||||
// if we have what we wanted, awesome, send an event...
|
|
||||||
if event.Name == safename {
|
|
||||||
//log.Println("Event!")
|
|
||||||
send = true
|
|
||||||
|
|
||||||
// file removed, move the watch upwards
|
|
||||||
if deltaDepth >= 0 && (event.Op&fsnotify.Remove == fsnotify.Remove) {
|
|
||||||
//log.Println("Removal!")
|
|
||||||
watcher.Remove(current)
|
|
||||||
index--
|
|
||||||
}
|
|
||||||
|
|
||||||
// we must be a parent watcher, so descend in
|
|
||||||
if deltaDepth < 0 {
|
|
||||||
watcher.Remove(current)
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
|
|
||||||
// if safename starts with event.Name, we're above, and no event should be sent
|
|
||||||
} else if HasPathPrefix(safename, event.Name) {
|
|
||||||
//log.Println("Above!")
|
|
||||||
|
|
||||||
if deltaDepth >= 0 && (event.Op&fsnotify.Remove == fsnotify.Remove) {
|
|
||||||
log.Println("Removal!")
|
|
||||||
watcher.Remove(current)
|
|
||||||
index--
|
|
||||||
}
|
|
||||||
|
|
||||||
if deltaDepth < 0 {
|
|
||||||
log.Println("Parent!")
|
|
||||||
if PathPrefixDelta(safename, event.Name) == 1 { // we're the parent dir
|
|
||||||
//send = true
|
|
||||||
}
|
|
||||||
watcher.Remove(current)
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
|
|
||||||
// if event.Name startswith safename, send event, we're already deeper
|
|
||||||
} else if HasPathPrefix(event.Name, safename) {
|
|
||||||
//log.Println("Event2!")
|
|
||||||
//send = true
|
|
||||||
}
|
|
||||||
|
|
||||||
case err := <-watcher.Errors:
|
|
||||||
log.Println("error:", err)
|
|
||||||
log.Fatal(err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// do our event sending all together to avoid duplicate msgs
|
|
||||||
if send {
|
|
||||||
send = false
|
|
||||||
ch <- true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//close(ch)
|
|
||||||
}()
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
386
converger/converger.go
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
// Mgmt
|
||||||
|
// Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package converger is a facility for reporting the converged state.
|
||||||
|
package converger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: we could make a new function that masks out the state of certain
|
||||||
|
// UID's, but at the moment the new Timer code has obsoleted the need...
|
||||||
|
|
||||||
|
// Converger is the general interface for implementing a convergence watcher.
|
||||||
|
type Converger interface { // TODO: need a better name
|
||||||
|
Register() UID
|
||||||
|
IsConverged(UID) bool // is the UID converged ?
|
||||||
|
SetConverged(UID, bool) error // set the converged state of the UID
|
||||||
|
Unregister(UID)
|
||||||
|
Start()
|
||||||
|
Pause()
|
||||||
|
Loop(bool)
|
||||||
|
ConvergedTimer(UID) <-chan time.Time
|
||||||
|
Status() map[uint64]bool
|
||||||
|
Timeout() int // returns the timeout that this was created with
|
||||||
|
SetStateFn(func(bool) error) // sets the stateFn
|
||||||
|
}
|
||||||
|
|
||||||
|
// UID is the interface resources can use to notify with if converged. You'll
|
||||||
|
// need to use part of the Converger interface to Register initially too.
|
||||||
|
type UID interface {
|
||||||
|
ID() uint64 // get Id
|
||||||
|
Name() string // get a friendly name
|
||||||
|
SetName(string)
|
||||||
|
IsValid() bool // has Id been initialized ?
|
||||||
|
InvalidateID() // set Id to nil
|
||||||
|
IsConverged() bool
|
||||||
|
SetConverged(bool) error
|
||||||
|
Unregister()
|
||||||
|
ConvergedTimer() <-chan time.Time
|
||||||
|
StartTimer() (func() error, error) // cancellable is the same as StopTimer()
|
||||||
|
ResetTimer() error // resets counter to zero
|
||||||
|
StopTimer() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// converger is an implementation of the Converger interface.
|
||||||
|
type converger struct {
|
||||||
|
timeout int // must be zero (instant) or greater seconds to run
|
||||||
|
stateFn func(bool) error // run on converged state changes with state bool
|
||||||
|
converged bool // did we converge (state changes of this run Fn)
|
||||||
|
channel chan struct{} // signal here to run an isConverged check
|
||||||
|
control chan bool // control channel for start/pause
|
||||||
|
mutex sync.RWMutex // used for controlling access to status and lastid
|
||||||
|
lastid uint64
|
||||||
|
status map[uint64]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// cuid is an implementation of the UID interface.
|
||||||
|
type cuid struct {
|
||||||
|
converger Converger
|
||||||
|
id uint64
|
||||||
|
name string // user defined, friendly name
|
||||||
|
mutex sync.Mutex
|
||||||
|
timer chan struct{}
|
||||||
|
running bool // is the above timer running?
|
||||||
|
wg sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConverger builds a new converger struct.
|
||||||
|
func NewConverger(timeout int, stateFn func(bool) error) *converger {
|
||||||
|
return &converger{
|
||||||
|
timeout: timeout,
|
||||||
|
stateFn: stateFn,
|
||||||
|
channel: make(chan struct{}),
|
||||||
|
control: make(chan bool),
|
||||||
|
lastid: 0,
|
||||||
|
status: make(map[uint64]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register assigns a UID to the caller.
|
||||||
|
func (obj *converger) Register() UID {
|
||||||
|
obj.mutex.Lock()
|
||||||
|
defer obj.mutex.Unlock()
|
||||||
|
obj.lastid++
|
||||||
|
obj.status[obj.lastid] = false // initialize as not converged
|
||||||
|
return &cuid{
|
||||||
|
converger: obj,
|
||||||
|
id: obj.lastid,
|
||||||
|
name: fmt.Sprintf("%d", obj.lastid), // some default
|
||||||
|
timer: nil,
|
||||||
|
running: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsConverged gets the converged status of a uid.
|
||||||
|
func (obj *converger) IsConverged(uid UID) bool {
|
||||||
|
if !uid.IsValid() {
|
||||||
|
panic(fmt.Sprintf("the ID of UID(%s) is nil", uid.Name()))
|
||||||
|
}
|
||||||
|
obj.mutex.RLock()
|
||||||
|
isConverged, found := obj.status[uid.ID()] // lookup
|
||||||
|
obj.mutex.RUnlock()
|
||||||
|
if !found {
|
||||||
|
panic("the ID of UID is unregistered")
|
||||||
|
}
|
||||||
|
return isConverged
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConverged updates the converger with the converged state of the UID.
|
||||||
|
func (obj *converger) SetConverged(uid UID, isConverged bool) error {
|
||||||
|
if !uid.IsValid() {
|
||||||
|
return fmt.Errorf("the ID of UID(%s) is nil", uid.Name())
|
||||||
|
}
|
||||||
|
obj.mutex.Lock()
|
||||||
|
if _, found := obj.status[uid.ID()]; !found {
|
||||||
|
panic("the ID of UID is unregistered")
|
||||||
|
}
|
||||||
|
obj.status[uid.ID()] = isConverged // set
|
||||||
|
obj.mutex.Unlock() // unlock *before* poke or deadlock!
|
||||||
|
if isConverged != obj.converged { // only poke if it would be helpful
|
||||||
|
// run in a go routine so that we never block... just queue up!
|
||||||
|
// this allows us to send events, even if we haven't started...
|
||||||
|
go func() { obj.channel <- struct{}{} }()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isConverged returns true if *every* registered uid has converged.
|
||||||
|
func (obj *converger) isConverged() bool {
|
||||||
|
obj.mutex.RLock() // take a read lock
|
||||||
|
defer obj.mutex.RUnlock()
|
||||||
|
for _, v := range obj.status {
|
||||||
|
if !v { // everyone must be converged for this to be true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister dissociates the ConvergedUID from the converged checking.
|
||||||
|
func (obj *converger) Unregister(uid UID) {
|
||||||
|
if !uid.IsValid() {
|
||||||
|
panic(fmt.Sprintf("the ID of UID(%s) is nil", uid.Name()))
|
||||||
|
}
|
||||||
|
obj.mutex.Lock()
|
||||||
|
uid.StopTimer() // ignore any errors
|
||||||
|
delete(obj.status, uid.ID())
|
||||||
|
obj.mutex.Unlock()
|
||||||
|
uid.InvalidateID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start causes a Converger object to start or resume running.
|
||||||
|
func (obj *converger) Start() {
|
||||||
|
obj.control <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause causes a Converger object to stop running temporarily.
|
||||||
|
func (obj *converger) Pause() { // FIXME: add a sync ACK on pause before return
|
||||||
|
obj.control <- false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop is the main loop for a Converger object. It usually runs in a goroutine.
|
||||||
|
// TODO: we could eventually have each resource tell us as soon as it converges,
|
||||||
|
// and then keep track of the time delays here, to avoid callers needing select.
|
||||||
|
// NOTE: when we have very short timeouts, if we start before all the resources
|
||||||
|
// have joined the map, then it might appear as if we converged before we did!
|
||||||
|
func (obj *converger) Loop(startPaused bool) {
|
||||||
|
if obj.control == nil {
|
||||||
|
panic("converger not initialized correctly")
|
||||||
|
}
|
||||||
|
if startPaused { // start paused without racing
|
||||||
|
select {
|
||||||
|
case e := <-obj.control:
|
||||||
|
if !e {
|
||||||
|
panic("converger expected true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-obj.control: // expecting "false" which means pause!
|
||||||
|
if e {
|
||||||
|
panic("converger expected false")
|
||||||
|
}
|
||||||
|
// now i'm paused...
|
||||||
|
select {
|
||||||
|
case e := <-obj.control:
|
||||||
|
if !e {
|
||||||
|
panic("converger expected true")
|
||||||
|
}
|
||||||
|
// restart
|
||||||
|
// kick once to refresh the check...
|
||||||
|
go func() { obj.channel <- struct{}{} }()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-obj.channel:
|
||||||
|
if !obj.isConverged() {
|
||||||
|
if obj.converged { // we're doing a state change
|
||||||
|
if obj.stateFn != nil {
|
||||||
|
// call an arbitrary function
|
||||||
|
if err := obj.stateFn(false); err != nil {
|
||||||
|
// FIXME: what to do on error ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.converged = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have converged!
|
||||||
|
if obj.timeout >= 0 { // only run if timeout is valid
|
||||||
|
if !obj.converged { // we're doing a state change
|
||||||
|
if obj.stateFn != nil {
|
||||||
|
// call an arbitrary function
|
||||||
|
if err := obj.stateFn(true); err != nil {
|
||||||
|
// FIXME: what to do on error ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.converged = true
|
||||||
|
// loop and wait again...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvergedTimer adds a timeout to a select call and blocks until then.
|
||||||
|
// TODO: this means we could eventually have per resource converged timeouts
|
||||||
|
func (obj *converger) ConvergedTimer(uid UID) <-chan time.Time {
|
||||||
|
// be clever: if i'm already converged, this timeout should block which
|
||||||
|
// avoids unnecessary new signals being sent! this avoids fast loops if
|
||||||
|
// we have a low timeout, or in particular a timeout == 0
|
||||||
|
if uid.IsConverged() {
|
||||||
|
// blocks the case statement in select forever!
|
||||||
|
return util.TimeAfterOrBlock(-1)
|
||||||
|
}
|
||||||
|
return util.TimeAfterOrBlock(obj.timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns a map of the converged status of each UID.
|
||||||
|
func (obj *converger) Status() map[uint64]bool {
|
||||||
|
status := make(map[uint64]bool)
|
||||||
|
obj.mutex.RLock() // take a read lock
|
||||||
|
defer obj.mutex.RUnlock()
|
||||||
|
for k, v := range obj.status { // make a copy to avoid the mutex
|
||||||
|
status[k] = v
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout in seconds that converger was created with. This
|
||||||
|
// is useful to avoid passing in the timeout value separately when you're
|
||||||
|
// already passing in the Converger struct.
|
||||||
|
func (obj *converger) Timeout() int {
|
||||||
|
return obj.timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStateFn sets the state function to be run on change of converged state.
|
||||||
|
func (obj *converger) SetStateFn(stateFn func(bool) error) {
|
||||||
|
obj.stateFn = stateFn
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the unique id of this UID object.
|
||||||
|
func (obj *cuid) ID() uint64 {
|
||||||
|
return obj.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns a user defined name for the specific cuid.
|
||||||
|
func (obj *cuid) Name() string {
|
||||||
|
return obj.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName sets a user defined name for the specific cuid.
|
||||||
|
func (obj *cuid) SetName(name string) {
|
||||||
|
obj.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid tells us if the id is valid or has already been destroyed.
|
||||||
|
func (obj *cuid) IsValid() bool {
|
||||||
|
return obj.id != 0 // an id of 0 is invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidateID marks the id as no longer valid.
|
||||||
|
func (obj *cuid) InvalidateID() {
|
||||||
|
obj.id = 0 // an id of 0 is invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsConverged is a helper function to the regular IsConverged method.
|
||||||
|
func (obj *cuid) IsConverged() bool {
|
||||||
|
return obj.converger.IsConverged(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConverged is a helper function to the regular SetConverged notification.
|
||||||
|
func (obj *cuid) SetConverged(isConverged bool) error {
|
||||||
|
return obj.converger.SetConverged(obj, isConverged)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister is a helper function to unregister myself.
|
||||||
|
func (obj *cuid) Unregister() {
|
||||||
|
obj.converger.Unregister(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvergedTimer is a helper around the regular ConvergedTimer method.
|
||||||
|
func (obj *cuid) ConvergedTimer() <-chan time.Time {
|
||||||
|
return obj.converger.ConvergedTimer(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartTimer runs an invisible timer that automatically converges on timeout.
|
||||||
|
func (obj *cuid) StartTimer() (func() error, error) {
|
||||||
|
obj.mutex.Lock()
|
||||||
|
if !obj.running {
|
||||||
|
obj.timer = make(chan struct{})
|
||||||
|
obj.running = true
|
||||||
|
} else {
|
||||||
|
obj.mutex.Unlock()
|
||||||
|
return obj.StopTimer, fmt.Errorf("timer already started")
|
||||||
|
}
|
||||||
|
obj.mutex.Unlock()
|
||||||
|
obj.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer obj.wg.Done()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _, ok := <-obj.timer: // reset signal channel
|
||||||
|
if !ok { // channel is closed
|
||||||
|
return // false to exit
|
||||||
|
}
|
||||||
|
obj.SetConverged(false)
|
||||||
|
|
||||||
|
case <-obj.ConvergedTimer():
|
||||||
|
obj.SetConverged(true) // converged!
|
||||||
|
select {
|
||||||
|
case _, ok := <-obj.timer: // reset signal channel
|
||||||
|
if !ok { // channel is closed
|
||||||
|
return // false to exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return obj.StopTimer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetTimer resets the counter to zero if using a StartTimer internally.
|
||||||
|
func (obj *cuid) ResetTimer() error {
|
||||||
|
obj.mutex.Lock()
|
||||||
|
defer obj.mutex.Unlock()
|
||||||
|
if obj.running {
|
||||||
|
obj.timer <- struct{}{} // send the reset message
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("timer hasn't been started")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopTimer stops the running timer permanently until a StartTimer is run.
|
||||||
|
func (obj *cuid) StopTimer() error {
|
||||||
|
obj.mutex.Lock()
|
||||||
|
defer obj.mutex.Unlock()
|
||||||
|
if !obj.running {
|
||||||
|
return fmt.Errorf("timer isn't running")
|
||||||
|
}
|
||||||
|
close(obj.timer)
|
||||||
|
obj.wg.Wait()
|
||||||
|
obj.running = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Mgmt
|
// Mgmt
|
||||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
// Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
@@ -15,12 +15,5 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package main provides the main entrypoint for using the `mgmt` software.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
//"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
//func TestT1(t *testing.T) {
|
|
||||||
|
|
||||||
//}
|
|
||||||
22
docker/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.6.2
|
||||||
|
|
||||||
|
MAINTAINER Michał Czeraszkiewicz <contact@czerasz.com>
|
||||||
|
|
||||||
|
# Set the reset cache variable
|
||||||
|
# Read more here: http://czerasz.com/2014/11/13/docker-tip-and-tricks/#use-refreshedat-variable-for-better-cache-control
|
||||||
|
ENV REFRESHED_AT 2016-05-10
|
||||||
|
|
||||||
|
# Update the package list to be able to use required packages
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
# Change the working directory
|
||||||
|
WORKDIR /go/src/mgmt
|
||||||
|
|
||||||
|
# Copy all the files to the working directory
|
||||||
|
COPY . /go/src/mgmt
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN make deps
|
||||||
|
|
||||||
|
# Build the binary
|
||||||
|
RUN make build
|
||||||
31
docker/Dockerfile.development
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
FROM golang:1.6.2
|
||||||
|
|
||||||
|
MAINTAINER Michał Czeraszkiewicz <contact@czerasz.com>
|
||||||
|
|
||||||
|
# Set the reset cache variable
|
||||||
|
# Read more here: http://czerasz.com/2014/11/13/docker-tip-and-tricks/#use-refreshedat-variable-for-better-cache-control
|
||||||
|
ENV REFRESHED_AT 2016-05-14
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
# Setup User to match Host User
|
||||||
|
# Give the nre user superuser permissions
|
||||||
|
ARG USER_ID=1000
|
||||||
|
ARG GROUP_ID=1000
|
||||||
|
ARG USER_NAME=mgmt
|
||||||
|
ARG GROUP_NAME=$USER_NAME
|
||||||
|
RUN groupadd --gid $GROUP_ID $GROUP_NAME && \
|
||||||
|
useradd --create-home --home /home/$USER_NAME --uid ${USER_ID} --gid $GROUP_NAME --groups sudo $USER_NAME && \
|
||||||
|
echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||||
|
|
||||||
|
# Copy all the files to the working directory
|
||||||
|
COPY . /home/$USER_NAME/mgmt
|
||||||
|
|
||||||
|
# Change working directory
|
||||||
|
WORKDIR /home/$USER_NAME/mgmt
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN make deps
|
||||||
|
|
||||||
|
# Change user
|
||||||
|
USER ${USER_NAME}
|
||||||
26
docker/scripts/build
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
script_directory="$( cd "$( dirname "$0" )" && pwd )"
|
||||||
|
project_directory=$script_directory/../..
|
||||||
|
|
||||||
|
# Specify the Docker image name
|
||||||
|
image_name='purpleidea/mgmt'
|
||||||
|
|
||||||
|
# Build the image which contains the compiled binary
|
||||||
|
docker build -t $image_name \
|
||||||
|
--file=$project_directory/docker/Dockerfile $project_directory
|
||||||
|
|
||||||
|
# Remove the container if it already exists
|
||||||
|
docker rm -f mgmt-export 2> /dev/null
|
||||||
|
|
||||||
|
# Start the container in background so we can "copy out" the binary
|
||||||
|
docker run -d --name=mgmt-export $image_name bash -c 'while true; sleep 1000; done'
|
||||||
|
|
||||||
|
# Remove the current binary
|
||||||
|
rm $project_directory/mgmt 2> /dev/null
|
||||||
|
|
||||||
|
# Get the binary from the container
|
||||||
|
docker cp mgmt-export:/go/src/mgmt/mgmt $project_directory/mgmt
|
||||||
|
|
||||||
|
# Remove the container
|
||||||
|
docker rm -f mgmt-export 2> /dev/null
|
||||||
14
docker/scripts/build-development
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Stop on any error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
script_directory="$( cd "$( dirname "$0" )" && pwd )"
|
||||||
|
project_directory=$script_directory/../..
|
||||||
|
|
||||||
|
# Specify the Docker image name
|
||||||
|
image_name='purpleidea/mgmt:development'
|
||||||
|
|
||||||
|
# Build the image
|
||||||
|
docker build -t $image_name \
|
||||||
|
--file=$project_directory/docker/Dockerfile.development $project_directory
|
||||||
15
docker/scripts/run-development
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Stop on any error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
script_directory="$( cd "$( dirname "$0" )" && pwd )"
|
||||||
|
project_directory=$script_directory/../..
|
||||||
|
|
||||||
|
# Specify the Docker image name
|
||||||
|
image_name='purpleidea/mgmt:development'
|
||||||
|
|
||||||
|
# Run container in development mode
|
||||||
|
docker run --rm --name=mgm_development --user=mgmt \
|
||||||
|
-v $project_directory:/home/mgmt/mgmt \
|
||||||
|
-it $image_name bash
|
||||||
2
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mgmt-documentation.pdf
|
||||||
|
_build
|
||||||
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
SPHINXPROJ = mgmt
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
158
docs/conf.py
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# mgmt documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Wed Feb 15 21:34:09 2017.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its
|
||||||
|
# containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
from recommonmark.parser import CommonMarkParser
|
||||||
|
|
||||||
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#
|
||||||
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = []
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix(es) of source filenames.
|
||||||
|
# You can specify multiple suffix as a list of string:
|
||||||
|
#
|
||||||
|
|
||||||
|
source_parsers = {
|
||||||
|
'.md': CommonMarkParser,
|
||||||
|
}
|
||||||
|
|
||||||
|
source_suffix = ['.rst', '.md']
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'mgmt'
|
||||||
|
copyright = u'2013-2017+ James Shubin and the project contributors'
|
||||||
|
author = u'James Shubin'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = u''
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = u''
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#
|
||||||
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
|
# Usually you set "language" from the command line for these cases.
|
||||||
|
language = None
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'venv']
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
|
todo_include_todos = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
#html_theme = 'alabaster'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'mgmtdoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#
|
||||||
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#
|
||||||
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#
|
||||||
|
# 'preamble': '',
|
||||||
|
|
||||||
|
# Latex figure (float) alignment
|
||||||
|
#
|
||||||
|
# 'figure_align': 'htbp',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, documentclass [howto, manual, or own class]).
|
||||||
|
latex_documents = [
|
||||||
|
(master_doc, 'mgmt.tex', u'mgmt Documentation',
|
||||||
|
u'James Shubin', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
(master_doc, 'mgmt', u'mgmt Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
(master_doc, 'mgmt', u'mgmt Documentation',
|
||||||
|
author, 'mgmt', 'A next generation config management prototype!',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
646
docs/documentation.md
Normal file
@@ -0,0 +1,646 @@
|
|||||||
|
# mgmt
|
||||||
|
|
||||||
|
Available from:
|
||||||
|
[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/)
|
||||||
|
|
||||||
|
This documentation is available in: [Markdown](https://github.com/purpleidea/mgmt/blob/master/docs/documentation.md) or [PDF](https://pdfdoc-purpleidea.rhcloud.com/pdf/https://github.com/purpleidea/mgmt/blob/master/docs/documentation.md) format.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `mgmt` tool is a next generation config management prototype. It's not yet
|
||||||
|
ready for production, but we hope to get there soon. Get involved today!
|
||||||
|
|
||||||
|
## Project Description
|
||||||
|
|
||||||
|
The mgmt tool is a distributed, event driven, config management tool, that
|
||||||
|
supports parallel execution, and librarification to be used as the management
|
||||||
|
foundation in and for, new and existing software.
|
||||||
|
|
||||||
|
For more information, you may like to read some blog posts from the author:
|
||||||
|
|
||||||
|
* [Next generation config mgmt](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/)
|
||||||
|
* [Automatic edges in mgmt](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/)
|
||||||
|
* [Automatic grouping in mgmt](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/)
|
||||||
|
* [Automatic clustering in mgmt](https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/)
|
||||||
|
* [Remote execution in mgmt](https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/)
|
||||||
|
* [Send/Recv in mgmt](https://ttboj.wordpress.com/2016/12/07/sendrecv-in-mgmt/)
|
||||||
|
* [Metaparameters in mgmt](https://ttboj.wordpress.com/2017/03/01/metaparameters-in-mgmt/)
|
||||||
|
|
||||||
|
There is also an [introductory video](http://meetings-archive.debian.net/pub/debian-meetings/2016/debconf16/Next_Generation_Config_Mgmt.webm) available.
|
||||||
|
Older videos and other material [is available](https://github.com/purpleidea/mgmt/#on-the-web).
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
During this prototype phase, the tool can be run out of the source directory.
|
||||||
|
You'll probably want to use ```./run.sh run --yaml examples/graph1.yaml``` to
|
||||||
|
get started. Beware that this _can_ cause data loss. Understand what you're
|
||||||
|
doing first, or perform these actions in a virtual environment such as the one
|
||||||
|
provided by [Oh-My-Vagrant](https://github.com/purpleidea/oh-my-vagrant).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
This section details the numerous features of mgmt and some caveats you might
|
||||||
|
need to be aware of.
|
||||||
|
|
||||||
|
### Autoedges
|
||||||
|
|
||||||
|
Automatic edges, or AutoEdges, is the mechanism in mgmt by which it will
|
||||||
|
automatically create dependencies for you between resources. For example,
|
||||||
|
since mgmt can discover which files are installed by a package it will
|
||||||
|
automatically ensure that any file resource you declare that matches a
|
||||||
|
file installed by your package resource will only be processed after the
|
||||||
|
package is installed.
|
||||||
|
|
||||||
|
#### Controlling autoedges
|
||||||
|
|
||||||
|
Though autoedges is likely to be very helpful and avoid you having to declare
|
||||||
|
all dependencies explicitly, there are cases where this behaviour is
|
||||||
|
undesirable.
|
||||||
|
|
||||||
|
Some distributions allow package installations to automatically start the
|
||||||
|
service they ship. This can be problematic in the case of packages like MySQL
|
||||||
|
as there are configuration options that need to be set before MySQL is ever
|
||||||
|
started for the first time (or you'll need to wipe the data directory). In
|
||||||
|
order to handle this situation you can disable autoedges per resource and
|
||||||
|
explicitly declare that you want `my.cnf` to be written to disk before the
|
||||||
|
installation of the `mysql-server` package.
|
||||||
|
|
||||||
|
You can disable autoedges for a resource by setting the `autoedge` key on
|
||||||
|
the meta attributes of that resource to `false`.
|
||||||
|
|
||||||
|
#### Blog post
|
||||||
|
|
||||||
|
You can read the introductory blog post about this topic here:
|
||||||
|
[https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/)
|
||||||
|
|
||||||
|
### Autogrouping
|
||||||
|
|
||||||
|
Automatic grouping or AutoGroup is the mechanism in mgmt by which it will
|
||||||
|
automatically group multiple resource vertices into a single one. This is
|
||||||
|
particularly useful for grouping multiple package resources into a single
|
||||||
|
resource, since the multiple installations can happen together in a single
|
||||||
|
transaction, which saves a lot of time because package resources typically have
|
||||||
|
a large fixed cost to running (downloading and verifying the package repo) and
|
||||||
|
if they are grouped they share this fixed cost. This grouping feature can be
|
||||||
|
used for other use cases too.
|
||||||
|
|
||||||
|
You can disable autogrouping for a resource by setting the `autogroup` key on
|
||||||
|
the meta attributes of that resource to `false`.
|
||||||
|
|
||||||
|
#### Blog post
|
||||||
|
|
||||||
|
You can read the introductory blog post about this topic here:
|
||||||
|
[https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/)
|
||||||
|
|
||||||
|
### Automatic clustering
|
||||||
|
|
||||||
|
Automatic clustering is a feature by which mgmt automatically builds, scales,
|
||||||
|
and manages the embedded etcd cluster which is compiled into mgmt itself. It is
|
||||||
|
quite helpful for rapidly bootstrapping clusters and avoiding the extra work to
|
||||||
|
setup etcd.
|
||||||
|
|
||||||
|
If you prefer to avoid this feature. you can always opt to use an existing etcd
|
||||||
|
cluster that is managed separately from mgmt by pointing your mgmt agents at it
|
||||||
|
with the `--seeds` variable.
|
||||||
|
|
||||||
|
#### Blog post
|
||||||
|
|
||||||
|
You can read the introductory blog post about this topic here:
|
||||||
|
[https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/](https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/)
|
||||||
|
|
||||||
|
### Remote ("agent-less") mode
|
||||||
|
|
||||||
|
Remote mode is a special mode that lets you kick off mgmt runs on one or more
|
||||||
|
remote machines which are only accessible via SSH. In this mode the initiating
|
||||||
|
host connects over SSH, copies over the `mgmt` binary, opens an SSH tunnel, and
|
||||||
|
runs the remote program while simultaneously passing the etcd traffic back
|
||||||
|
through the tunnel so that the initiators etcd cluster can be used to exchange
|
||||||
|
resource data.
|
||||||
|
|
||||||
|
The interesting benefit of this architecture is that multiple hosts which can't
|
||||||
|
connect directly use the initiator to pass the important traffic through to each
|
||||||
|
other. Once the cluster has converged all the remote programs can shutdown
|
||||||
|
leaving no residual agent.
|
||||||
|
|
||||||
|
This mode can also be useful for bootstrapping a new host where you'd like to
|
||||||
|
have the service run continuously and as part of an mgmt cluster normally.
|
||||||
|
|
||||||
|
In particular, when combined with the `--converged-timeout` parameter, the
|
||||||
|
entire set of running mgmt agents will need to all simultaneously converge for
|
||||||
|
the group to exit. This is particularly useful for bootstrapping new clusters
|
||||||
|
which need to exchange information that is only available at run time.
|
||||||
|
|
||||||
|
#### Blog post
|
||||||
|
|
||||||
|
You can read the introductory blog post about this topic here:
|
||||||
|
[https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/](https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/)
|
||||||
|
|
||||||
|
### Puppet support
|
||||||
|
|
||||||
|
You can supply a Puppet manifest instead of creating the (YAML) graph manually.
|
||||||
|
Puppet must be installed and in `mgmt`'s search path. You also need the
|
||||||
|
[ffrank-mgmtgraph Puppet module](https://forge.puppet.com/ffrank/mgmtgraph).
|
||||||
|
|
||||||
|
Invoke `mgmt` with the `--puppet` switch, which supports 3 variants:
|
||||||
|
|
||||||
|
1. Request the configuration from the Puppet Master (like `puppet agent` does)
|
||||||
|
|
||||||
|
mgmt run --puppet agent
|
||||||
|
|
||||||
|
2. Compile a local manifest file (like `puppet apply`)
|
||||||
|
|
||||||
|
mgmt run --puppet /path/to/my/manifest.pp
|
||||||
|
|
||||||
|
3. Compile an ad hoc manifest from the commandline (like `puppet apply -e`)
|
||||||
|
|
||||||
|
mgmt run --puppet 'file { "/etc/ntp.conf": ensure => file }'
|
||||||
|
|
||||||
|
For more details and caveats see [Puppet.md](Puppet.md).
|
||||||
|
|
||||||
|
#### Blog post
|
||||||
|
|
||||||
|
An introductory post on the Puppet support is on
|
||||||
|
[Felix's blog](http://ffrank.github.io/features/2016/06/19/puppet-powered-mgmt/).
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
This section lists all the built-in resources and their properties. The
|
||||||
|
resource primitives in `mgmt` are typically more powerful than resources in
|
||||||
|
other configuration management systems because they can be event based which
|
||||||
|
lets them respond in real-time to converge to the desired state. This property
|
||||||
|
allows you to build more complex resources that you probably hadn't considered
|
||||||
|
in the past.
|
||||||
|
|
||||||
|
In addition to the resource specific properties, there are resource properties
|
||||||
|
(otherwise known as parameters) which can apply to every resource. These are
|
||||||
|
called [meta parameters](#meta-parameters) and are listed separately. Certain
|
||||||
|
meta parameters aren't very useful when combined with certain resources, but
|
||||||
|
in general, it should be fairly obvious, such as when combining the `noop` meta
|
||||||
|
parameter with the [Noop](#Noop) resource.
|
||||||
|
|
||||||
|
* [Augeas](#Augeas): Manipulate files using augeas.
|
||||||
|
* [Exec](#Exec): Execute shell commands on the system.
|
||||||
|
* [File](#File): Manage files and directories.
|
||||||
|
* [Hostname](#Hostname): Manages the hostname on the system.
|
||||||
|
* [KV](#KV): Set a key value pair in our shared world database.
|
||||||
|
* [Msg](#Msg): Send log messages.
|
||||||
|
* [Noop](#Noop): A simple resource that does nothing.
|
||||||
|
* [Nspawn](#Nspawn): Manage systemd-machined nspawn containers.
|
||||||
|
* [Password](#Password): Create random password strings.
|
||||||
|
* [Pkg](#Pkg): Manage system packages with PackageKit.
|
||||||
|
* [Svc](#Svc): Manage system systemd services.
|
||||||
|
* [Timer](#Timer): Manage system systemd services.
|
||||||
|
* [Virt](#Virt): Manage virtual machines with libvirt.
|
||||||
|
|
||||||
|
|
||||||
|
### Augeas
|
||||||
|
|
||||||
|
The augeas resource uses [augeas](http://augeas.net/) commands to manipulate
|
||||||
|
files.
|
||||||
|
|
||||||
|
### Exec
|
||||||
|
|
||||||
|
The exec resource can execute commands on your system.
|
||||||
|
|
||||||
|
### File
|
||||||
|
|
||||||
|
The file resource manages files and directories. In `mgmt`, directories are
|
||||||
|
identified by a trailing slash in their path name. File have no such slash.
|
||||||
|
|
||||||
|
It has the following properties:
|
||||||
|
|
||||||
|
- `path`: file path (directories have a trailing slash here)
|
||||||
|
- `content`: raw file content
|
||||||
|
- `state`: either `exists` (the default value) or `absent`
|
||||||
|
- `mode`: octal unix file permissions
|
||||||
|
- `owner`: username or uid for the file owner
|
||||||
|
- `group`: group name or gid for the file group
|
||||||
|
|
||||||
|
#### Path
|
||||||
|
|
||||||
|
The path property specifies the file or directory that we are managing.
|
||||||
|
|
||||||
|
#### Content
|
||||||
|
|
||||||
|
The content property is a string that specifies the desired file contents.
|
||||||
|
|
||||||
|
#### Source
|
||||||
|
|
||||||
|
The source property points to a source file or directory path that we wish to
|
||||||
|
copy over and use as the desired contents for our resource.
|
||||||
|
|
||||||
|
#### State
|
||||||
|
|
||||||
|
The state property describes the action we'd like to apply for the resource. The
|
||||||
|
possible values are: `exists` and `absent`.
|
||||||
|
|
||||||
|
#### Recurse
|
||||||
|
|
||||||
|
The recurse property limits whether file resource operations should recurse into
|
||||||
|
and monitor directory contents with a depth greater than one.
|
||||||
|
|
||||||
|
#### Force
|
||||||
|
|
||||||
|
The force property is required if we want the file resource to be able to change
|
||||||
|
a file into a directory or vice-versa. If such a change is needed, but the force
|
||||||
|
property is not set to `true`, then this file resource will error.
|
||||||
|
|
||||||
|
### Hostname
|
||||||
|
|
||||||
|
The hostname resource manages static, transient/dynamic and pretty hostnames
|
||||||
|
on the system and watches them for changes.
|
||||||
|
|
||||||
|
#### static_hostname
|
||||||
|
The static hostname is the one configured in /etc/hostname or a similar
|
||||||
|
file.
|
||||||
|
It is chosen by the local user. It is not always in sync with the current
|
||||||
|
host name as returned by the gethostname() system call.
|
||||||
|
|
||||||
|
#### transient_hostname
|
||||||
|
The transient / dynamic hostname is the one configured via the kernel's
|
||||||
|
sethostbyname().
|
||||||
|
It can be different from the static hostname in case DHCP or mDNS have been
|
||||||
|
configured to change the name based on network information.
|
||||||
|
|
||||||
|
#### pretty_hostname
|
||||||
|
The pretty hostname is a free-form UTF8 host name for presentation to the user.
|
||||||
|
|
||||||
|
#### hostname
|
||||||
|
Hostname is the fallback value for all 3 fields above, if only `hostname` is
|
||||||
|
specified, it will set all 3 fields to this value.
|
||||||
|
|
||||||
|
### KV
|
||||||
|
|
||||||
|
The KV resource sets a key and value pair in the global world database. This is
|
||||||
|
quite useful for setting a flag after a number of resources have run. It will
|
||||||
|
ignore database updates to the value that are greater in compare order than the
|
||||||
|
requested key if the `SkipLessThan` parameter is set to true. If we receive a
|
||||||
|
refresh, then the stored value will be reset to the requested value even if the
|
||||||
|
stored value is greater.
|
||||||
|
|
||||||
|
#### Key
|
||||||
|
The string key used to store the key.
|
||||||
|
|
||||||
|
#### Value
|
||||||
|
The string value to set. This can also be set via Send/Recv.
|
||||||
|
|
||||||
|
#### SkipLessThan
|
||||||
|
If this parameter is set to `true`, then it will ignore updating the value as
|
||||||
|
long as the database versions are greater than the requested value. The compare
|
||||||
|
operation used is based on the `SkipCmpStyle` parameter.
|
||||||
|
|
||||||
|
#### SkipCmpStyle
|
||||||
|
By default this converts the string values to integers and compares them as you
|
||||||
|
would expect.
|
||||||
|
|
||||||
|
### Msg
|
||||||
|
|
||||||
|
The msg resource sends messages to the main log, or an external service such
|
||||||
|
as systemd's journal.
|
||||||
|
|
||||||
|
### Noop
|
||||||
|
|
||||||
|
The noop resource does absolutely nothing. It does have some utility in testing
|
||||||
|
`mgmt` and also as a placeholder in the resource graph.
|
||||||
|
|
||||||
|
### Nspawn
|
||||||
|
|
||||||
|
The nspawn resource is used to manage systemd-machined style containers.
|
||||||
|
|
||||||
|
### Password
|
||||||
|
|
||||||
|
The password resource can generate a random string to be used as a password. It
|
||||||
|
will re-generate the password if it receives a refresh notification.
|
||||||
|
|
||||||
|
### Pkg
|
||||||
|
|
||||||
|
The pkg resource is used to manage system packages. This resource works on many
|
||||||
|
different distributions because it uses the underlying packagekit facility which
|
||||||
|
supports different backends for different environments. This ensures that we
|
||||||
|
have great Debian (deb/dpkg) and Fedora (rpm/dnf) support simultaneously.
|
||||||
|
|
||||||
|
### Svc
|
||||||
|
|
||||||
|
The service resource is still very WIP. Please help us my improving it!
|
||||||
|
|
||||||
|
### Timer
|
||||||
|
|
||||||
|
This resource needs better documentation. Please help us my improving it!
|
||||||
|
|
||||||
|
### Virt
|
||||||
|
|
||||||
|
The virt resource can manage virtual machines via libvirt.
|
||||||
|
|
||||||
|
## Usage and frequently asked questions
|
||||||
|
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
||||||
|
respond by commit with the answer.)
|
||||||
|
|
||||||
|
### Why did you start this project?
|
||||||
|
|
||||||
|
I wanted a next generation config management solution that didn't have all of
|
||||||
|
the design flaws or limitations that the current generation of tools do, and no
|
||||||
|
tool existed!
|
||||||
|
|
||||||
|
### Why did you use etcd? What about consul?
|
||||||
|
|
||||||
|
Etcd and consul are both written in golang, which made them the top two
|
||||||
|
contenders for my prototype. Ultimately a choice had to be made, and etcd was
|
||||||
|
chosen, but it was also somewhat arbitrary. If there is available interest,
|
||||||
|
good reasoning, *and* patches, then we would consider either switching or
|
||||||
|
supporting both, but this is not a high priority at this time.
|
||||||
|
|
||||||
|
### Can I use an existing etcd cluster instead of the automatic embedded servers?
|
||||||
|
|
||||||
|
Yes, it's possible to use an existing etcd cluster instead of the automatic,
|
||||||
|
elastic embedded etcd servers. To do so, simply point to the cluster with the
|
||||||
|
`--seeds` variable, the same way you would if you were seeding a new member to
|
||||||
|
an existing mgmt cluster.
|
||||||
|
|
||||||
|
The downside to this approach is that you won't benefit from the automatic
|
||||||
|
elastic nature of the embedded etcd servers, and that you're responsible if you
|
||||||
|
accidentally break your etcd cluster, or if you use an unsupported version.
|
||||||
|
|
||||||
|
### What does the error message about an inconsistent dataDir mean?
|
||||||
|
|
||||||
|
If you get an error message similar to:
|
||||||
|
|
||||||
|
```
|
||||||
|
Etcd: Connect: CtxError...
|
||||||
|
Etcd: CtxError: Reason: CtxDelayErr(5s): No endpoints available yet!
|
||||||
|
Etcd: Connect: Endpoints: []
|
||||||
|
Etcd: The dataDir (/var/lib/mgmt/etcd) might be inconsistent or corrupt.
|
||||||
|
```
|
||||||
|
|
||||||
|
This happens when there are a series of fatal connect errors in a row. This can
|
||||||
|
happen when you start `mgmt` using a dataDir that doesn't correspond to the
|
||||||
|
current cluster view. As a result, the embedded etcd server never finishes
|
||||||
|
starting up, and as a result, a default endpoint never gets added. The solution
|
||||||
|
is to either reconcile the mistake, and if there is no important data saved, you
|
||||||
|
can remove the etcd dataDir. This is typically `/var/lib/mgmt/etcd/member/`.
|
||||||
|
|
||||||
|
### Why do resources have both a `Compare` method and an `IFF` (on the UID) method?
|
||||||
|
|
||||||
|
The `Compare()` methods are for determining if two resources are effectively the
|
||||||
|
same, which is used to make graph change delta's efficient. This is when we want
|
||||||
|
to change from the current running graph to a new graph, but preserve the common
|
||||||
|
vertices. Since we want to make this process efficient, we only update the parts
|
||||||
|
that are different, and leave everything else alone. This `Compare()` method can
|
||||||
|
tell us if two resources are the same.
|
||||||
|
|
||||||
|
The `IFF()` method is part of the whole UID system, which is for discerning if a
|
||||||
|
resource meets the requirements another expects for an automatic edge. This is
|
||||||
|
because the automatic edge system assumes a unified UID pattern to test for
|
||||||
|
equality. In the future it might be helpful or sane to merge the two similar
|
||||||
|
comparison functions although for now they are separate because they are
|
||||||
|
actually answer different questions.
|
||||||
|
|
||||||
|
### Did you know that there is a band named `MGMT`?
|
||||||
|
|
||||||
|
I didn't realize this when naming the project, and it is accidental. After much
|
||||||
|
anguishing, I chose the name because it was short and I thought it was
|
||||||
|
appropriately descriptive. If you need a less ambiguous search term or phrase,
|
||||||
|
you can try using `mgmtconfig` or `mgmt config`.
|
||||||
|
|
||||||
|
### You didn't answer my question, or I have a question!
|
||||||
|
|
||||||
|
It's best to ask on [IRC](https://webchat.freenode.net/?channels=#mgmtconfig)
|
||||||
|
to see if someone can help you. Once we get a big enough community going, we'll
|
||||||
|
add a mailing list. If you don't get any response from the above, you can
|
||||||
|
contact me through my [technical blog](https://ttboj.wordpress.com/contact/)
|
||||||
|
and I'll do my best to help. If you have a good question, please add it as a
|
||||||
|
patch to this documentation. I'll merge your question, and add a patch with the
|
||||||
|
answer!
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
Please note that there are a number of undocumented options. For more
|
||||||
|
information on these options, please view the source at:
|
||||||
|
[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/).
|
||||||
|
If you feel that a well used option needs documenting here, please patch it!
|
||||||
|
|
||||||
|
### Overview of reference
|
||||||
|
* [Meta parameters](#meta-parameters): List of available resource meta parameters.
|
||||||
|
* [Graph definition file](#graph-definition-file): Main graph definition file.
|
||||||
|
* [Command line](#command-line): Command line parameters.
|
||||||
|
* [Compilation options](#compilation-options): Compilation options.
|
||||||
|
|
||||||
|
### Meta parameters
|
||||||
|
These meta parameters are special parameters (or properties) which can apply to
|
||||||
|
any resource. The usefulness of doing so will depend on the particular meta
|
||||||
|
parameter and resource combination.
|
||||||
|
|
||||||
|
#### AutoEdge
|
||||||
|
Boolean. Should we generate auto edges for this resource?
|
||||||
|
|
||||||
|
#### AutoGroup
|
||||||
|
Boolean. Should we attempt to automatically group this resource with others?
|
||||||
|
|
||||||
|
#### Noop
|
||||||
|
Boolean. Should the Apply portion of the CheckApply method of the resource
|
||||||
|
make any changes? Noop is a concatenation of no-operation.
|
||||||
|
|
||||||
|
#### Retry
|
||||||
|
Integer. The number of times to retry running the resource on error. Use -1 for
|
||||||
|
infinite. This currently applies for both the Watch operation (which can fail)
|
||||||
|
and for the CheckApply operation. While they could have separate values, I've
|
||||||
|
decided to use the same ones for both until there's a proper reason to want to
|
||||||
|
do something differently for the Watch errors.
|
||||||
|
|
||||||
|
#### Delay
|
||||||
|
Integer. Number of milliseconds to wait between retries. The same value is
|
||||||
|
shared between the Watch and CheckApply retries. This currently applies for both
|
||||||
|
the Watch operation (which can fail) and for the CheckApply operation. While
|
||||||
|
they could have separate values, I've decided to use the same ones for both
|
||||||
|
until there's a proper reason to want to do something differently for the Watch
|
||||||
|
errors.
|
||||||
|
|
||||||
|
#### Poll
|
||||||
|
Integer. Number of seconds to wait between `CheckApply` checks. If this is
|
||||||
|
greater than zero, then the standard event based `Watch` mechanism for this
|
||||||
|
resource is replaced with a simple polling mechanism. In general, this is not
|
||||||
|
recommended, unless you have a very good reason for doing so.
|
||||||
|
|
||||||
|
Please keep in mind that if you have a resource which changes every `I` seconds,
|
||||||
|
and you poll it every `J` seconds, and you've asked for a converged timeout of
|
||||||
|
`K` seconds, and `I <= J <= K`, then your graph will likely never converge.
|
||||||
|
|
||||||
|
When polling, the system detects that a resource is not converged if its
|
||||||
|
`CheckApply` method returns false. This allows a resource which changes every
|
||||||
|
`I` seconds, and which is polled every `J` seconds, and with a converged timeout
|
||||||
|
of `K` seconds to still converge when `J <= K`, as long as `I > J || I > K`,
|
||||||
|
which is another way of saying that if the resource finally settles down to give
|
||||||
|
the graph enough time, it can probably converge.
|
||||||
|
|
||||||
|
#### Limit
|
||||||
|
Float. Maximum rate of `CheckApply` runs started per second. Useful to limit
|
||||||
|
an especially _eventful_ process from causing excessive checks to run. This
|
||||||
|
defaults to `+Infinity` which adds no limiting. If you change this value, you
|
||||||
|
will also need to change the `Burst` value to a non-zero value. Please see the
|
||||||
|
[rate](https://godoc.org/golang.org/x/time/rate) package for more information.
|
||||||
|
|
||||||
|
#### Burst
|
||||||
|
Integer. Burst is the maximum number of runs which can happen without invoking
|
||||||
|
the rate limiter as designated by the `Limit` value. If the `Limit` is not set
|
||||||
|
to `+Infinity`, this must be a non-zero value. Please see the
|
||||||
|
[rate](https://godoc.org/golang.org/x/time/rate) package for more information.
|
||||||
|
|
||||||
|
#### Sema
|
||||||
|
List of string ids. Sema is a P/V style counting semaphore which can be used to
|
||||||
|
limit parallelism during the CheckApply phase of resource execution. Each
|
||||||
|
resource can have `N` different semaphores which share a graph global namespace.
|
||||||
|
Each semaphore has a maximum count associated with it. The default value of the
|
||||||
|
size is 1 (one) if size is unspecified. Each string id is the unique id of the
|
||||||
|
semaphore. If the id contains a trailing colon (:) followed by a positive
|
||||||
|
integer, then that value is the max size for that semaphore. Valid semaphore
|
||||||
|
id's include: `some_id`, `hello:42`, `not:smart:4` and `:13`. It is expected
|
||||||
|
that the last bare example be only used by the engine to add a global semaphore.
|
||||||
|
|
||||||
|
### Graph definition file
|
||||||
|
graph.yaml is the compiled graph definition file. The format is currently
|
||||||
|
undocumented, but by looking through the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples)
|
||||||
|
you can probably figure out most of it, as it's fairly intuitive.
|
||||||
|
|
||||||
|
### Command line
|
||||||
|
The main interface to the `mgmt` tool is the command line. For the most recent
|
||||||
|
documentation, please run `mgmt --help`.
|
||||||
|
|
||||||
|
#### `--yaml <graph.yaml>`
|
||||||
|
Point to a graph file to run.
|
||||||
|
|
||||||
|
#### `--converged-timeout <seconds>`
|
||||||
|
Exit if the machine has converged for approximately this many seconds.
|
||||||
|
|
||||||
|
#### `--max-runtime <seconds>`
|
||||||
|
Exit when the agent has run for approximately this many seconds. This is not
|
||||||
|
generally recommended, but may be useful for users who know what they're doing.
|
||||||
|
|
||||||
|
#### `--noop`
|
||||||
|
Globally force all resources into no-op mode. This also disables the export to
|
||||||
|
etcd functionality, but does not disable resource collection, however all
|
||||||
|
resources that are collected will have their individual noop settings set.
|
||||||
|
|
||||||
|
#### `--sema <size>`
|
||||||
|
Globally add a counting semaphore of this size to each resource in the graph.
|
||||||
|
The semaphore will get given an id of `:size`. In other words if you specify a
|
||||||
|
size of 42, you can expect a semaphore if named: `:42`. It is expected that
|
||||||
|
consumers of the semaphore metaparameter always include a prefix to avoid a
|
||||||
|
collision with this globally defined semaphore. The size value must be greater
|
||||||
|
than zero at this time. The traditional non-parallel execution found in config
|
||||||
|
management tools such as `Puppet` can be obtained with `--sema 1`.
|
||||||
|
|
||||||
|
#### `--remote <graph.yaml>`
|
||||||
|
Point to a graph file to run on the remote host specified within. This parameter
|
||||||
|
can be used multiple times if you'd like to remotely run on multiple hosts in
|
||||||
|
parallel.
|
||||||
|
|
||||||
|
#### `--allow-interactive`
|
||||||
|
Allow interactive prompting for SSH passwords if there is no authentication
|
||||||
|
method that works.
|
||||||
|
|
||||||
|
#### `--ssh-priv-id-rsa`
|
||||||
|
Specify the path for finding SSH keys. This defaults to `~/.ssh/id_rsa`. To
|
||||||
|
never use this method of authentication, set this to the empty string.
|
||||||
|
|
||||||
|
#### `--cconns`
|
||||||
|
The maximum number of concurrent remote ssh connections to run. This defaults
|
||||||
|
to `0`, which means unlimited.
|
||||||
|
|
||||||
|
#### `--no-caching`
|
||||||
|
Don't allow remote caching of the remote execution binary. This will require
|
||||||
|
the binary to be copied over for every remote execution, but it limits the
|
||||||
|
likelihood that there is leftover information from the configuration process.
|
||||||
|
|
||||||
|
#### `--prefix <path>`
|
||||||
|
Specify a path to a custom working directory prefix. This directory will get
|
||||||
|
created if it does not exist. This usually defaults to `/var/lib/mgmt/`. This
|
||||||
|
can't be combined with the `--tmp-prefix` option. It can be combined with the
|
||||||
|
`--allow-tmp-prefix` option.
|
||||||
|
|
||||||
|
#### `--tmp-prefix`
|
||||||
|
If this option is specified, a temporary prefix will be used instead of the
|
||||||
|
default prefix. This can't be combined with the `--prefix` option.
|
||||||
|
|
||||||
|
#### `--allow-tmp-prefix`
|
||||||
|
If this option is specified, we will attempt to fall back to a temporary prefix
|
||||||
|
if the primary prefix couldn't be created. This is useful for avoiding failures
|
||||||
|
in environments where the primary prefix may or may not be available, but you'd
|
||||||
|
like to try. The canonical example is when running `mgmt` with `--remote` there
|
||||||
|
might be a cached copy of the binary in the primary prefix, but in case there's
|
||||||
|
no binary available continue working in a temporary directory to avoid failure.
|
||||||
|
|
||||||
|
### Compilation options
|
||||||
|
|
||||||
|
You can control some compilation variables by using environment variables.
|
||||||
|
|
||||||
|
#### Disable libvirt support
|
||||||
|
|
||||||
|
If you wish to compile mgmt without libvirt, you can use the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
GOTAGS=novirt make build
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Disable augeas support
|
||||||
|
|
||||||
|
If you wish to compile mgmt without augeas support, you can use the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
GOTAGS=noaugeas make build
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Combining compile-time flags
|
||||||
|
|
||||||
|
You can combine multiple tags by using a space-separated list:
|
||||||
|
|
||||||
|
```
|
||||||
|
GOTAGS="noaugeas novirt" make build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
For example configurations, please consult the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples) directory in the git
|
||||||
|
source repository. It is available from:
|
||||||
|
|
||||||
|
[https://github.com/purpleidea/mgmt/tree/master/examples](https://github.com/purpleidea/mgmt/tree/master/examples)
|
||||||
|
|
||||||
|
### Systemd:
|
||||||
|
See [`misc/mgmt.service`](misc/mgmt.service) for a sample systemd unit file.
|
||||||
|
This unit file is part of the RPM.
|
||||||
|
|
||||||
|
To specify your custom options for `mgmt` on a systemd distro:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /etc/systemd/system/mgmt.service.d/
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/mgmt.service.d/env.conf <<EOF
|
||||||
|
# Environment variables:
|
||||||
|
MGMT_SEEDS=http://127.0.0.1:2379
|
||||||
|
MGMT_CONVERGED_TIMEOUT=-1
|
||||||
|
MGMT_MAX_RUNTIME=0
|
||||||
|
|
||||||
|
# Other CLI options if necessary.
|
||||||
|
#OPTS="--max-runtime=0"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
This is a project that I started in my free time in 2013. Development is driven
|
||||||
|
by all of our collective patches! Dive right in, and start hacking!
|
||||||
|
Please contact me if you'd like to invite me to speak about this at your event.
|
||||||
|
|
||||||
|
You can follow along [on my technical blog](https://ttboj.wordpress.com/).
|
||||||
|
|
||||||
|
To report any bugs, please file a ticket at: [https://github.com/purpleidea/mgmt/issues](https://github.com/purpleidea/mgmt/issues).
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
|
|
||||||
|
Please see the
|
||||||
|
[AUTHORS](https://github.com/purpleidea/mgmt/tree/master/AUTHORS) file
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
* [github](https://github.com/purpleidea/)
|
||||||
|
* [@purpleidea](https://twitter.com/#!/purpleidea)
|
||||||
|
* [https://ttboj.wordpress.com/](https://ttboj.wordpress.com/)
|
||||||
17
docs/index.rst
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
.. mgmt documentation master file, created by
|
||||||
|
sphinx-quickstart on Wed Feb 15 21:34:09 2017.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to mgmt's documentation!
|
||||||
|
================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
documentation
|
||||||
|
quick-start-guide
|
||||||
|
resource-guide
|
||||||
|
prometheus
|
||||||
|
puppet-guide
|
||||||
66
docs/prometheus.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Prometheus support
|
||||||
|
|
||||||
|
Mgmt comes with a built-in prometheus support. It is disabled by default, and
|
||||||
|
can be enabled with the `--prometheus` command line switch.
|
||||||
|
|
||||||
|
By default, the prometheus instance will listen on [`127.0.0.1:9233`][pd]. You
|
||||||
|
can change this setting by using the `--prometheus-listen` cli option:
|
||||||
|
|
||||||
|
To have mgmt prometheus bind interface on 0.0.0.0:45001, use:
|
||||||
|
`./mgmt r --prometheus --prometheus-listen :45001`
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
Mgmt exposes three kinds of resources: _go_ metrics, _etcd_ metrics and _mgmt_
|
||||||
|
metrics.
|
||||||
|
|
||||||
|
### go metrics
|
||||||
|
|
||||||
|
We use the [prometheus go_collector][pgc] to expose go metrics. Those metrics
|
||||||
|
are mainly useful for debugging and perf testing.
|
||||||
|
|
||||||
|
### etcd metrics
|
||||||
|
|
||||||
|
mgmt exposes etcd metrics. Read more in the [upstream documentation][etcdm]
|
||||||
|
|
||||||
|
### mgmt metrics
|
||||||
|
|
||||||
|
Here is a list of the metrics we provide:
|
||||||
|
|
||||||
|
- `mgmt_resources_total`: The number of resources that mgmt is managing
|
||||||
|
- `mgmt_checkapply_total`: The number of CheckApply's that mgmt has run
|
||||||
|
- `mgmt_failures_total`: The number of resources that have failed
|
||||||
|
- `mgmt_failures_current`: The number of resources that have failed
|
||||||
|
- `mgmt_graph_start_time_seconds`: Start time of the current graph since unix epoch in seconds
|
||||||
|
|
||||||
|
For each metric, you will get some extra labels:
|
||||||
|
|
||||||
|
- `kind`: The kind of mgmt resource
|
||||||
|
|
||||||
|
For `mgmt_checkapply_total`, those extra labels are set:
|
||||||
|
|
||||||
|
- `eventful`: "true" or "false", if the CheckApply triggered some changes
|
||||||
|
- `errorful`: "true" or "false", if the CheckApply reported an error
|
||||||
|
- `apply`: "true" or "false", if the CheckApply ran in apply or noop mode
|
||||||
|
|
||||||
|
## Alerting
|
||||||
|
|
||||||
|
You can use prometheus to alert you upon changes or failures. We do not provide
|
||||||
|
such templates yet, but we plan to provide some examples in this repository.
|
||||||
|
Patches welcome!
|
||||||
|
|
||||||
|
## Grafana
|
||||||
|
|
||||||
|
We do not have grafana dashboards yet. Patches welcome!
|
||||||
|
|
||||||
|
## External resources
|
||||||
|
|
||||||
|
- [prometheus website](https://prometheus.io/)
|
||||||
|
- [prometheus documentation](https://prometheus.io/docs/introduction/overview/)
|
||||||
|
- [prometheus best practices regarding metrics
|
||||||
|
naming](https://prometheus.io/docs/practices/naming/)
|
||||||
|
- [grafana website](http://grafana.org/)
|
||||||
|
|
||||||
|
[pgc]: https://github.com/prometheus/client_golang/blob/master/prometheus/go_collector.go
|
||||||
|
[etcdm]: https://coreos.com/etcd/docs/latest/metrics.html
|
||||||
|
[pd]: https://github.com/prometheus/prometheus/wiki/Default-port-allocation
|
||||||
166
docs/puppet-guide.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# Puppet guide
|
||||||
|
|
||||||
|
`mgmt` can use Puppet as its source for the configuration graph.
|
||||||
|
This document goes into detail on how this works, and lists
|
||||||
|
some pitfalls and limitations.
|
||||||
|
|
||||||
|
For basic instructions on how to use the Puppet support, see
|
||||||
|
the [main documentation](documentation.md#puppet-support).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
You need Puppet installed in your system. It is not important how you
|
||||||
|
get it. On the most common Linux distributions, you can use packages
|
||||||
|
from the OS maintainer, or upstream Puppet repositories. An alternative
|
||||||
|
that will also work on OSX is the `puppet` Ruby gem. It also has the
|
||||||
|
advantage that you can install any desired version in your home directory
|
||||||
|
or any other location.
|
||||||
|
|
||||||
|
Any release of Puppet's 3.x and 4.x series should be suitable for use with
|
||||||
|
`mgmt`. Most importantly, make sure to install the `ffrank-mgmtgraph` Puppet
|
||||||
|
module (referred to below as "the translator module").
|
||||||
|
|
||||||
|
```
|
||||||
|
puppet module install ffrank-mgmtgraph
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that the module is not required on your Puppet master (if you
|
||||||
|
use a master/agent setup). It's needed on the machine that runs `mgmt`.
|
||||||
|
You can install the module on the master anyway, so that it gets distributed
|
||||||
|
to your agents through Puppet's `pluginsync` mechanism.
|
||||||
|
|
||||||
|
### Testing the Puppet side
|
||||||
|
|
||||||
|
The following command should run successfully and print a YAML hash on your
|
||||||
|
terminal:
|
||||||
|
|
||||||
|
```puppet
|
||||||
|
puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": ensure => present }'
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use this CLI to test any manifests before handing them straight
|
||||||
|
to `mgmt`.
|
||||||
|
|
||||||
|
## Writing a suitable manifest
|
||||||
|
|
||||||
|
### Unsupported attributes
|
||||||
|
|
||||||
|
`mgmt` inherited its resource module from Puppet, so by and large, it's quite
|
||||||
|
possible to express `mgmt` graphs in terms of Puppet manifests. However,
|
||||||
|
there isn't (and likely never will be) full feature parity between the
|
||||||
|
respective resource types. In consequence, a manifest can have semantics that
|
||||||
|
cannot be transferred to `mgmt`.
|
||||||
|
|
||||||
|
For example, at the time of writing this, the `file` type in `mgmt` had no
|
||||||
|
notion of permissions (the file `mode`) yet. This lead to the following
|
||||||
|
warning (among others that will be discussed below):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ puppet mgmtgraph print --code 'file { "/tmp/foo": mode => "0600" }'
|
||||||
|
Warning: cannot translate: File[/tmp/foo] { mode => "600" } (attribute is ignored)
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a heads-up for the user, because the resulting `mgmt` graph will
|
||||||
|
in fact not pass this information to the `/tmp/foo` file resource, and
|
||||||
|
`mgmt` will ignore this file's permissions. Including such attributes in
|
||||||
|
manifests that are written expressly for `mgmt` is not sensible and should
|
||||||
|
be avoided.
|
||||||
|
|
||||||
|
### Unsupported resources
|
||||||
|
|
||||||
|
Puppet has a fairly large number of
|
||||||
|
[built-in types](https://docs.puppet.com/puppet/latest/reference/type.html),
|
||||||
|
and countless more are available through
|
||||||
|
[modules](https://forge.puppet.com/). It's unlikely that all of them will
|
||||||
|
eventually receive native counterparts in `mgmt`.
|
||||||
|
|
||||||
|
When encountering an unknown resource, the translator module will replace
|
||||||
|
it with an `exec` resource in its output. This resource will run the equivalent
|
||||||
|
of a `puppet resource` command to make Puppet apply the original resource
|
||||||
|
itself. This has quite abysmal performance, because processing such a
|
||||||
|
resource requires the forking of at least one Puppet process (two if it
|
||||||
|
is found to be out of sync). This comes with considerable overhead.
|
||||||
|
On most systems, starting up any Puppet command takes several seconds.
|
||||||
|
Compared to the split second that the actual work usually takes,
|
||||||
|
this overhead can amount to several orders of magnitude.
|
||||||
|
|
||||||
|
Avoid Puppet types that `mgmt` does not implement (yet).
|
||||||
|
|
||||||
|
### Avoiding common warnings
|
||||||
|
|
||||||
|
Many resource parameters in Puppet take default values. For the most part,
|
||||||
|
the translator module just ignores them. However, there are cases in which
|
||||||
|
Puppet will default to convenient behavior that `mgmt` cannot quite replicate.
|
||||||
|
For example, translating a plain `file` resource will lead to a warning message:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": }'
|
||||||
|
Warning: File[/tmp/mgmt-test] uses the 'puppet' file bucket, which mgmt cannot do. There will be no backup copies!
|
||||||
|
```
|
||||||
|
|
||||||
|
The reason is that per default, Puppet assumes the following parameter value
|
||||||
|
(among others)
|
||||||
|
|
||||||
|
```puppet
|
||||||
|
file { "/tmp/mgmt-test":
|
||||||
|
backup => 'puppet',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To avoid this, specify the parameter explicitly:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": backup => false }'
|
||||||
|
```
|
||||||
|
|
||||||
|
This is tedious in a more complex manifest. A good simplification is the
|
||||||
|
following [resource default](https://docs.puppet.com/puppet/latest/reference/lang_defaults.html)
|
||||||
|
anywhere on the top scope of your manifest:
|
||||||
|
|
||||||
|
```puppet
|
||||||
|
File { backup => false }
|
||||||
|
```
|
||||||
|
|
||||||
|
If you encounter similar warnings from other types and/or parameters,
|
||||||
|
use the same approach to silence them if possible.
|
||||||
|
|
||||||
|
## Configuring Puppet
|
||||||
|
|
||||||
|
Since `mgmt` uses an actual Puppet CLI behind the scenes, you might
|
||||||
|
need to tweak some of Puppet's runtime options in order to make it
|
||||||
|
do what you want. Reasons for this could be among the following:
|
||||||
|
|
||||||
|
* You use the `--puppet agent` variant and need to configure
|
||||||
|
`servername`, `certname` and other master/agent-related options.
|
||||||
|
* You don't want runtime information to end up in the `vardir`
|
||||||
|
that is used by your regular `puppet agent`.
|
||||||
|
* You install specific Puppet modules for `mgmt` in a non-standard
|
||||||
|
location.
|
||||||
|
|
||||||
|
`mgmt` exposes only one Puppet option in order to allow you to
|
||||||
|
control all of them, through its `--puppet-conf` option. It allows
|
||||||
|
you to specify which `puppet.conf` file should be used during
|
||||||
|
translation.
|
||||||
|
|
||||||
|
```
|
||||||
|
mgmt run --puppet /opt/my-manifest.pp --puppet-conf /etc/mgmt/puppet.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Within this file, you can just specify any needed options in the
|
||||||
|
`[main]` section:
|
||||||
|
|
||||||
|
```
|
||||||
|
[main]
|
||||||
|
server=mgmt-master.example.net
|
||||||
|
vardir=/var/lib/mgmt/puppet
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
Please see the [README](https://github.com/ffrank/puppet-mgmtgraph/blob/master/README.md)
|
||||||
|
of the translator module for the current state of supported and unsupported
|
||||||
|
language features.
|
||||||
|
|
||||||
|
You should probably make sure to always use the latest release of
|
||||||
|
both `ffrank-mgmtgraph` and `ffrank-yamlresource` (the latter is
|
||||||
|
getting pulled in as a dependency of the former).
|
||||||
93
docs/quick-start-guide.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# Quick start guide
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
This guide is intended for developers. Once `mgmt` is minimally viable, we'll
|
||||||
|
publish a quick start guide for users too. In the meantime, please contribute!
|
||||||
|
If you're brand new to `mgmt`, it's probably a good idea to start by reading the
|
||||||
|
[introductory article](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/)
|
||||||
|
or to watch an [introductory video](https://github.com/purpleidea/mgmt/#on-the-web).
|
||||||
|
Once you're familiar with the general idea, please start hacking...
|
||||||
|
|
||||||
|
## Vagrant
|
||||||
|
If you would like to avoid doing the following steps manually, we have prepared
|
||||||
|
a [Vagrant](https://www.vagrantup.com/) environment for your convenience. From
|
||||||
|
the project directory, run a `vagrant up`, and then a `vagrant status`. From
|
||||||
|
there, you can `vagrant ssh` into the `mgmt` machine. The MOTD will explain the
|
||||||
|
rest.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
Software projects have a few different kinds of dependencies. There are _build_
|
||||||
|
dependencies, _runtime_ dependencies, and additionally, a few extra dependencies
|
||||||
|
required for running the _test_ suite.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
* `golang` 1.6 or higher (required, available in most distros)
|
||||||
|
* golang libraries (required, available with `go get ./...`) a partial list includes:
|
||||||
|
```
|
||||||
|
github.com/coreos/etcd/client
|
||||||
|
gopkg.in/yaml.v2
|
||||||
|
gopkg.in/fsnotify.v1
|
||||||
|
github.com/urfave/cli
|
||||||
|
github.com/coreos/go-systemd/dbus
|
||||||
|
github.com/coreos/go-systemd/util
|
||||||
|
github.com/libvirt/libvirt-go
|
||||||
|
```
|
||||||
|
* `stringer` (optional), available as a package on some platforms, otherwise via `go get`
|
||||||
|
```
|
||||||
|
golang.org/x/tools/cmd/stringer
|
||||||
|
```
|
||||||
|
* `pandoc` (optional), for building a pdf of the documentation
|
||||||
|
|
||||||
|
### Runtime
|
||||||
|
A relatively modern GNU/Linux system should be able to run `mgmt` without any
|
||||||
|
problems. Since `mgmt` runs as a single statically compiled binary, all of the
|
||||||
|
library dependencies are included. It is expected, that certain advanced
|
||||||
|
resources require host specific facilities to work. These requirements are
|
||||||
|
listed below:
|
||||||
|
|
||||||
|
| Resource | Dependency | Version |
|
||||||
|
|----------|-------------------|---------|
|
||||||
|
| file | inotify | ? |
|
||||||
|
| hostname | systemd-hostnamed | ? |
|
||||||
|
| nspawn | systemd-nspawn | ? |
|
||||||
|
| pkg | packagekitd | ? |
|
||||||
|
| svc | systemd | ? |
|
||||||
|
| virt | libvirtd | ? |
|
||||||
|
|
||||||
|
For building a visual representation of the graph, `graphviz` is required.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
* golint `github.com/golang/lint/golint`
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
* Make sure you have golang version 1.6 or greater installed.
|
||||||
|
* If you do not have a GOPATH yet, create one and export it:
|
||||||
|
```
|
||||||
|
mkdir $HOME/gopath
|
||||||
|
export GOPATH=$HOME/gopath
|
||||||
|
```
|
||||||
|
* You might also want to add the GOPATH to your `~/.bashrc` or `~/.profile`.
|
||||||
|
* For more information you can read the [GOPATH documentation](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable).
|
||||||
|
* Next download the mgmt code base, and switch to that directory:
|
||||||
|
```
|
||||||
|
mkdir -p $GOPATH/src/github.com/purpleidea/
|
||||||
|
cd $GOPATH/src/github.com/purpleidea/
|
||||||
|
git clone --recursive https://github.com/purpleidea/mgmt/
|
||||||
|
cd $GOPATH/src/github.com/purpleidea/mgmt
|
||||||
|
```
|
||||||
|
* Run `make deps` to install system and golang dependencies. Take a look at `misc/make-deps.sh` for details.
|
||||||
|
* Run `make build` to get a freshly built `mgmt` binary.
|
||||||
|
* Run `time ./mgmt run --yaml examples/graph0.yaml --converged-timeout=5 --tmp-prefix` to try out a very simple example!
|
||||||
|
* To run continuously in the default mode of operation, omit the `--converged-timeout` option.
|
||||||
|
* Have fun hacking on our future technology!
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
Please look in the [examples/](../examples/) folder for some examples!
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Installation of `mgmt` from distribution packages currently needs improvement.
|
||||||
|
At the moment we have:
|
||||||
|
* [COPR](https://copr.fedoraproject.org/coprs/purpleidea/mgmt/)
|
||||||
|
* [Arch](https://aur.archlinux.org/packages/mgmt/)
|
||||||
|
|
||||||
|
Please contribute more! We'd especially like to see a Debian package!
|
||||||
575
docs/resource-guide.md
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
# Resource guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `mgmt` tool has built-in resource primitives which make up the building
|
||||||
|
blocks of any configuration. Each instance of a resource is mapped to a single
|
||||||
|
vertex in the resource [graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph).
|
||||||
|
This guide is meant to instruct developers on how to write a brand new resource.
|
||||||
|
Since `mgmt` and the core resources are written in golang, some prior golang
|
||||||
|
knowledge is assumed.
|
||||||
|
|
||||||
|
## Theory
|
||||||
|
|
||||||
|
Resources in `mgmt` are similar to resources in other systems in that they are
|
||||||
|
[idempotent](https://en.wikipedia.org/wiki/Idempotence). Our resources are
|
||||||
|
uniquely different in that they can detect when their state has changed, and as
|
||||||
|
a result can run to revert or repair this change instantly. For some background
|
||||||
|
on this design, please read the
|
||||||
|
[original article](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/)
|
||||||
|
on the subject.
|
||||||
|
|
||||||
|
## Resource API
|
||||||
|
|
||||||
|
To implement a resource in `mgmt` it must satisfy the
|
||||||
|
[`Res`](https://github.com/purpleidea/mgmt/blob/master/resources/resources.go)
|
||||||
|
interface. What follows are each of the method signatures and a description of
|
||||||
|
each.
|
||||||
|
|
||||||
|
### Default
|
||||||
|
```golang
|
||||||
|
Default() Res
|
||||||
|
```
|
||||||
|
|
||||||
|
This returns a populated resource struct as a `Res`. It shouldn't populate any
|
||||||
|
values which already have the correct default as the golang zero value. In
|
||||||
|
general it is preferable if the zero values make for the correct defaults.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// Default returns some sensible defaults for this resource.
|
||||||
|
func (obj *FooRes) Default() Res {
|
||||||
|
return &FooRes{
|
||||||
|
Answer: 42, // sometimes, defaults shouldn't be the zero value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validate
|
||||||
|
```golang
|
||||||
|
Validate() error
|
||||||
|
```
|
||||||
|
|
||||||
|
This method is used to validate if the populated resource struct is a valid
|
||||||
|
representation of the resource kind. If it does not conform to the resource
|
||||||
|
specifications, it should generate an error. If you notice that this method is
|
||||||
|
quite large, it might be an indication that you should reconsider the parameter
|
||||||
|
list and interface to this resource. This method is called _before_ `Init`.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// Validate reports any problems with the struct definition.
|
||||||
|
func (obj *FooRes) Validate() error {
|
||||||
|
if obj.Answer != 42 { // validate whatever you want
|
||||||
|
return fmt.Errorf("expected an answer of 42")
|
||||||
|
}
|
||||||
|
return obj.BaseRes.Validate() // remember to call the base method!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Init
|
||||||
|
```golang
|
||||||
|
Init() error
|
||||||
|
```
|
||||||
|
|
||||||
|
This is called to initialize the resource. If something goes wrong, it should
|
||||||
|
return an error. It should set the resource `kind`, do any resource specific
|
||||||
|
work, and finish by calling the `Init` method of the base resource.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// Init initializes the Foo resource.
|
||||||
|
func (obj *FooRes) Init() error {
|
||||||
|
obj.BaseRes.kind = "foo" // must lower case resource kind
|
||||||
|
// run the resource specific initialization, and error if anything fails
|
||||||
|
if some_error {
|
||||||
|
return err // something went wrong!
|
||||||
|
}
|
||||||
|
return obj.BaseRes.Init() // call the base resource init
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This method is always called after `Validate` has run successfully, with the
|
||||||
|
exception that we can't prevent a malicious or buggy `libmgmt` user to not run
|
||||||
|
this. In other words, you should expect `Validate` to have run first, but you
|
||||||
|
shouldn't allow `Init` to dangerously `rm -rf /$the_world` if your code only
|
||||||
|
checks `$the_world` in `Validate`. Remember to always program safely!
|
||||||
|
|
||||||
|
### Close
|
||||||
|
```golang
|
||||||
|
Close() error
|
||||||
|
```
|
||||||
|
|
||||||
|
This is called to cleanup after the resource. It is usually not necessary, but
|
||||||
|
can be useful if you'd like to properly close a persistent connection that you
|
||||||
|
opened in the `Init` method and were using throughout the resource.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// Close runs some cleanup code for this resource.
|
||||||
|
func (obj *FooRes) Close() error {
|
||||||
|
err := obj.conn.Close() // close some internal connection
|
||||||
|
|
||||||
|
// call base close, b/c we're overriding
|
||||||
|
if e := obj.BaseRes.Close(); err == nil {
|
||||||
|
err = e
|
||||||
|
} else if e != nil {
|
||||||
|
err = multierr.Append(err, e) // list of errors
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should probably check the return errors of your internal methods, and pass
|
||||||
|
on an error if something went wrong. Remember to always call the base `Close`
|
||||||
|
method! If you plan to return early if you hit an internal error, then at least
|
||||||
|
call it with a defer!
|
||||||
|
|
||||||
|
### CheckApply
|
||||||
|
```golang
|
||||||
|
CheckApply(apply bool) (checkOK bool, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
`CheckApply` is where the real _work_ is done. Under normal circumstances, this
|
||||||
|
function should check if the state of this resource is correct, and if so, it
|
||||||
|
should return: `(true, nil)`. If the `apply` variable is set to `true`, then
|
||||||
|
this means that we should then proceed to run the changes required to bring the
|
||||||
|
resource into the correct state. If the `apply` variable is set to `false`, then
|
||||||
|
the resource is operating in _noop_ mode and _no operations_ should be executed!
|
||||||
|
|
||||||
|
After having executed the necessary operations to bring the resource back into
|
||||||
|
the desired state, or after having detected that the state was incorrect, but
|
||||||
|
that changes can't be made because `apply` is `false`, you should then return
|
||||||
|
`(false, nil)`.
|
||||||
|
|
||||||
|
You must cause the resource to converge during a single execution of this
|
||||||
|
function. If you cannot, then you must return an error! The exception to this
|
||||||
|
rule is that if an external force changes the state of the resource while it is
|
||||||
|
being remedied, it is possible to return from this function even though the
|
||||||
|
resource isn't now converged. This is not a bug, as the resources `Watch`
|
||||||
|
facility will detect the change, ultimately resulting in a subsequent call to
|
||||||
|
`CheckApply`.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// CheckApply does the idempotent work of checking and applying resource state.
|
||||||
|
func (obj *FooRes) CheckApply(apply bool) (bool, error) {
|
||||||
|
// check the state
|
||||||
|
if state_is_okay { return true, nil } // done early! :)
|
||||||
|
// state was bad
|
||||||
|
if !apply { return false, nil } // don't apply; !stateok, nil
|
||||||
|
// do the apply!
|
||||||
|
return false, nil // after success applying
|
||||||
|
if any_error { return false, err } // anytime there's an err!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `CheckApply` function is called by the `mgmt` engine when it believes a call
|
||||||
|
is necessary. Under certain conditions when a `Watch` call does not invalidate
|
||||||
|
the state of the resource, and no refresh call was sent, its execution might be
|
||||||
|
skipped. This is an engine optimization, and not a bug. It is mentioned here in
|
||||||
|
the documentation in case you are confused as to why a debug message you've
|
||||||
|
added to the code isn't always printed.
|
||||||
|
|
||||||
|
#### Refresh notifications
|
||||||
|
Some resources may choose to support receiving refresh notifications. In general
|
||||||
|
these should be avoided if possible, but nevertheless, they do make sense in
|
||||||
|
certain situations. Resources that support these need to verify if one was sent
|
||||||
|
during the CheckApply phase of execution. This is accomplished by calling the
|
||||||
|
`Refresh() bool` method of the resource, and inspecting the return value. This
|
||||||
|
is only necessary if you plan to perform a refresh action. Refresh actions
|
||||||
|
should still respect the `apply` variable, and no system changes should be made
|
||||||
|
if it is `false`. Refresh notifications are generated by any resource when an
|
||||||
|
action is applied by that resource and are transmitted through graph edges which
|
||||||
|
have enabled their propagation. Resources that currently perform some refresh
|
||||||
|
action include `svc`, `timer`, and `password`.
|
||||||
|
|
||||||
|
#### Paired execution
|
||||||
|
For many resources it is not uncommon to see `CheckApply` run twice in rapid
|
||||||
|
succession. This is usually not a pathological occurrence, but rather a healthy
|
||||||
|
pattern which is a consequence of the event system. When the state of the
|
||||||
|
resource is incorrect, `CheckApply` will run to remedy the state. In response to
|
||||||
|
having just changed the state, it is usually the case that this repair will
|
||||||
|
trigger the `Watch` code! In response, a second `CheckApply` is triggered, which
|
||||||
|
will likely find the state to now be correct.
|
||||||
|
|
||||||
|
#### Summary
|
||||||
|
* Anytime an error occurs during `CheckApply`, you should return `(false, err)`.
|
||||||
|
* If the state is correct and no changes are needed, return `(true, nil)`.
|
||||||
|
* You should only make changes to the system if `apply` is set to `true`.
|
||||||
|
* After checking the state and possibly applying the fix, return `(false, nil)`.
|
||||||
|
* Returning `(true, err)` is a programming error and will cause a `Fatal`.
|
||||||
|
|
||||||
|
### Watch
|
||||||
|
```golang
|
||||||
|
Watch(chan *Event) error
|
||||||
|
```
|
||||||
|
|
||||||
|
`Watch` is a main loop that runs and sends messages when it detects that the
|
||||||
|
state of the resource might have changed. To send a message you should write to
|
||||||
|
the input event channel using the `Event` helper method. The Watch function
|
||||||
|
should run continuously until a shutdown message is received. If at any time
|
||||||
|
something goes wrong, you should return an error, and the `mgmt` engine will
|
||||||
|
handle possibly restarting the main loop based on the `retry` meta parameters.
|
||||||
|
|
||||||
|
It is better to send an event notification which turns out to be spurious, than
|
||||||
|
to miss a possible event. Resources which can miss events are incorrect and need
|
||||||
|
to be re-engineered so that this isn't the case. If you have an idea for a
|
||||||
|
resource which would fit this criteria, but you can't find a solution, please
|
||||||
|
contact the `mgmt` maintainers so that this problem can be investigated and a
|
||||||
|
possible system level engineering fix can be found.
|
||||||
|
|
||||||
|
You may have trouble deciding how much resource state checking should happen in
|
||||||
|
the `Watch` loop versus deferring it all to the `CheckApply` method. You may
|
||||||
|
want to put some simple fast path checking in `Watch` to avoid generating
|
||||||
|
obviously spurious events, but in general it's best to keep the `Watch` method
|
||||||
|
as simple as possible. Contact the `mgmt` maintainers if you're not sure.
|
||||||
|
|
||||||
|
If the resource is activated in `polling` mode, the `Watch` method will not get
|
||||||
|
executed. As a result, the resource must still work even if the main loop is not
|
||||||
|
running.
|
||||||
|
|
||||||
|
#### Select
|
||||||
|
The lifetime of most resources `Watch` method should be spent in an infinite
|
||||||
|
loop that is bounded by a `select` call. The `select` call is the point where
|
||||||
|
our method hands back control to the engine (and the kernel) so that we can
|
||||||
|
sleep until something of interest wakes us up. In this loop we must process
|
||||||
|
events from the engine via the `<-obj.Events()` call, and receive events for our
|
||||||
|
resource itself!
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
If we receive an internal event from the `<-obj.Events()` method, we can read it
|
||||||
|
with the ReadEvent helper function. This function tells us if we should shutdown
|
||||||
|
our resource, and if we should generate an event. When we want to send an event,
|
||||||
|
we use the `Event` helper function. It is also important to mark the resource
|
||||||
|
state as `dirty` if we believe it might have changed. We do this with the
|
||||||
|
`StateOK(false)` function.
|
||||||
|
|
||||||
|
#### Startup
|
||||||
|
Once the `Watch` function has finished starting up successfully, it is important
|
||||||
|
to generate one event to notify the `mgmt` engine that we're now listening
|
||||||
|
successfully, so that it can run an initial `CheckApply` to ensure we're safely
|
||||||
|
tracking a healthy state and that we didn't miss anything when `Watch` was down
|
||||||
|
or from before `mgmt` was running. It does this by calling the `Running` method.
|
||||||
|
|
||||||
|
#### Converged
|
||||||
|
The engine might be asked to shutdown when the entire state of the system has
|
||||||
|
not seen any changes for some duration of time. The engine can determine this
|
||||||
|
automatically, but each resource can block this if it is absolutely necessary.
|
||||||
|
To do this, the `Watch` method should get the `ConvergedUID` handle that has
|
||||||
|
been prepared for it by the engine. This is done by calling the `ConvergerUID`
|
||||||
|
method on the resource object. The result can be used to set the converged
|
||||||
|
status with `SetConverged`, and to notify when the particular timeout has been
|
||||||
|
reached by waiting on `ConvergedTimer`.
|
||||||
|
|
||||||
|
Instead of interacting with the `ConvergedUID` with these two methods, we can
|
||||||
|
instead use the `StartTimer` and `ResetTimer` methods which accomplish the same
|
||||||
|
thing, but provide a `select`-free interface for different coding situations.
|
||||||
|
|
||||||
|
This particular facility is most likely not required for most resources. It may
|
||||||
|
prove to be useful if a resource wants to start off a long operation, but avoid
|
||||||
|
sending out erroneous `Event` messages to keep things alive until it finishes.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// Watch is the listener and main loop for this resource.
|
||||||
|
func (obj *FooRes) Watch() error {
|
||||||
|
// setup the Foo resource
|
||||||
|
var err error
|
||||||
|
if err, obj.foo = OpenFoo(); err != nil {
|
||||||
|
return err // we couldn't startup
|
||||||
|
}
|
||||||
|
defer obj.whatever.CloseFoo() // shutdown our
|
||||||
|
|
||||||
|
// notify engine that we're running
|
||||||
|
if err := obj.Running(); err != nil {
|
||||||
|
return err // bubble up a NACK...
|
||||||
|
}
|
||||||
|
|
||||||
|
var send = false // send event?
|
||||||
|
var exit *error
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-obj.Events():
|
||||||
|
// we avoid sending events on unpause
|
||||||
|
if exit, send = obj.ReadEvent(event); exit != nil {
|
||||||
|
return *exit // exit
|
||||||
|
}
|
||||||
|
|
||||||
|
// the actual events!
|
||||||
|
case event := <-obj.foo.Events:
|
||||||
|
if is_an_event {
|
||||||
|
send = true // used below
|
||||||
|
obj.StateOK(false) // dirty
|
||||||
|
}
|
||||||
|
|
||||||
|
// event errors
|
||||||
|
case err := <-obj.foo.Errors:
|
||||||
|
return err // will cause a retry or permanent failure
|
||||||
|
}
|
||||||
|
|
||||||
|
// do all our event sending all together to avoid duplicate msgs
|
||||||
|
if send {
|
||||||
|
send = false
|
||||||
|
obj.Event() // send the event!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Summary
|
||||||
|
* Remember to call the appropriate `converger` methods throughout the resource.
|
||||||
|
* Remember to call `Startup` when the `Watch` is running successfully.
|
||||||
|
* Remember to process internal events and shutdown promptly if asked to.
|
||||||
|
* Ensure the design of your resource is well thought out.
|
||||||
|
* Have a look at the existing resources for a rough idea of how this all works.
|
||||||
|
|
||||||
|
### Compare
|
||||||
|
```golang
|
||||||
|
Compare(Res) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
Each resource must have a `Compare` method. This takes as input another resource
|
||||||
|
and must return whether they are identical or not. This is used for identifying
|
||||||
|
if an existing resource can be used in place of a new one with a similar set of
|
||||||
|
parameters. In particular, when switching from one graph to a new (possibly
|
||||||
|
identical) graph, this avoids recomputing the state for resources which don't
|
||||||
|
change or that are sufficiently similar that they don't need to be swapped out.
|
||||||
|
|
||||||
|
In general if all the resource properties are identical, then they usually don't
|
||||||
|
need to be changed. On occasion, not all of them need to be compared, in
|
||||||
|
particular if they store some generated state, or if they aren't significant in
|
||||||
|
some way.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// Compare two resources and return if they are equivalent.
|
||||||
|
func (obj *FooRes) Compare(res Res) bool {
|
||||||
|
switch res.(type) {
|
||||||
|
case *FooRes: // only compare to other resources of the Foo kind!
|
||||||
|
res := res.(*FileRes)
|
||||||
|
if !obj.BaseRes.Compare(res) { // call base Compare
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if obj.Name != res.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if obj.whatever != res.whatever {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if obj.Flag != res.Flag {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false // different kind of resource
|
||||||
|
}
|
||||||
|
return true // they must match!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### UIDs
|
||||||
|
```golang
|
||||||
|
UIDs() []ResUID
|
||||||
|
```
|
||||||
|
|
||||||
|
The `UIDs` method returns a list of `ResUID` interfaces that represent the
|
||||||
|
particular resource uniquely. This is used with the AutoEdges API to determine
|
||||||
|
if another resource can match a dependency to this one.
|
||||||
|
|
||||||
|
### AutoEdges
|
||||||
|
```golang
|
||||||
|
AutoEdges() AutoEdge
|
||||||
|
```
|
||||||
|
|
||||||
|
This returns a struct that implements the `AutoEdge` interface. This struct
|
||||||
|
is used to match other resources that might be relevant dependencies for this
|
||||||
|
resource.
|
||||||
|
|
||||||
|
### CollectPattern
|
||||||
|
```golang
|
||||||
|
CollectPattern() string
|
||||||
|
```
|
||||||
|
|
||||||
|
This is currently a stub and will be updated once the DSL is further along.
|
||||||
|
|
||||||
|
### UnmarshalYAML
|
||||||
|
```golang
|
||||||
|
UnmarshalYAML(unmarshal func(interface{}) error) error // optional
|
||||||
|
```
|
||||||
|
|
||||||
|
This is optional, but recommended for any resource that will have a YAML
|
||||||
|
accessible struct, and an entry in the `GraphConfig` struct. It is not required
|
||||||
|
because to do so would mean that third-party or custom resources (such as those
|
||||||
|
someone writes to use with `libmgmt`) would have to implement this needlessly.
|
||||||
|
|
||||||
|
The signature intentionally matches what is required to satisfy the `go-yaml`
|
||||||
|
[Unmarshaler](https://godoc.org/gopkg.in/yaml.v2#Unmarshaler) interface.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||||
|
// It is primarily useful for setting the defaults.
|
||||||
|
func (obj *FooRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
type rawRes FooRes // indirection to avoid infinite recursion
|
||||||
|
|
||||||
|
def := obj.Default() // get the default
|
||||||
|
res, ok := def.(*FooRes) // put in the right format
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("could not convert to FooRes")
|
||||||
|
}
|
||||||
|
raw := rawRes(*res) // convert; the defaults go here
|
||||||
|
|
||||||
|
if err := unmarshal(&raw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*obj = FooRes(raw) // restore from indirection with type conversion!
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Further considerations
|
||||||
|
There is some additional information that any resource writer will need to know.
|
||||||
|
Each issue is listed separately below!
|
||||||
|
|
||||||
|
### Resource struct
|
||||||
|
Each resource will implement methods as pointer receivers on a resource struct.
|
||||||
|
The resource struct must include an anonymous reference to the `BaseRes` struct.
|
||||||
|
The naming convention for resources is that they end with a `Res` suffix. If
|
||||||
|
you'd like your resource to be accessible by the `YAML` graph API (GAPI), then
|
||||||
|
you'll need to include the appropriate YAML fields as shown below.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```golang
|
||||||
|
type FooRes struct {
|
||||||
|
BaseRes `yaml:",inline"` // base properties
|
||||||
|
|
||||||
|
Whatever string `yaml:"whatever"` // you pick!
|
||||||
|
Bar int // no yaml, used as public output value for send/recv
|
||||||
|
Baz bool `yaml:"baz"` // something else
|
||||||
|
|
||||||
|
something string // some private field
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### YAML
|
||||||
|
In addition to labelling your resource struct with YAML fields, you must also
|
||||||
|
add an entry to the internal `GraphConfig` struct. It is a fairly straight
|
||||||
|
forward one line patch.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
type GraphConfig struct {
|
||||||
|
// [snip...]
|
||||||
|
Resources struct {
|
||||||
|
Noop []*resources.NoopRes `yaml:"noop"`
|
||||||
|
File []*resources.FileRes `yaml:"file"`
|
||||||
|
// [snip...]
|
||||||
|
Foo []*resources.FooRes `yaml:"foo"` // tada :)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It's also recommended that you add the [UnmarshalYAML](#unmarshalyaml) method to
|
||||||
|
your resources so that unspecified values are given sane defaults.
|
||||||
|
|
||||||
|
### Gob registration
|
||||||
|
All resources must be registered with the `golang` _gob_ module so that they can
|
||||||
|
be encoded and decoded. Make sure to include the following code snippet for this
|
||||||
|
to work.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
import "encoding/gob"
|
||||||
|
func init() { // special golang method that runs once
|
||||||
|
gob.Register(&FooRes{}) // substitude your resource here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Automatic edges
|
||||||
|
Automatic edges in `mgmt` are well described in [this article](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/).
|
||||||
|
The best example of this technique can be seen in the `svc` resource.
|
||||||
|
Unfortunately no further documentation about this subject has been written. To
|
||||||
|
expand this section, please send a patch! Please contact us if you'd like to
|
||||||
|
work on a resource that uses this feature, or to add it to an existing one!
|
||||||
|
|
||||||
|
## Automatic grouping
|
||||||
|
Automatic grouping in `mgmt` is well described in [this article](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/).
|
||||||
|
The best example of this technique can be seen in the `pkg` resource.
|
||||||
|
Unfortunately no further documentation about this subject has been written. To
|
||||||
|
expand this section, please send a patch! Please contact us if you'd like to
|
||||||
|
work on a resource that uses this feature, or to add it to an existing one!
|
||||||
|
|
||||||
|
|
||||||
|
## Send/Recv
|
||||||
|
In `mgmt` there is a novel concept called _Send/Recv_. For some background,
|
||||||
|
please [read the introductory article](https://ttboj.wordpress.com/2016/12/07/sendrecv-in-mgmt/).
|
||||||
|
When using this feature, the engine will automatically send the user specified
|
||||||
|
value to the intended destination without requiring any resource specific code.
|
||||||
|
Any time that one of the destination values is changed, the engine automatically
|
||||||
|
marks the resource state as `dirty`. To detect if a particular value was
|
||||||
|
received, and if it changed (during this invocation of CheckApply) from the
|
||||||
|
previous value, you can query the Recv parameter. It will contain a `map` of all
|
||||||
|
the keys which can be received on, and the value has a `Changed` property which
|
||||||
|
will indicate whether the value was updated on this particular `CheckApply`
|
||||||
|
invocation. The type of the sending key must match that of the receiving one.
|
||||||
|
This can _only_ be done inside of the `CheckApply` function!
|
||||||
|
|
||||||
|
```golang
|
||||||
|
// inside CheckApply, probably near the top
|
||||||
|
if val, exists := obj.Recv["SomeKey"]; exists {
|
||||||
|
log.Printf("SomeKey was sent to us from: %s[%s].%s", val.Res.Kind(), val.Res.GetName(), val.Key)
|
||||||
|
if val.Changed {
|
||||||
|
log.Printf("SomeKey was just updated!")
|
||||||
|
// you may want to invalidate some local cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Astute readers will note that there isn't anything that prevents a user from
|
||||||
|
sending an identically typed value to some arbitrary (public) key that the
|
||||||
|
resource author hadn't considered! While this is true, resources should probably
|
||||||
|
work within this problem space anyways. The rule of thumb is that any public
|
||||||
|
parameter which is normally used in a resource can be used safely.
|
||||||
|
|
||||||
|
One subtle scenario is that if a resource creates a local cache or stores a
|
||||||
|
computation that depends on the value of a public parameter and will require
|
||||||
|
invalidation should that public parameter change, then you must detect that
|
||||||
|
scenario and invalidate the cache when it occurs. This *must* be processed
|
||||||
|
before there is a possibility of failure in CheckApply, because if we fail (and
|
||||||
|
possibly run again) the subsequent send->recv transfer might not have a new
|
||||||
|
value to copy, and therefore we won't see this notification of change.
|
||||||
|
Therefore, it is important to process these promptly, if they must not be lost,
|
||||||
|
such as for cache invalidation.
|
||||||
|
|
||||||
|
Remember, `Send/Recv` only changes your resource code if you cache state.
|
||||||
|
|
||||||
|
## Composite resources
|
||||||
|
Composite resources are resources which embed one or more existing resources.
|
||||||
|
This is useful to prevent code duplication in higher level resource scenarios.
|
||||||
|
The best example of this technique can be seen in the `nspawn` resource which
|
||||||
|
can be seen to partially embed a `svc` resource, but without its `Watch`.
|
||||||
|
Unfortunately no further documentation about this subject has been written. To
|
||||||
|
expand this section, please send a patch! Please contact us if you'd like to
|
||||||
|
work on a resource that uses this feature, or to add it to an existing one!
|
||||||
|
|
||||||
|
## Frequently asked questions
|
||||||
|
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
||||||
|
respond by commit with the answer.)
|
||||||
|
|
||||||
|
### Can I write resources in a different language?
|
||||||
|
Currently `golang` is the only supported language for built-in resources. We
|
||||||
|
might consider allowing external resources to be imported in the future. This
|
||||||
|
will likely require a language that can expose a C-like API, such as `python` or
|
||||||
|
`ruby`. Custom `golang` resources are already possible when using mgmt as a lib.
|
||||||
|
Higher level resource collections will be possible once the `mgmt` DSL is ready.
|
||||||
|
|
||||||
|
### What new resource primitives need writing?
|
||||||
|
There are still many ideas for new resources that haven't been written yet. If
|
||||||
|
you'd like to contribute one, please contact us and tell us about your idea!
|
||||||
|
|
||||||
|
### Where can I find more information about mgmt?
|
||||||
|
Additional blog posts, videos and other material [is available!](https://github.com/purpleidea/mgmt/#on-the-web).
|
||||||
|
|
||||||
|
## Suggestions
|
||||||
|
If you have any ideas for API changes or other improvements to resource writing,
|
||||||
|
please let us know! We're still pre 1.0 and pre 0.1 and happy to break API in
|
||||||
|
order to get it right!
|
||||||
300
etcd.go
@@ -1,300 +0,0 @@
|
|||||||
// Mgmt
|
|
||||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
|
||||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
etcd_context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
|
||||||
etcd "github.com/coreos/etcd/client"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate stringer -type=etcdMsg -output=etcdmsg_stringer.go
|
|
||||||
type etcdMsg int
|
|
||||||
|
|
||||||
const (
|
|
||||||
etcdStart etcdMsg = iota
|
|
||||||
etcdEvent
|
|
||||||
etcdFoo
|
|
||||||
etcdBar
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate stringer -type=etcdConvergedState -output=etcdconvergedstate_stringer.go
|
|
||||||
type etcdConvergedState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
etcdConvergedNil etcdConvergedState = iota
|
|
||||||
//etcdConverged
|
|
||||||
etcdConvergedTimeout
|
|
||||||
)
|
|
||||||
|
|
||||||
type EtcdWObject struct { // etcd wrapper object
|
|
||||||
seed string
|
|
||||||
ctimeout int
|
|
||||||
converged chan bool
|
|
||||||
kapi etcd.KeysAPI
|
|
||||||
convergedState etcdConvergedState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etcdO *EtcdWObject) GetConvergedState() etcdConvergedState {
|
|
||||||
return etcdO.convergedState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etcdO *EtcdWObject) SetConvergedState(state etcdConvergedState) {
|
|
||||||
etcdO.convergedState = state
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etcdO *EtcdWObject) GetKAPI() etcd.KeysAPI {
|
|
||||||
if etcdO.kapi != nil { // memoize
|
|
||||||
return etcdO.kapi
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := etcd.Config{
|
|
||||||
Endpoints: []string{etcdO.seed},
|
|
||||||
Transport: etcd.DefaultTransport,
|
|
||||||
// set timeout per request to fail fast when the target endpoint is unavailable
|
|
||||||
HeaderTimeoutPerRequest: time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
var c etcd.Client
|
|
||||||
var err error
|
|
||||||
|
|
||||||
c, err = etcd.New(cfg)
|
|
||||||
if err != nil {
|
|
||||||
// XXX: not sure if this ever errors
|
|
||||||
if cerr, ok := err.(*etcd.ClusterError); ok {
|
|
||||||
// XXX: not sure if this part ever matches
|
|
||||||
// not running or disconnected
|
|
||||||
if cerr == etcd.ErrClusterUnavailable {
|
|
||||||
log.Fatal("XXX: etcd: ErrClusterUnavailable")
|
|
||||||
} else {
|
|
||||||
log.Fatal("XXX: etcd: Unknown")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Fatal(err) // some unhandled error
|
|
||||||
}
|
|
||||||
etcdO.kapi = etcd.NewKeysAPI(c)
|
|
||||||
return etcdO.kapi
|
|
||||||
}
|
|
||||||
|
|
||||||
type EtcdChannelWatchResponse struct {
|
|
||||||
resp *etcd.Response
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap the etcd watcher.Next blocking function inside of a channel
|
|
||||||
func (etcdO *EtcdWObject) EtcdChannelWatch(watcher etcd.Watcher, context etcd_context.Context) chan *EtcdChannelWatchResponse {
|
|
||||||
ch := make(chan *EtcdChannelWatchResponse)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
resp, err := watcher.Next(context) // blocks here
|
|
||||||
ch <- &EtcdChannelWatchResponse{resp, err}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etcdO *EtcdWObject) EtcdWatch() chan etcdMsg {
|
|
||||||
kapi := etcdO.GetKAPI()
|
|
||||||
ctimeout := etcdO.ctimeout
|
|
||||||
converged := etcdO.converged
|
|
||||||
// XXX: i think we need this buffered so that when we're hanging on the
|
|
||||||
// channel, which is inside the EtcdWatch main loop, we still want the
|
|
||||||
// calls to Get/Set on etcd to succeed, so blocking them here would
|
|
||||||
// kill the whole thing
|
|
||||||
ch := make(chan etcdMsg, 1) // XXX: buffer of at least 1 is required
|
|
||||||
go func(ch chan etcdMsg) {
|
|
||||||
tmin := 500 // initial (min) delay in ms
|
|
||||||
t := tmin // current time
|
|
||||||
tmult := 2 // multiplier for exponential delay
|
|
||||||
tmax := 16000 // max delay
|
|
||||||
watcher := kapi.Watcher("/exported/", &etcd.WatcherOptions{Recursive: true})
|
|
||||||
etcdch := etcdO.EtcdChannelWatch(watcher, etcd_context.Background())
|
|
||||||
for {
|
|
||||||
log.Printf("Etcd: Watching...")
|
|
||||||
var resp *etcd.Response // = nil by default
|
|
||||||
var err error
|
|
||||||
select {
|
|
||||||
case out := <-etcdch:
|
|
||||||
etcdO.SetConvergedState(etcdConvergedNil)
|
|
||||||
resp, err = out.resp, out.err
|
|
||||||
|
|
||||||
case _ = <-TimeAfterOrBlock(ctimeout):
|
|
||||||
etcdO.SetConvergedState(etcdConvergedTimeout)
|
|
||||||
converged <- true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if err == etcd_context.Canceled {
|
|
||||||
// ctx is canceled by another routine
|
|
||||||
log.Fatal("Canceled")
|
|
||||||
} else if err == etcd_context.DeadlineExceeded {
|
|
||||||
// ctx is attached with a deadline and it exceeded
|
|
||||||
log.Fatal("Deadline")
|
|
||||||
} else if cerr, ok := err.(*etcd.ClusterError); ok {
|
|
||||||
// not running or disconnected
|
|
||||||
// TODO: is there a better way to parse errors?
|
|
||||||
for _, e := range cerr.Errors {
|
|
||||||
if strings.HasSuffix(e.Error(), "getsockopt: connection refused") {
|
|
||||||
t = int(math.Min(float64(t*tmult), float64(tmax)))
|
|
||||||
log.Printf("Etcd: Waiting %d ms for connection...", t)
|
|
||||||
time.Sleep(time.Duration(t) * time.Millisecond) // sleep for t ms
|
|
||||||
|
|
||||||
} else if e.Error() == "unexpected EOF" {
|
|
||||||
log.Printf("Etcd: Unexpected disconnect...")
|
|
||||||
|
|
||||||
} else if e.Error() == "EOF" {
|
|
||||||
log.Printf("Etcd: Disconnected...")
|
|
||||||
|
|
||||||
} else if strings.HasPrefix(e.Error(), "unsupported protocol scheme") {
|
|
||||||
// usually a bad peer endpoint value
|
|
||||||
log.Fatal("Bad peer endpoint value?")
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log.Fatal("Woops: ", e.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// bad cluster endpoints, which are not etcd servers
|
|
||||||
log.Fatal("Woops: ", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//log.Print(resp)
|
|
||||||
//log.Printf("Watcher().Node.Value(%v): %+v", key, resp.Node.Value)
|
|
||||||
// FIXME: we should actually reset when the server comes back, not here on msg!
|
|
||||||
//XXX: can we fix this with one of these patterns?: https://blog.golang.org/go-concurrency-patterns-timing-out-and
|
|
||||||
t = tmin // reset timer
|
|
||||||
|
|
||||||
// don't trigger event if nothing changed
|
|
||||||
if n, p := resp.Node, resp.PrevNode; resp.Action == "set" && p != nil {
|
|
||||||
if n.Key == p.Key && n.Value == p.Value {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: we get events on key/type/value changes for
|
|
||||||
// each type directory... ignore the non final ones...
|
|
||||||
// IOW, ignore everything except for the value or some
|
|
||||||
// field which gets set last... this could be the max count field thing...
|
|
||||||
|
|
||||||
log.Printf("Etcd: Value: %v", resp.Node.Value) // event
|
|
||||||
ch <- etcdEvent // event
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end for loop
|
|
||||||
//close(ch)
|
|
||||||
}(ch) // call go routine
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper function to store our data in etcd
|
|
||||||
func (etcdO *EtcdWObject) EtcdPut(hostname, key, typ string, obj interface{}) bool {
|
|
||||||
kapi := etcdO.GetKAPI()
|
|
||||||
output, ok := ObjToB64(obj)
|
|
||||||
if !ok {
|
|
||||||
log.Printf("Etcd: Could not encode %v key.", key)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("/exported/%s/types/%s/type", hostname, key)
|
|
||||||
_, err := kapi.Set(etcd_context.Background(), path, typ, nil)
|
|
||||||
// XXX validate...
|
|
||||||
|
|
||||||
path = fmt.Sprintf("/exported/%s/types/%s/value", hostname, key)
|
|
||||||
resp, err := kapi.Set(etcd_context.Background(), path, output, nil)
|
|
||||||
if err != nil {
|
|
||||||
if cerr, ok := err.(*etcd.ClusterError); ok {
|
|
||||||
// not running or disconnected
|
|
||||||
for _, e := range cerr.Errors {
|
|
||||||
if strings.HasSuffix(e.Error(), "getsockopt: connection refused") {
|
|
||||||
}
|
|
||||||
//if e == etcd.ErrClusterUnavailable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Printf("Etcd: Could not store %v key.", key)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
log.Print("Etcd: ", resp) // w00t... bonus
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup /exported/ node hierarchy
|
|
||||||
func (etcdO *EtcdWObject) EtcdGet() (etcd.Nodes, bool) {
|
|
||||||
kapi := etcdO.GetKAPI()
|
|
||||||
// key structure is /exported/<hostname>/types/...
|
|
||||||
resp, err := kapi.Get(etcd_context.Background(), "/exported/", &etcd.GetOptions{Recursive: true})
|
|
||||||
if err != nil {
|
|
||||||
return nil, false // not found
|
|
||||||
}
|
|
||||||
return resp.Node.Nodes, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (etcdO *EtcdWObject) EtcdGetProcess(nodes etcd.Nodes, typ string) []string {
|
|
||||||
//path := fmt.Sprintf("/exported/%s/types/", h)
|
|
||||||
top := "/exported/"
|
|
||||||
log.Printf("Etcd: Get: %+v", nodes) // Get().Nodes.Nodes
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
for _, x := range nodes { // loop through hosts
|
|
||||||
if !strings.HasPrefix(x.Key, top) {
|
|
||||||
log.Fatal("Error!")
|
|
||||||
}
|
|
||||||
host := x.Key[len(top):]
|
|
||||||
//log.Printf("Get().Nodes[%v]: %+v ==> %+v", -1, host, x.Nodes)
|
|
||||||
//log.Printf("Get().Nodes[%v]: %+v ==> %+v", i, x.Key, x.Nodes)
|
|
||||||
types, ok := EtcdGetChildNodeByKey(x, "types")
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, y := range types.Nodes { // loop through types
|
|
||||||
//key := y.Key # UUID?
|
|
||||||
//log.Printf("Get(%v): TYPE[%v]", host, y.Key)
|
|
||||||
t, ok := EtcdGetChildNodeByKey(y, "type")
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if typ != "" && typ != t.Value {
|
|
||||||
continue
|
|
||||||
} // filter based on type
|
|
||||||
|
|
||||||
v, ok := EtcdGetChildNodeByKey(y, "value") // B64ToObj this
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Printf("Etcd: Hostname: %v; Get: %v", host, t.Value)
|
|
||||||
|
|
||||||
output = append(output, v.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: wrap this somehow so it's a method of *etcd.Node
|
|
||||||
// helper function that returns the node for a particular key under a node
|
|
||||||
func EtcdGetChildNodeByKey(node *etcd.Node, key string) (*etcd.Node, bool) {
|
|
||||||
for _, x := range node.Nodes {
|
|
||||||
if x.Key == fmt.Sprintf("%s/%s", node.Key, key) {
|
|
||||||
return x, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false // not found
|
|
||||||
}
|
|
||||||
2298
etcd/etcd.go
Normal file
115
etcd/str.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// Mgmt
|
||||||
|
// Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/util"
|
||||||
|
|
||||||
|
etcd "github.com/coreos/etcd/clientv3"
|
||||||
|
errwrap "github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WatchStr returns a channel which spits out events on key activity.
|
||||||
|
// FIXME: It should close the channel when it's done, and spit out errors when
|
||||||
|
// something goes wrong.
|
||||||
|
func WatchStr(obj *EmbdEtcd, key string) chan error {
|
||||||
|
// new key structure is /$NS/strings/$key/$hostname = $data
|
||||||
|
path := fmt.Sprintf("/%s/strings/%s", NS, key)
|
||||||
|
ch := make(chan error, 1)
|
||||||
|
// FIXME: fix our API so that we get a close event on shutdown.
|
||||||
|
callback := func(re *RE) error {
|
||||||
|
// TODO: is this even needed? it used to happen on conn errors
|
||||||
|
//log.Printf("Etcd: Watch: Path: %v", path) // event
|
||||||
|
if re == nil || re.response.Canceled {
|
||||||
|
return fmt.Errorf("watch is empty") // will cause a CtxError+retry
|
||||||
|
}
|
||||||
|
if len(ch) == 0 { // send event only if one isn't pending
|
||||||
|
ch <- nil // event
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, _ = obj.AddWatcher(path, callback, true, false, etcd.WithPrefix()) // no need to check errors
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStr collects all of the strings which match a namespace in etcd.
|
||||||
|
func GetStr(obj *EmbdEtcd, hostnameFilter []string, key string) (map[string]string, error) {
|
||||||
|
// old key structure is /$NS/strings/$hostname/$key = $data
|
||||||
|
// new key structure is /$NS/strings/$key/$hostname = $data
|
||||||
|
// FIXME: if we have the $key as the last token (old key structure), we
|
||||||
|
// can allow the key to contain the slash char, otherwise we need to
|
||||||
|
// verify that one isn't present in the input string.
|
||||||
|
path := fmt.Sprintf("/%s/strings/%s", NS, key)
|
||||||
|
keyMap, err := obj.Get(path, etcd.WithPrefix(), etcd.WithSort(etcd.SortByKey, etcd.SortAscend))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "could not get strings in: %s", key)
|
||||||
|
}
|
||||||
|
result := make(map[string]string)
|
||||||
|
for key, val := range keyMap {
|
||||||
|
if !strings.HasPrefix(key, path) { // sanity check
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
str := strings.Split(key[len(path):], "/")
|
||||||
|
if len(str) != 2 {
|
||||||
|
return nil, fmt.Errorf("unexpected chunk count of %d", len(str))
|
||||||
|
}
|
||||||
|
_, hostname := str[0], str[1]
|
||||||
|
|
||||||
|
if hostname == "" {
|
||||||
|
return nil, fmt.Errorf("unexpected chunk length of %d", len(hostname))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: ideally this would be a server side filter instead!
|
||||||
|
if len(hostnameFilter) > 0 && !util.StrInList(hostname, hostnameFilter) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//log.Printf("Etcd: GetStr(%s): (Hostname, Data): (%s, %s)", key, hostname, val)
|
||||||
|
result[hostname] = val
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStr sets a key and hostname pair to a certain value. If the value is nil,
|
||||||
|
// then it deletes the key. Otherwise the value should point to a string.
|
||||||
|
// TODO: TTL or delete disconnect?
|
||||||
|
func SetStr(obj *EmbdEtcd, hostname, key string, data *string) error {
|
||||||
|
// key structure is /$NS/strings/$key/$hostname = $data
|
||||||
|
path := fmt.Sprintf("/%s/strings/%s/%s", NS, key, hostname)
|
||||||
|
ifs := []etcd.Cmp{} // list matching the desired state
|
||||||
|
ops := []etcd.Op{} // list of ops in this transaction (then)
|
||||||
|
els := []etcd.Op{} // list of ops in this transaction (else)
|
||||||
|
if data == nil { // perform a delete
|
||||||
|
// TODO: use https://github.com/coreos/etcd/pull/7417 if merged
|
||||||
|
//ifs = append(ifs, etcd.KeyExists(path))
|
||||||
|
ifs = append(ifs, etcd.Compare(etcd.Version(path), ">", 0))
|
||||||
|
ops = append(ops, etcd.OpDelete(path))
|
||||||
|
} else {
|
||||||
|
data := *data // get the real value
|
||||||
|
ifs = append(ifs, etcd.Compare(etcd.Value(path), "=", data)) // desired state
|
||||||
|
els = append(els, etcd.OpPut(path, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's important to do this in one transaction, and atomically, because
|
||||||
|
// this way, we only generate one watch event, and only when it's needed
|
||||||
|
_, err := obj.Txn(ifs, ops, els) // TODO: do we need to look at response?
|
||||||
|
return errwrap.Wrapf(err, "could not set strings in: %s", key)
|
||||||
|
}
|
||||||
64
etcd/world.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Mgmt
|
||||||
|
// Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/purpleidea/mgmt/resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
// World is an etcd backed implementation of the World interface.
|
||||||
|
type World struct {
|
||||||
|
Hostname string // uuid for the consumer of these
|
||||||
|
EmbdEtcd *EmbdEtcd
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResExport exports a list of resources under our hostname namespace.
|
||||||
|
// Subsequent calls replace the previously set collection atomically.
|
||||||
|
func (obj *World) ResExport(resourceList []resources.Res) error {
|
||||||
|
return SetResources(obj.EmbdEtcd, obj.Hostname, resourceList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResCollect gets the collection of exported resources which match the filter.
|
||||||
|
// It does this atomically so that a call always returns a complete collection.
|
||||||
|
func (obj *World) ResCollect(hostnameFilter, kindFilter []string) ([]resources.Res, error) {
|
||||||
|
// XXX: should we be restricted to retrieving resources that were
|
||||||
|
// exported with a tag that allows or restricts our hostname? We could
|
||||||
|
// enforce that here if the underlying API supported it... Add this?
|
||||||
|
return GetResources(obj.EmbdEtcd, hostnameFilter, kindFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWatch returns a channel which spits out events on possible string changes.
|
||||||
|
func (obj *World) StrWatch(namespace string) chan error {
|
||||||
|
return WatchStr(obj.EmbdEtcd, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrGet returns a map of hostnames to values in the given namespace.
|
||||||
|
func (obj *World) StrGet(namespace string) (map[string]string, error) {
|
||||||
|
return GetStr(obj.EmbdEtcd, []string{}, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrSet sets the namespace value to a particular string under the identity of
|
||||||
|
// its own hostname.
|
||||||
|
func (obj *World) StrSet(namespace, value string) error {
|
||||||
|
return SetStr(obj.EmbdEtcd, obj.Hostname, namespace, &value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrDel deletes the value in a particular namespace.
|
||||||
|
func (obj *World) StrDel(namespace string) error {
|
||||||
|
return SetStr(obj.EmbdEtcd, obj.Hostname, namespace, nil)
|
||||||
|
}
|
||||||
55
event.go
@@ -1,55 +0,0 @@
|
|||||||
// Mgmt
|
|
||||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
|
||||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
//go:generate stringer -type=eventName -output=eventname_stringer.go
|
|
||||||
type eventName int
|
|
||||||
|
|
||||||
const (
|
|
||||||
eventExit eventName = iota
|
|
||||||
eventStart
|
|
||||||
eventPause
|
|
||||||
eventPoke
|
|
||||||
eventBackPoke
|
|
||||||
)
|
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
Name eventName
|
|
||||||
Resp chan bool // channel to send an ack response on, nil to skip
|
|
||||||
//Wg *sync.WaitGroup // receiver barrier to Wait() for everyone else on
|
|
||||||
Msg string // some words for fun
|
|
||||||
Activity bool // did something interesting happen?
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a single acknowledgement on the channel if one was requested
|
|
||||||
func (event *Event) ACK() {
|
|
||||||
if event.Resp != nil { // if they've requested an ACK
|
|
||||||
event.Resp <- true // send ACK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (event *Event) NACK() {
|
|
||||||
if event.Resp != nil { // if they've requested an ACK
|
|
||||||
event.Resp <- false // send NACK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the activity value
|
|
||||||
func (event *Event) GetActivity() bool {
|
|
||||||
return event.Activity
|
|
||||||
}
|
|
||||||
119
event/event.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// Mgmt
|
||||||
|
// Copyright (C) 2013-2017+ James Shubin and the project contributors
|
||||||
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package event provides some primitives that are used for message passing.
|
||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type=Kind -output=kind_stringer.go
|
||||||
|
|
||||||
|
// Kind represents the type of event being passed.
|
||||||
|
type Kind int
|
||||||
|
|
||||||
|
// The different event kinds are used in different contexts.
|
||||||
|
const (
|
||||||
|
EventNil Kind = iota
|
||||||
|
EventExit
|
||||||
|
EventStart
|
||||||
|
EventPause
|
||||||
|
EventPoke
|
||||||
|
EventBackPoke
|
||||||
|
)
|
||||||
|
|
||||||
|
// Resp is a channel to be used for boolean responses. A nil represents an ACK,
|
||||||
|
// and a non-nil represents a NACK (false). This also lets us use custom errors.
|
||||||
|
type Resp chan error
|
||||||
|
|
||||||
|
// Event is the main struct that stores event information and responses.
|
||||||
|
type Event struct {
|
||||||
|
Kind Kind
|
||||||
|
Resp Resp // channel to send an ack response on, nil to skip
|
||||||
|
//Wg *sync.WaitGroup // receiver barrier to Wait() for everyone else on
|
||||||
|
Err error // store an error in our event
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACK sends a single acknowledgement on the channel if one was requested.
|
||||||
|
func (event *Event) ACK() {
|
||||||
|
if event.Resp != nil { // if they've requested an ACK
|
||||||
|
event.Resp.ACK()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NACK sends a negative acknowledgement message on the channel if one was requested.
|
||||||
|
func (event *Event) NACK() {
|
||||||
|
if event.Resp != nil { // if they've requested a NACK
|
||||||
|
event.Resp.NACK()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACKNACK sends a custom ACK or NACK message on the channel if one was requested.
|
||||||
|
func (event *Event) ACKNACK(err error) {
|
||||||
|
if event.Resp != nil { // if they've requested a NACK
|
||||||
|
event.Resp.ACKNACK(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResp is just a helper to return the right type of response channel.
|
||||||
|
func NewResp() Resp {
|
||||||
|
resp := make(chan error)
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACK sends a true value to resp.
|
||||||
|
func (resp Resp) ACK() {
|
||||||
|
if resp != nil {
|
||||||
|
resp <- nil // TODO: close instead?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NACK sends a false value to resp.
|
||||||
|
func (resp Resp) NACK() {
|
||||||
|
if resp != nil {
|
||||||
|
resp <- fmt.Errorf("NACK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACKNACK sends a custom ACK or NACK. The ACK value is always nil, the NACK can
|
||||||
|
// be any non-nil error value.
|
||||||
|
func (resp Resp) ACKNACK(err error) {
|
||||||
|
if resp != nil {
|
||||||
|
resp <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for any response from a Resp channel and returns it.
|
||||||
|
func (resp Resp) Wait() error {
|
||||||
|
return <-resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACKWait waits for a +ive Ack from a Resp channel.
|
||||||
|
func (resp Resp) ACKWait() {
|
||||||
|
for {
|
||||||
|
// wait until true value
|
||||||
|
if resp.Wait() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the stored error value.
|
||||||
|
func (event *Event) Error() error {
|
||||||
|
return event.Err
|
||||||
|
}
|
||||||
11
examples/augeas1.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
augeas:
|
||||||
|
- name: sshd_config
|
||||||
|
lens: Sshd.lns
|
||||||
|
file: "/etc/ssh/sshd_config"
|
||||||
|
sets:
|
||||||
|
- path: X11Forwarding
|
||||||
|
value: false
|
||||||
|
edges:
|
||||||
19
examples/autoedges1.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
path: "/tmp/foo/bar/f1"
|
||||||
|
content: |
|
||||||
|
i am f1
|
||||||
|
state: exists
|
||||||
|
- name: file2
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
path: "/tmp/foo/"
|
||||||
|
content: |
|
||||||
|
i am f2
|
||||||
|
state: exists
|
||||||
|
edges: []
|
||||||
24
examples/autoedges2.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
path: "/etc/drbd.conf"
|
||||||
|
content: |
|
||||||
|
# this is an mgmt test
|
||||||
|
state: exists
|
||||||
|
- name: file2
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
path: "/tmp/foo/"
|
||||||
|
content: |
|
||||||
|
i am f2
|
||||||
|
state: exists
|
||||||
|
pkg:
|
||||||
|
- name: drbd-utils
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
state: installed
|
||||||
|
edges: []
|
||||||
32
examples/autoedges3.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
pkg:
|
||||||
|
- name: drbd-utils
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
noop: true
|
||||||
|
state: installed
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
noop: true
|
||||||
|
path: "/etc/drbd.conf"
|
||||||
|
content: |
|
||||||
|
# this is an mgmt test
|
||||||
|
state: exists
|
||||||
|
- name: file2
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
noop: true
|
||||||
|
path: "/etc/drbd.d/"
|
||||||
|
source: /dev/null
|
||||||
|
state: exists
|
||||||
|
svc:
|
||||||
|
- name: drbd
|
||||||
|
meta:
|
||||||
|
autoedge: true
|
||||||
|
noop: true
|
||||||
|
state: stopped
|
||||||
|
edges: []
|
||||||
21
examples/autogroup1.yaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
pkg:
|
||||||
|
- name: drbd-utils
|
||||||
|
meta:
|
||||||
|
autogroup: false
|
||||||
|
state: installed
|
||||||
|
- name: powertop
|
||||||
|
meta:
|
||||||
|
autogroup: true
|
||||||
|
state: installed
|
||||||
|
- name: sl
|
||||||
|
meta:
|
||||||
|
autogroup: true
|
||||||
|
state: installed
|
||||||
|
- name: cowsay
|
||||||
|
meta:
|
||||||
|
autogroup: true
|
||||||
|
state: installed
|
||||||
|
edges: []
|
||||||
17
examples/autogroup2.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
pkg:
|
||||||
|
- name: powertop
|
||||||
|
meta:
|
||||||
|
autogroup: true
|
||||||
|
state: installed
|
||||||
|
- name: sl
|
||||||
|
meta:
|
||||||
|
autogroup: true
|
||||||
|
state: installed
|
||||||
|
- name: cowsay
|
||||||
|
meta:
|
||||||
|
autogroup: true
|
||||||
|
state: installed
|
||||||
|
edges: []
|
||||||
9
examples/deep-dirs.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
path: "/tmp/mgmt/a/b/c/f1"
|
||||||
|
content: |
|
||||||
|
i am f1
|
||||||
|
state: exists
|
||||||
13
examples/etcd1a.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: "@@filea"
|
||||||
|
path: "/tmp/mgmtA/fA"
|
||||||
|
content: |
|
||||||
|
i am fA, exported from host A
|
||||||
|
state: exists
|
||||||
|
collect:
|
||||||
|
- kind: file
|
||||||
|
pattern: "/tmp/mgmtA/"
|
||||||
|
edges: []
|
||||||
13
examples/etcd1b.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: "@@fileb"
|
||||||
|
path: "/tmp/mgmtB/fB"
|
||||||
|
content: |
|
||||||
|
i am fB, exported from host B
|
||||||
|
state: exists
|
||||||
|
collect:
|
||||||
|
- kind: file
|
||||||
|
pattern: "/tmp/mgmtB/"
|
||||||
|
edges: []
|
||||||
13
examples/etcd1c.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: "@@filec"
|
||||||
|
path: "/tmp/mgmtC/fC"
|
||||||
|
content: |
|
||||||
|
i am fC, exported from host C
|
||||||
|
state: exists
|
||||||
|
collect:
|
||||||
|
- kind: file
|
||||||
|
pattern: "/tmp/mgmtC/"
|
||||||
|
edges: []
|
||||||
13
examples/etcd1d.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: "@@filed"
|
||||||
|
path: "/tmp/mgmtD/fD"
|
||||||
|
content: |
|
||||||
|
i am fD, exported from host D
|
||||||
|
state: exists
|
||||||
|
collect:
|
||||||
|
- kind: file
|
||||||
|
pattern: "/tmp/mgmtD/"
|
||||||
|
edges: []
|
||||||
13
examples/etcd1e.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: "@@filee"
|
||||||
|
path: "/tmp/mgmtE/fE"
|
||||||
|
content: |
|
||||||
|
i am fE, exported from host E
|
||||||
|
state: exists
|
||||||
|
collect:
|
||||||
|
- kind: file
|
||||||
|
pattern: "/tmp/mgmtE/"
|
||||||
|
edges: []
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: sleep 10s
|
cmd: sleep 10s
|
||||||
@@ -45,15 +45,15 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec1
|
name: exec1
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
- name: e2
|
- name: e2
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec3
|
name: exec3
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: sleep 10s
|
cmd: sleep 10s
|
||||||
@@ -25,8 +25,8 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec1
|
name: exec1
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: sleep 10s
|
cmd: sleep 10s
|
||||||
@@ -25,8 +25,8 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec1
|
name: exec1
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: echo hello from exec1
|
cmd: echo hello from exec1
|
||||||
@@ -25,8 +25,8 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec1
|
name: exec1
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: echo hello from exec1
|
cmd: echo hello from exec1
|
||||||
83
examples/exec2.yaml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
exec:
|
||||||
|
- name: exec1
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: exec2
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: exec3
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: exec4
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: exec5
|
||||||
|
cmd: sleep 15s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
edges:
|
||||||
|
- name: e1
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: exec1
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: exec2
|
||||||
|
- name: e2
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: exec1
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: exec3
|
||||||
|
- name: e3
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: exec2
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: exec4
|
||||||
|
- name: e4
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: exec3
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: exec4
|
||||||
67
examples/exec3-sema.yaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
graph: parallel
|
||||||
|
resources:
|
||||||
|
exec:
|
||||||
|
- name: pkg10
|
||||||
|
meta:
|
||||||
|
sema: ['mylock:1', 'otherlock:42']
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: svc10
|
||||||
|
meta:
|
||||||
|
sema: ['mylock:1']
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: exec10
|
||||||
|
meta:
|
||||||
|
sema: ['mylock:1']
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: pkg15
|
||||||
|
meta:
|
||||||
|
sema: ['mylock:1', 'otherlock:42']
|
||||||
|
cmd: sleep 15s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
edges:
|
||||||
|
- name: e1
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: pkg10
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: svc10
|
||||||
|
- name: e2
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: svc10
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: exec10
|
||||||
59
examples/exec3.yaml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
graph: parallel
|
||||||
|
resources:
|
||||||
|
exec:
|
||||||
|
- name: pkg10
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: svc10
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: exec10
|
||||||
|
cmd: sleep 10s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
- name: pkg15
|
||||||
|
cmd: sleep 15s
|
||||||
|
shell: ''
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
edges:
|
||||||
|
- name: e1
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: pkg10
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: svc10
|
||||||
|
- name: e2
|
||||||
|
from:
|
||||||
|
kind: exec
|
||||||
|
name: svc10
|
||||||
|
to:
|
||||||
|
kind: exec
|
||||||
|
name: exec10
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
noop:
|
noop:
|
||||||
- name: noop1
|
- name: noop1
|
||||||
file:
|
file:
|
||||||
@@ -27,15 +27,15 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: file
|
kind: file
|
||||||
name: file1
|
name: file1
|
||||||
to:
|
to:
|
||||||
type: file
|
kind: file
|
||||||
name: file2
|
name: file2
|
||||||
- name: e2
|
- name: e2
|
||||||
from:
|
from:
|
||||||
type: file
|
kind: file
|
||||||
name: file2
|
name: file2
|
||||||
to:
|
to:
|
||||||
type: file
|
kind: file
|
||||||
name: file3
|
name: file3
|
||||||
13
examples/file2.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
noop:
|
||||||
|
- name: noop1
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
path: "/tmp/mgmt/hello/"
|
||||||
|
source: "/var/lib/mgmt/files/some_dir/"
|
||||||
|
recurse: true
|
||||||
|
force: true
|
||||||
|
state: exists
|
||||||
|
edges: []
|
||||||
13
examples/file3.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
meta:
|
||||||
|
limit: .inf
|
||||||
|
burst: 0
|
||||||
|
path: "/tmp/mgmt/hello"
|
||||||
|
content: |
|
||||||
|
i am a file
|
||||||
|
state: exists
|
||||||
|
edges: []
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
comment: hello world example
|
comment: hello world example
|
||||||
types:
|
resources:
|
||||||
noop:
|
noop:
|
||||||
- name: noop1
|
- name: noop1
|
||||||
file:
|
file:
|
||||||
@@ -13,8 +13,8 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: noop
|
kind: noop
|
||||||
name: noop1
|
name: noop1
|
||||||
to:
|
to:
|
||||||
type: file
|
kind: file
|
||||||
name: file1
|
name: file1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
comment: simple exec fan in to fan out example to demonstrate optimization
|
comment: simple exec fan in to fan out example to demonstrate optimization
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: sleep 10s
|
cmd: sleep 10s
|
||||||
@@ -86,43 +86,43 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec1
|
name: exec1
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec4
|
name: exec4
|
||||||
- name: e2
|
- name: e2
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec4
|
name: exec4
|
||||||
- name: e3
|
- name: e3
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec3
|
name: exec3
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec4
|
name: exec4
|
||||||
- name: e4
|
- name: e4
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec4
|
name: exec4
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec5
|
name: exec5
|
||||||
- name: e5
|
- name: e5
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec4
|
name: exec4
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec6
|
name: exec6
|
||||||
- name: e6
|
- name: e6
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec4
|
name: exec4
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec7
|
name: exec7
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file1
|
- name: file1
|
||||||
path: "/tmp/mgmt/f1"
|
path: "/tmp/mgmt/f1"
|
||||||
@@ -15,8 +15,8 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: file
|
kind: file
|
||||||
name: file1
|
name: file1
|
||||||
to:
|
to:
|
||||||
type: file
|
kind: file
|
||||||
name: file2
|
name: file2
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file2
|
- name: file2
|
||||||
path: "/tmp/mgmt/f2"
|
path: "/tmp/mgmt/f2"
|
||||||
@@ -15,8 +15,8 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e2
|
- name: e2
|
||||||
from:
|
from:
|
||||||
type: file
|
kind: file
|
||||||
name: file2
|
name: file2
|
||||||
to:
|
to:
|
||||||
type: file
|
kind: file
|
||||||
name: file3
|
name: file3
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file1a
|
- name: file1a
|
||||||
path: "/tmp/mgmtA/f1a"
|
path: "/tmp/mgmtA/f1a"
|
||||||
@@ -23,6 +23,6 @@ types:
|
|||||||
i am f4, exported from host A
|
i am f4, exported from host A
|
||||||
state: exists
|
state: exists
|
||||||
collect:
|
collect:
|
||||||
- type: file
|
- kind: file
|
||||||
pattern: "/tmp/mgmtA/"
|
pattern: "/tmp/mgmtA/"
|
||||||
edges: []
|
edges: []
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file1b
|
- name: file1b
|
||||||
path: "/tmp/mgmtB/f1b"
|
path: "/tmp/mgmtB/f1b"
|
||||||
@@ -23,6 +23,6 @@ types:
|
|||||||
i am f4, exported from host B
|
i am f4, exported from host B
|
||||||
state: exists
|
state: exists
|
||||||
collect:
|
collect:
|
||||||
- type: file
|
- kind: file
|
||||||
pattern: "/tmp/mgmtB/"
|
pattern: "/tmp/mgmtB/"
|
||||||
edges: []
|
edges: []
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file1c
|
- name: file1c
|
||||||
path: "/tmp/mgmtC/f1c"
|
path: "/tmp/mgmtC/f1c"
|
||||||
@@ -23,6 +23,6 @@ types:
|
|||||||
i am f4, exported from host C
|
i am f4, exported from host C
|
||||||
state: exists
|
state: exists
|
||||||
collect:
|
collect:
|
||||||
- type: file
|
- kind: file
|
||||||
pattern: "/tmp/mgmtC/"
|
pattern: "/tmp/mgmtC/"
|
||||||
edges: []
|
edges: []
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file1
|
- name: file1
|
||||||
path: "/tmp/mgmt/f1"
|
path: "/tmp/mgmt/f1"
|
||||||
@@ -13,6 +13,6 @@ types:
|
|||||||
i am f3, exported from host A
|
i am f3, exported from host A
|
||||||
state: exists
|
state: exists
|
||||||
collect:
|
collect:
|
||||||
- type: file
|
- kind: file
|
||||||
pattern: ''
|
pattern: ''
|
||||||
edges:
|
edges:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
file:
|
file:
|
||||||
- name: file1
|
- name: file1
|
||||||
path: "/tmp/mgmt/f1"
|
path: "/tmp/mgmt/f1"
|
||||||
@@ -8,6 +8,6 @@ types:
|
|||||||
i am f1
|
i am f1
|
||||||
state: exists
|
state: exists
|
||||||
collect:
|
collect:
|
||||||
- type: file
|
- kind: file
|
||||||
pattern: ''
|
pattern: ''
|
||||||
edges:
|
edges:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
noop:
|
noop:
|
||||||
- name: noop1
|
- name: noop1
|
||||||
edges:
|
edges:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
types:
|
resources:
|
||||||
noop:
|
noop:
|
||||||
- name: noop1
|
- name: noop1
|
||||||
exec:
|
exec:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
graph: mygraph
|
graph: mygraph
|
||||||
comment: simple exec fan in example to demonstrate optimization
|
comment: simple exec fan in example to demonstrate optimization
|
||||||
types:
|
resources:
|
||||||
exec:
|
exec:
|
||||||
- name: exec1
|
- name: exec1
|
||||||
cmd: sleep 10s
|
cmd: sleep 10s
|
||||||
@@ -56,22 +56,22 @@ types:
|
|||||||
edges:
|
edges:
|
||||||
- name: e1
|
- name: e1
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec1
|
name: exec1
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec5
|
name: exec5
|
||||||
- name: e2
|
- name: e2
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec2
|
name: exec2
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec5
|
name: exec5
|
||||||
- name: e3
|
- name: e3
|
||||||
from:
|
from:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec3
|
name: exec3
|
||||||
to:
|
to:
|
||||||
type: exec
|
kind: exec
|
||||||
name: exec5
|
name: exec5
|
||||||
|
|||||||
7
examples/hostname.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
hostname:
|
||||||
|
- name: Hostname Watcher @ TestHost
|
||||||
|
hostname: test.hostname.example.com
|
||||||
|
edges: []
|
||||||
8
examples/kv1.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
kv:
|
||||||
|
- name: kv1
|
||||||
|
key: "hello"
|
||||||
|
value: "world"
|
||||||
|
edges: []
|
||||||
7
examples/kv2.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
kv:
|
||||||
|
- name: kv1
|
||||||
|
key: "iamdeleted"
|
||||||
|
edges: []
|
||||||
9
examples/kv3.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
kv:
|
||||||
|
- name: kv1
|
||||||
|
key: "stage"
|
||||||
|
value: "3"
|
||||||
|
skiplessthan: true
|
||||||
|
edges: []
|
||||||
31
examples/kv4.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
kv:
|
||||||
|
- name: kv1
|
||||||
|
key: "stage"
|
||||||
|
value: "1"
|
||||||
|
skiplessthan: true
|
||||||
|
- name: kv2
|
||||||
|
key: "stage"
|
||||||
|
value: "2"
|
||||||
|
skiplessthan: true
|
||||||
|
- name: kv3
|
||||||
|
key: "stage"
|
||||||
|
value: "3"
|
||||||
|
skiplessthan: true
|
||||||
|
edges:
|
||||||
|
- name: e1
|
||||||
|
from:
|
||||||
|
kind: kv
|
||||||
|
name: kv1
|
||||||
|
to:
|
||||||
|
kind: kv
|
||||||
|
name: kv2
|
||||||
|
- name: e2
|
||||||
|
from:
|
||||||
|
kind: kv
|
||||||
|
name: kv2
|
||||||
|
to:
|
||||||
|
kind: kv
|
||||||
|
name: kv3
|
||||||
192
examples/lib/libmgmt1.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
// libmgmt example
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/gapi"
|
||||||
|
mgmt "github.com/purpleidea/mgmt/lib"
|
||||||
|
"github.com/purpleidea/mgmt/pgraph"
|
||||||
|
"github.com/purpleidea/mgmt/resources"
|
||||||
|
"github.com/purpleidea/mgmt/yamlgraph"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyGAPI implements the main GAPI interface.
|
||||||
|
type MyGAPI struct {
|
||||||
|
Name string // graph name
|
||||||
|
Interval uint // refresh interval, 0 to never refresh
|
||||||
|
|
||||||
|
data gapi.Data
|
||||||
|
initialized bool
|
||||||
|
closeChan chan struct{}
|
||||||
|
wg sync.WaitGroup // sync group for tunnel go routines
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMyGAPI creates a new MyGAPI struct and calls Init().
|
||||||
|
func NewMyGAPI(data gapi.Data, name string, interval uint) (*MyGAPI, error) {
|
||||||
|
obj := &MyGAPI{
|
||||||
|
Name: name,
|
||||||
|
Interval: interval,
|
||||||
|
}
|
||||||
|
return obj, obj.Init(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the MyGAPI struct.
|
||||||
|
func (obj *MyGAPI) Init(data gapi.Data) error {
|
||||||
|
if obj.initialized {
|
||||||
|
return fmt.Errorf("already initialized")
|
||||||
|
}
|
||||||
|
if obj.Name == "" {
|
||||||
|
return fmt.Errorf("the graph name must be specified")
|
||||||
|
}
|
||||||
|
obj.data = data // store for later
|
||||||
|
obj.closeChan = make(chan struct{})
|
||||||
|
obj.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph returns a current Graph.
|
||||||
|
func (obj *MyGAPI) Graph() (*pgraph.Graph, error) {
|
||||||
|
if !obj.initialized {
|
||||||
|
return nil, fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
n1, err := resources.NewNoopRes("noop1")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't create resource: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can still build a graph via the yaml method
|
||||||
|
gc := &yamlgraph.GraphConfig{
|
||||||
|
Graph: obj.Name,
|
||||||
|
Resources: yamlgraph.Resources{ // must redefine anonymous struct :(
|
||||||
|
// in alphabetical order
|
||||||
|
Exec: []*resources.ExecRes{},
|
||||||
|
File: []*resources.FileRes{},
|
||||||
|
Msg: []*resources.MsgRes{},
|
||||||
|
Noop: []*resources.NoopRes{n1},
|
||||||
|
Pkg: []*resources.PkgRes{},
|
||||||
|
Svc: []*resources.SvcRes{},
|
||||||
|
Timer: []*resources.TimerRes{},
|
||||||
|
Virt: []*resources.VirtRes{},
|
||||||
|
},
|
||||||
|
//Collector: []collectorResConfig{},
|
||||||
|
//Edges: []Edge{},
|
||||||
|
Comment: "comment!",
|
||||||
|
}
|
||||||
|
|
||||||
|
g, err := gc.NewGraphFromConfig(obj.data.Hostname, obj.data.World, obj.data.Noop)
|
||||||
|
return g, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns nil errors every time there could be a new graph.
|
||||||
|
func (obj *MyGAPI) Next() chan error {
|
||||||
|
if obj.data.NoWatch || obj.Interval <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ch := make(chan error)
|
||||||
|
obj.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer obj.wg.Done()
|
||||||
|
defer close(ch) // this will run before the obj.wg.Done()
|
||||||
|
if !obj.initialized {
|
||||||
|
ch <- fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// arbitrarily change graph every interval seconds
|
||||||
|
ticker := time.NewTicker(time.Duration(obj.Interval) * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
log.Printf("libmgmt: Generating new graph...")
|
||||||
|
select {
|
||||||
|
case ch <- nil: // trigger a run
|
||||||
|
case <-obj.closeChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-obj.closeChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close shuts down the MyGAPI.
|
||||||
|
func (obj *MyGAPI) Close() error {
|
||||||
|
if !obj.initialized {
|
||||||
|
return fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
}
|
||||||
|
close(obj.closeChan)
|
||||||
|
obj.wg.Wait()
|
||||||
|
obj.initialized = false // closed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs an embedded mgmt server.
|
||||||
|
func Run() error {
|
||||||
|
|
||||||
|
obj := &mgmt.Main{}
|
||||||
|
obj.Program = "libmgmt" // TODO: set on compilation
|
||||||
|
obj.Version = "0.0.1" // TODO: set on compilation
|
||||||
|
obj.TmpPrefix = true
|
||||||
|
obj.IdealClusterSize = -1
|
||||||
|
obj.ConvergedTimeout = -1
|
||||||
|
obj.Noop = true
|
||||||
|
|
||||||
|
obj.GAPI = &MyGAPI{ // graph API
|
||||||
|
Name: "libmgmt", // TODO: set on compilation
|
||||||
|
Interval: 15, // arbitrarily change graph every 15 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obj.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// install the exit signal handler
|
||||||
|
exit := make(chan struct{})
|
||||||
|
defer close(exit)
|
||||||
|
go func() {
|
||||||
|
signals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||||
|
//signal.Notify(signals, os.Kill) // catch signals
|
||||||
|
signal.Notify(signals, syscall.SIGTERM)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sig := <-signals: // any signal will do
|
||||||
|
if sig == os.Interrupt {
|
||||||
|
log.Println("Interrupted by ^C")
|
||||||
|
obj.Exit(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Interrupted by signal")
|
||||||
|
obj.Exit(fmt.Errorf("killed by %v", sig))
|
||||||
|
return
|
||||||
|
case <-exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := obj.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Hello!")
|
||||||
|
if err := Run(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Goodbye!")
|
||||||
|
}
|
||||||
192
examples/lib/libmgmt2.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
// libmgmt example
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/gapi"
|
||||||
|
mgmt "github.com/purpleidea/mgmt/lib"
|
||||||
|
"github.com/purpleidea/mgmt/pgraph"
|
||||||
|
"github.com/purpleidea/mgmt/resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyGAPI implements the main GAPI interface.
|
||||||
|
type MyGAPI struct {
|
||||||
|
Name string // graph name
|
||||||
|
Count uint // number of resources to create
|
||||||
|
Interval uint // refresh interval, 0 to never refresh
|
||||||
|
|
||||||
|
data gapi.Data
|
||||||
|
initialized bool
|
||||||
|
closeChan chan struct{}
|
||||||
|
wg sync.WaitGroup // sync group for tunnel go routines
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMyGAPI creates a new MyGAPI struct and calls Init().
|
||||||
|
func NewMyGAPI(data gapi.Data, name string, interval uint, count uint) (*MyGAPI, error) {
|
||||||
|
obj := &MyGAPI{
|
||||||
|
Name: name,
|
||||||
|
Count: count,
|
||||||
|
Interval: interval,
|
||||||
|
}
|
||||||
|
return obj, obj.Init(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the MyGAPI struct.
|
||||||
|
func (obj *MyGAPI) Init(data gapi.Data) error {
|
||||||
|
if obj.initialized {
|
||||||
|
return fmt.Errorf("already initialized")
|
||||||
|
}
|
||||||
|
if obj.Name == "" {
|
||||||
|
return fmt.Errorf("the graph name must be specified")
|
||||||
|
}
|
||||||
|
obj.data = data // store for later
|
||||||
|
obj.closeChan = make(chan struct{})
|
||||||
|
obj.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph returns a current Graph.
|
||||||
|
func (obj *MyGAPI) Graph() (*pgraph.Graph, error) {
|
||||||
|
if !obj.initialized {
|
||||||
|
return nil, fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
g := pgraph.NewGraph(obj.Name)
|
||||||
|
var vertex *pgraph.Vertex
|
||||||
|
for i := uint(0); i < obj.Count; i++ {
|
||||||
|
n, err := resources.NewNoopRes(fmt.Sprintf("noop%d", i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't create resource: %v", err)
|
||||||
|
}
|
||||||
|
v := pgraph.NewVertex(n)
|
||||||
|
g.AddVertex(v)
|
||||||
|
if i > 0 {
|
||||||
|
g.AddEdge(vertex, v, pgraph.NewEdge(fmt.Sprintf("e%d", i)))
|
||||||
|
}
|
||||||
|
vertex = v // save
|
||||||
|
}
|
||||||
|
|
||||||
|
//g, err := config.NewGraphFromConfig(obj.data.Hostname, obj.data.World, obj.data.Noop)
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns nil errors every time there could be a new graph.
|
||||||
|
func (obj *MyGAPI) Next() chan error {
|
||||||
|
if obj.data.NoWatch || obj.Interval <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ch := make(chan error)
|
||||||
|
obj.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer obj.wg.Done()
|
||||||
|
defer close(ch) // this will run before the obj.wg.Done()
|
||||||
|
if !obj.initialized {
|
||||||
|
ch <- fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// arbitrarily change graph every interval seconds
|
||||||
|
ticker := time.NewTicker(time.Duration(obj.Interval) * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
log.Printf("libmgmt: Generating new graph...")
|
||||||
|
select {
|
||||||
|
case ch <- nil: // trigger a run
|
||||||
|
case <-obj.closeChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-obj.closeChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close shuts down the MyGAPI.
|
||||||
|
func (obj *MyGAPI) Close() error {
|
||||||
|
if !obj.initialized {
|
||||||
|
return fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
}
|
||||||
|
close(obj.closeChan)
|
||||||
|
obj.wg.Wait()
|
||||||
|
obj.initialized = false // closed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs an embedded mgmt server.
|
||||||
|
func Run(count uint) error {
|
||||||
|
|
||||||
|
obj := &mgmt.Main{}
|
||||||
|
obj.Program = "libmgmt" // TODO: set on compilation
|
||||||
|
obj.Version = "0.0.1" // TODO: set on compilation
|
||||||
|
obj.TmpPrefix = true
|
||||||
|
obj.IdealClusterSize = -1
|
||||||
|
obj.ConvergedTimeout = -1
|
||||||
|
obj.Noop = true
|
||||||
|
|
||||||
|
obj.GAPI = &MyGAPI{ // graph API
|
||||||
|
Name: "libmgmt", // TODO: set on compilation
|
||||||
|
Count: count, // number of vertices to add
|
||||||
|
Interval: 15, // arbitrarily change graph every 15 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obj.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// install the exit signal handler
|
||||||
|
exit := make(chan struct{})
|
||||||
|
defer close(exit)
|
||||||
|
go func() {
|
||||||
|
signals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||||
|
//signal.Notify(signals, os.Kill) // catch signals
|
||||||
|
signal.Notify(signals, syscall.SIGTERM)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sig := <-signals: // any signal will do
|
||||||
|
if sig == os.Interrupt {
|
||||||
|
log.Println("Interrupted by ^C")
|
||||||
|
obj.Exit(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Interrupted by signal")
|
||||||
|
obj.Exit(fmt.Errorf("killed by %v", sig))
|
||||||
|
return
|
||||||
|
case <-exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := obj.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Hello!")
|
||||||
|
var count uint = 1 // default
|
||||||
|
if len(os.Args) == 2 {
|
||||||
|
if i, err := strconv.Atoi(os.Args[1]); err == nil && i > 0 {
|
||||||
|
count = uint(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := Run(count); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Goodbye!")
|
||||||
|
}
|
||||||
241
examples/lib/libmgmt3.go
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
// libmgmt example of send->recv
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/gapi"
|
||||||
|
mgmt "github.com/purpleidea/mgmt/lib"
|
||||||
|
"github.com/purpleidea/mgmt/pgraph"
|
||||||
|
"github.com/purpleidea/mgmt/resources"
|
||||||
|
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyGAPI implements the main GAPI interface.
|
||||||
|
type MyGAPI struct {
|
||||||
|
Name string // graph name
|
||||||
|
Interval uint // refresh interval, 0 to never refresh
|
||||||
|
|
||||||
|
data gapi.Data
|
||||||
|
initialized bool
|
||||||
|
closeChan chan struct{}
|
||||||
|
wg sync.WaitGroup // sync group for tunnel go routines
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMyGAPI creates a new MyGAPI struct and calls Init().
|
||||||
|
func NewMyGAPI(data gapi.Data, name string, interval uint) (*MyGAPI, error) {
|
||||||
|
obj := &MyGAPI{
|
||||||
|
Name: name,
|
||||||
|
Interval: interval,
|
||||||
|
}
|
||||||
|
return obj, obj.Init(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the MyGAPI struct.
|
||||||
|
func (obj *MyGAPI) Init(data gapi.Data) error {
|
||||||
|
if obj.initialized {
|
||||||
|
return fmt.Errorf("already initialized")
|
||||||
|
}
|
||||||
|
if obj.Name == "" {
|
||||||
|
return fmt.Errorf("the graph name must be specified")
|
||||||
|
}
|
||||||
|
obj.data = data // store for later
|
||||||
|
obj.closeChan = make(chan struct{})
|
||||||
|
obj.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph returns a current Graph.
|
||||||
|
func (obj *MyGAPI) Graph() (*pgraph.Graph, error) {
|
||||||
|
if !obj.initialized {
|
||||||
|
return nil, fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
g := pgraph.NewGraph(obj.Name)
|
||||||
|
|
||||||
|
// FIXME: these are being specified temporarily until it's the default!
|
||||||
|
metaparams := resources.MetaParams{
|
||||||
|
Limit: rate.Inf,
|
||||||
|
Burst: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
content := "Delete me to trigger a notification!\n"
|
||||||
|
f0 := &resources.FileRes{
|
||||||
|
BaseRes: resources.BaseRes{
|
||||||
|
Name: "README",
|
||||||
|
MetaParams: metaparams,
|
||||||
|
},
|
||||||
|
Path: "/tmp/mgmt/README",
|
||||||
|
Content: &content,
|
||||||
|
State: "present",
|
||||||
|
}
|
||||||
|
|
||||||
|
v0 := pgraph.NewVertex(f0)
|
||||||
|
g.AddVertex(v0)
|
||||||
|
|
||||||
|
p1 := &resources.PasswordRes{
|
||||||
|
BaseRes: resources.BaseRes{
|
||||||
|
Name: "password1",
|
||||||
|
MetaParams: metaparams,
|
||||||
|
},
|
||||||
|
Length: 8, // generated string will have this many characters
|
||||||
|
Saved: true, // this causes passwords to be stored in plain text!
|
||||||
|
}
|
||||||
|
v1 := pgraph.NewVertex(p1)
|
||||||
|
g.AddVertex(v1)
|
||||||
|
|
||||||
|
f1 := &resources.FileRes{
|
||||||
|
BaseRes: resources.BaseRes{
|
||||||
|
Name: "file1",
|
||||||
|
MetaParams: metaparams,
|
||||||
|
// send->recv!
|
||||||
|
Recv: map[string]*resources.Send{
|
||||||
|
"Content": {Res: p1, Key: "Password"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Path: "/tmp/mgmt/secret",
|
||||||
|
//Content: p1.Password, // won't work
|
||||||
|
State: "present",
|
||||||
|
}
|
||||||
|
|
||||||
|
v2 := pgraph.NewVertex(f1)
|
||||||
|
g.AddVertex(v2)
|
||||||
|
|
||||||
|
n1 := &resources.NoopRes{
|
||||||
|
BaseRes: resources.BaseRes{
|
||||||
|
Name: "noop1",
|
||||||
|
MetaParams: metaparams,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
v3 := pgraph.NewVertex(n1)
|
||||||
|
g.AddVertex(v3)
|
||||||
|
|
||||||
|
e0 := pgraph.NewEdge("e0")
|
||||||
|
e0.Notify = true // send a notification from v0 to v1
|
||||||
|
g.AddEdge(v0, v1, e0)
|
||||||
|
|
||||||
|
g.AddEdge(v1, v2, pgraph.NewEdge("e1"))
|
||||||
|
|
||||||
|
e2 := pgraph.NewEdge("e2")
|
||||||
|
e2.Notify = true // send a notification from v2 to v3
|
||||||
|
g.AddEdge(v2, v3, e2)
|
||||||
|
|
||||||
|
//g, err := config.NewGraphFromConfig(obj.data.Hostname, obj.data.World, obj.data.Noop)
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns nil errors every time there could be a new graph.
|
||||||
|
func (obj *MyGAPI) Next() chan error {
|
||||||
|
if obj.data.NoWatch || obj.Interval <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ch := make(chan error)
|
||||||
|
obj.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer obj.wg.Done()
|
||||||
|
defer close(ch) // this will run before the obj.wg.Done()
|
||||||
|
if !obj.initialized {
|
||||||
|
ch <- fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// arbitrarily change graph every interval seconds
|
||||||
|
ticker := time.NewTicker(time.Duration(obj.Interval) * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
log.Printf("libmgmt: Generating new graph...")
|
||||||
|
select {
|
||||||
|
case ch <- nil: // trigger a run
|
||||||
|
case <-obj.closeChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-obj.closeChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close shuts down the MyGAPI.
|
||||||
|
func (obj *MyGAPI) Close() error {
|
||||||
|
if !obj.initialized {
|
||||||
|
return fmt.Errorf("libmgmt: MyGAPI is not initialized")
|
||||||
|
}
|
||||||
|
close(obj.closeChan)
|
||||||
|
obj.wg.Wait()
|
||||||
|
obj.initialized = false // closed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs an embedded mgmt server.
|
||||||
|
func Run() error {
|
||||||
|
|
||||||
|
obj := &mgmt.Main{}
|
||||||
|
obj.Program = "libmgmt" // TODO: set on compilation
|
||||||
|
obj.Version = "0.0.1" // TODO: set on compilation
|
||||||
|
obj.TmpPrefix = true // disable for easy debugging
|
||||||
|
//prefix := "/tmp/testprefix/"
|
||||||
|
//obj.Prefix = &p // enable for easy debugging
|
||||||
|
obj.IdealClusterSize = -1
|
||||||
|
obj.ConvergedTimeout = -1
|
||||||
|
obj.Noop = false // FIXME: careful!
|
||||||
|
|
||||||
|
obj.GAPI = &MyGAPI{ // graph API
|
||||||
|
Name: "libmgmt", // TODO: set on compilation
|
||||||
|
Interval: 60 * 10, // arbitrarily change graph every 15 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obj.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// install the exit signal handler
|
||||||
|
exit := make(chan struct{})
|
||||||
|
defer close(exit)
|
||||||
|
go func() {
|
||||||
|
signals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||||
|
//signal.Notify(signals, os.Kill) // catch signals
|
||||||
|
signal.Notify(signals, syscall.SIGTERM)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sig := <-signals: // any signal will do
|
||||||
|
if sig == os.Interrupt {
|
||||||
|
log.Println("Interrupted by ^C")
|
||||||
|
obj.Exit(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Interrupted by signal")
|
||||||
|
obj.Exit(fmt.Errorf("killed by %v", sig))
|
||||||
|
return
|
||||||
|
case <-exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := obj.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Hello!")
|
||||||
|
if err := Run(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Goodbye!")
|
||||||
|
}
|
||||||
13
examples/limit1.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
meta:
|
||||||
|
limit: 0.2
|
||||||
|
burst: 5
|
||||||
|
path: "/tmp/mgmt/limit"
|
||||||
|
content: |
|
||||||
|
i am a normal file
|
||||||
|
state: exists
|
||||||
|
edges: []
|
||||||
19
examples/msg1.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
comment: timer example
|
||||||
|
resources:
|
||||||
|
timer:
|
||||||
|
- name: timer1
|
||||||
|
interval: 30
|
||||||
|
msg:
|
||||||
|
- name: msg1
|
||||||
|
body: mgmt logged this message
|
||||||
|
journal: true
|
||||||
|
edges:
|
||||||
|
- name: e1
|
||||||
|
from:
|
||||||
|
kind: timer
|
||||||
|
name: timer1
|
||||||
|
to:
|
||||||
|
kind: msg
|
||||||
|
name: msg1
|
||||||
24
examples/noop1.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
comment: noop example
|
||||||
|
resources:
|
||||||
|
noop:
|
||||||
|
- name: noop1
|
||||||
|
meta:
|
||||||
|
noop: true
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
path: "/tmp/mgmt-hello-noop"
|
||||||
|
content: |
|
||||||
|
hello world from @purpleidea
|
||||||
|
state: exists
|
||||||
|
meta:
|
||||||
|
noop: true
|
||||||
|
edges:
|
||||||
|
- name: e1
|
||||||
|
from:
|
||||||
|
kind: noop
|
||||||
|
name: noop1
|
||||||
|
to:
|
||||||
|
kind: file
|
||||||
|
name: file1
|
||||||
30
examples/noop2.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
comment: dangerous noop example
|
||||||
|
resources:
|
||||||
|
noop:
|
||||||
|
- name: noop1
|
||||||
|
meta:
|
||||||
|
noop: true
|
||||||
|
file:
|
||||||
|
- name: file1
|
||||||
|
path: "/tmp/mgmt/hello-noop"
|
||||||
|
content: |
|
||||||
|
hello world from @purpleidea
|
||||||
|
state: exists
|
||||||
|
meta:
|
||||||
|
noop: true
|
||||||
|
exec:
|
||||||
|
- name: exec1
|
||||||
|
meta:
|
||||||
|
noop: true
|
||||||
|
cmd: 'rm -rf /'
|
||||||
|
shell: '/bin/bash'
|
||||||
|
timeout: 0
|
||||||
|
watchcmd: ''
|
||||||
|
watchshell: ''
|
||||||
|
ifcmd: ''
|
||||||
|
ifshell: ''
|
||||||
|
pollint: 0
|
||||||
|
state: present
|
||||||
|
edges: []
|
||||||
7
examples/nspawn1.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
nspawn:
|
||||||
|
- name: mgmt-nspawn1
|
||||||
|
state: running
|
||||||
|
edges: []
|
||||||
7
examples/nspawn2.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
nspawn:
|
||||||
|
- name: mgmt-nspawn2
|
||||||
|
state: stopped
|
||||||
|
edges: []
|
||||||
7
examples/pkg1.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
graph: mygraph
|
||||||
|
resources:
|
||||||
|
pkg:
|
||||||
|
- name: powertop
|
||||||
|
state: installed
|
||||||
|
edges: []
|
||||||