1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
|
<html>
<head>
<link rel="stylesheet" type="text/css" href="http://www.cfengine.org/menus.css" />
<link rel="stylesheet" type="text/css" href="http://www.cfengine.org/cf_blue.css"/>
<title>Cfengine 3 -- a promising new language</title>
</head>
<body>
<h1>The Cfengine 3 concept guide</h1>
<p>
Cfengine is a suite of programs for integrated autonomic management of
either individual or networked computers. It has existed a This document represents
cfengine versions 3.0.0 and later, which are a radical departure from
earlier versions.
Cfengine 3 has been changed so as to be both a more powerful and much
simpler. Cfengine 3 is not backwards compatible with the cfengine 2
configuration language, but it interoperates with cfengine 2 so that
it is "run-time compatible". This means that you can change over to
version 3 slowly, with low risk and at your own speed.
With cfengine 3 you can install, configure and maintain computers
using powerful hands-free tools. You can also integrate knowledge
management and diagnosis into the processes.
<p>
Cfengine differs from most management systems in being
<ul>
<li>Open software (GPL).
<li>Lightweight and generic.
<li>Non-reliant on a working network to function correctly.
<li>Capable of making each and every host autonomous
</ul>
<p>
Cfengine 3 consists of a number of components:
<p>
<table border="1">
<tr><th>Component </th><th>New</th><th>Function</th></tr>
<tr><td>cf-agent </td><td> </td> <td>Active agent</td></tr>
<tr><td>cf-execd </td><td> </td> <td>Scheduler</td></tr>
<tr><td>cf-graph </td><td> </td> <td>Graph data extractor</td></tr>
<tr><td>cf-know </td><td>*</td> <td>Knowledge modelling agent</td></tr>
<tr><td>cf-monitord </td><td></td> <td>Passive monitoring agent</td></tr>
<tr><td>cf-promises </td><td>*</td> <td>Promise validator</td></tr>
<tr><td>cf-runAgent </td><td> </td> <td>Remote run agent</td></tr>
<tr><td>cf-serverd </td><td> </td> <td>Server agent</td></tr>
<tr><td>cf-show </td><td> </td> <td>Self-knowledge extractor</td></tr>
</table>
<p>
The starred components are new. The daemon formally called
<tt>cfenvd</tt> i previous versions of cfengine is now called
<tt>cfMonitord</tt>.
<p>
Unlike previous versions of cfengine, which had no consistent model
for its features, you can recognize <i>everything</i> in cfengine 3
from just a few concepts.
<p>
<table border="1">
<tr><th>Concept</th><th>Quick description</th>
<tr><td>Promise</td><td>A statement about the state we desire to maintain</td>
<tr><td>Promise bundles</td><td>A collection of promises</td>
<tr><td>Promise bodies</td><td>A part of a promise which details and constrains its nature</td>
<tr><td>Data types</td><td>An interpretation of a scalar value: string, integer or real number</td>
<tr><td>Variables</td><td>An association of the form "LVALUE <i>represents</i> RVALUE", where rval may be a scalar value or a list of scalar values</td>
<tr><td>Functions</td><td>Built-in parameterized rvalues</td>
<tr><td>Classes</td><td>Cfengine's boolean classifiers that describe <i>context</i></td>
</table>
<p>
If you have used cfengine before then the most visible part of
cfengine 3 will be its new language interface. Although it has been
clear for a long time that the organically grown language used in
cfengines 1 and 2 developed many problems, it was not immediately
clear exactly what would be better. It has taken years of research to
simplify the successful features of cfengine to a single overarching
model. To understand the new cfengine, it is best to set aside any
preconceptions about what cfengine is today. Cfengine 3 is a genuine
"next generation" effort, which is will be a springboard into the
future of system management.
</p>
<h2>Why change the language?</h2>
<p>
Many attempts at improving the user interface of cfengine have been
proposed but none of them have been sufficiently impressive to make
the change worthwhile before now. Some have gone in for an Object Oriented
approach, but this imposes a hierarchical model that does not fit
cfengine's autonomous peer model. The main goal in changing the
language is to simplify and improve the robustness and functionality
without sacrificing the basic freedoms and concepts. Concepts such as
explicit loops and and tests have long been banished from cfengine and
proposals to reintroduce them have been dismissed --- something better
is needed. The difficulty, of course is to provide a genuine
simplification and improvement that is robust and lasting: this
requires a deep understanding of the problem.
</p>
<p>
Cfengine 3's new language is a direct implementation of a model
developed at Oslo University College over the past four years, known
colloquially as "promise theory". Promises were originally introduced
by Mark Burgess as a way to talk about cfengine's model of autonomy
and have since become a powerful way of modelling cooperative
systems. Cfengine 3 is a generic implementation of the language of
promises that allows all of the aspects of configuration management to
be unified under a single umbrella.
</p>
<p>
Why talk about promises instead of simply talking about changes? After all,
the trend in business and IT management today is to talk about Change
Management, e.g. in the IT Infrastructure Library (ITIL) terminology.
This comes from a long history of process management thinking. But we are not
really interested in change -- we are interested in being in a state where we
don't need to make any changes. In other words we want to be able to promise
that the system is correct, verify this and only make changes if our promises
are not kept.
</p>
<p>
To put it another way, cfengine is not really a <i>change
management</i> system, it is a <i>maintenance system</i>. Maintenance
is the process of making small changes or corrections to a model. A
"model" is just another word for a template or a specification of how
we want the system to work. Cfengine's model is based on the idea of
promises, which means that it focuses on what is stable and lasting
about a system -- not about what is changing.
<p>
This is an important philosophical shift. It means we are focused
mainly on what is right and not on what is wrong. By saying what
"right" is (the ideal state of our system) we are focussed on the
actual behaviour. If we focus too much on the changes, i.e. the
differences between now and the future, we might forget to verify that what
we assume is working now in fact works.
<p>
Models that talk about change management tend to forget that after
every change there is a litany of <i>incidents</i> during which it is
necessary to repair the system or return it to its intended state.
But if we know what we have promised, it is easy to verify whether the
promise is kept.
This means that it is the <i>promises</i> about how the system should
be that are most important, not the actual changes that are made in
order to keep them.
</p>
<h1>Familiarizing yourself</h1>
<p>
To familiarize yourself with cfengine 3, type or paste in the following example text:
<pre>
########################################################
#
# Simple test execution
#
########################################################
body common control
{
bundlesequence => { "testbundle" };
}
########################################################
bundle agent testbundle
{
vars:
"size" int => "46k";
"rand" int => randomint("33","$(size)");
commands:
"/bin/echo"
args => "Hello world - $(size)/$(rand)",
contain => standard,
classes => mydefine("followup","alert");
followup::
"/bin/ls"
contain => standard;
reports:
alert::
"What happened?";
}
######################################################################
body contain standard
{
exec_owner => "mark";
useshell => "true";
}
######################################################################
body classes mydefine(class,alert)
{
on_change => { "$(class)" };
on_failure => { "$(alert)" };
}
</pre>
<p>
This example shows all of the main features of cfengine: bundles,
bodies, control, variables, and promises. To the casual eye it might
look complex, but that is because it is explicit about all of the
details. Fortunately it is easy to hide many of these details to
make the example simpler without sacrificing any functionality.
<p>
The first thing to try with this example is to verify it -- did we
make any mistakes? Are there any inconsistencies? To do this we use
the new cfengine program <tt>cfpromises</tt>. Let's assume that you
typed this into a file called <tt>test.cf</tt> in the current directory.
<pre>
cfpromises -f ./test.cf
</pre>
<p>
If all is well, typing this command shows no output. Try now running the
command with verbose output.
<pre>
cfpromises -f ./test.cf -v
</pre>
<p>
Now you see a lot of information
<pre>
Reference time set to Sat Aug 2 11:26:06 2008
cf3 Cfengine - 3.0.0
Free Software Foundation 1994-
Donated by Mark Burgess, Oslo University College, Norway
cf3 ------------------------------------------------------------------------
cf3 Host name is: atlas
cf3 Operating System Type is linux
cf3 Operating System Release is 2.6.22.18-0.2-default
cf3 Architecture = x86_64
cf3 Using internal soft-class linux for host linux
cf3 The time is now Sat Aug 2 11:26:06 2008
cf3 ------------------------------------------------------------------------
cf3 Additional hard class defined as: 64_bit
cf3 Additional hard class defined as: linux_2_6_22_18_0_2_default
cf3 Additional hard class defined as: linux_x86_64
cf3 Additional hard class defined as: linux_x86_64_2_6_22_18_0_2_default
cf3 GNU autoconf class from compile time: compiled_on_linux_gnu
cf3 Interface 1: lo
cf3 Trying to locate my IPv6 address
cf3 Looking for environment from cfenvd...
cf3 Unable to detect environment from cfMonitord
---------------------------------------------------------------------
Loading persistent classes
---------------------------------------------------------------------
---------------------------------------------------------------------
Loaded persistent memory
---------------------------------------------------------------------
cf3 > Parsing file ../tests/runtest_8.cf
---------------------------------------------------------------------
Agent's basic classified context
---------------------------------------------------------------------
Defined Classes = ( any Saturday Hr11 Min26 Min25_30 Q2 Hr11_Q2 Day2 August Yr2008 linux atlas 64_bit linux_2_6_22_18_0_2_default x86_64 linux_x86_64 linux_x86_64_2_6_22_18_0_2_default linux_x86_64_2_6_22_18_0_2_default__1_SMP_2008_06_09_13_53_20__0200 compiled_on_linux_gnu net_iface_lo )
Negated Classes = ( )
Installable classes = ( )
<b>cf3 Wrote expansion summary to promise_output_common.html
cf3 Inputs are valid</b>
</pre>
<p>
The last two lines fo this are of interest. Each time a component of
cfengine 3 parses a number of promises, it summarizes the information
in an HTML file called generically <tt>promise_output_<i>component-type</i>.html</tt>.
In this case the <tt>cfpromises</tt> command represents all possible promises,
by the type "common". You can view this output file in a suitable web browser
to see exactly what cfengine has understood by the configuration.
The output looks something like this:
<p>
<table border=1>
<tr><td>
<h2>Expanded promise list for common component</h2>
<p>
<table>
<tr><td>
<tr><td bgcolor=#fefdec></td><td bgcolor=#fefeec><table class=border width=800><tr><td bgcolor=#ffffff>
Promise type is <i><font color=blue>vars</font></i>, context is <i><font color=blue>any</font></i> <br><hr>
Resource object <b>'size'</b> promises to default promisee 'cf-agent' (about vars)...
<br><font color=green>........................int</font> => 46k , if body context any
<p><small>Promise belongs to bundle <b>testbundle</b> (type agent) in '<i>../tests/runtest_8.cf</i>' near line 20</small></p>
</td></tr></table></td></tr>
</td></tr>
</p><p><tr><td>
<tr><td bgcolor=#fefdec></td><td bgcolor=#fefeec><table class=border width=800><tr><td bgcolor=#ffffff>
Promise type is <i><font color=blue>vars</font></i>, context is <i><font color=blue>any</font></i> <br><hr>
Resource object <b>'rand'</b> promises to default promisee 'cf-agent' (about vars)...
<br><font color=green>........................int</font> => 15145 , if body context any
<p><small>Promise belongs to bundle <b>testbundle</b> (type agent) in '<i>../tests/runtest_8.cf</i>' near line 21</small></p>
</td></tr></table></td></tr>
</td></tr>
</p><p><tr><td>
<tr><td bgcolor=#fefdec></td><td bgcolor=#fefeec><table class=border width=800><tr><td bgcolor=#ffffff>
Promise type is <i><font color=blue>commands</font></i>, context is <i><font color=blue>any</font></i> <br><hr>
Resource object <b>'/bin/echo'</b> promises to default promisee 'cf-agent' (about commands)...
<br><font color=green>........................args</font> => Hello world - 46k/15145 , if body context any
<br><font color=green>........................contain</font> => true , if body context any
<br><font color=green>........................exec_owner</font> => mark , if body context any
<br><font color=green>........................useshell</font> => true , if body context any
<br><font color=green>........................classes</font> => true , if body context any
<br><font color=green>........................on_change</font> => {'followup'} , if body context any
<br><font color=green>........................on_failure</font> => {'alert'} , if body context any
<p><small>Promise belongs to bundle <b>testbundle</b> (type agent) in '<i>../tests/runtest_8.cf</i>' near line 26</small></p>
</td></tr></table></td></tr>
</td></tr>
</p><p><tr><td>
<tr><td bgcolor=#fefdec></td><td bgcolor=#fefeec><table class=border width=800><tr><td bgcolor=#ffffff>
Promise type is <i><font color=blue>commands</font></i>, context is <i><font color=blue>followup</font></i> <br><hr>
Resource object <b>'/bin/ls'</b> promises to default promisee 'cf-agent' (about commands)...
<br><font color=green>........................contain</font> => true , if body context any
<br><font color=green>........................exec_owner</font> => mark , if body context any
<br><font color=green>........................useshell</font> => true , if body context any
<p><small>Promise belongs to bundle <b>testbundle</b> (type agent) in '<i>../tests/runtest_8.cf</i>' near line 33</small></p>
</td></tr></table></td></tr>
</td></tr>
</p><p><tr><td>
<tr><td bgcolor=#fefdec></td><td bgcolor=#fefeec><table class=border width=800><tr><td bgcolor=#ffffff>
Promise type is <i><font color=blue>reports</font></i>, context is <i><font color=blue>alert</font></i> <br><hr>
Resource object <b>'What happened?'</b> promises to default promisee 'cf-agent' (about reports)...
<p><small>Promise belongs to bundle <b>testbundle</b> (type agent) in '<i>../tests/runtest_8.cf</i>' near line 39</small></p>
</td></tr></table></td></tr>
</td></tr>
</p><p>
Constant variables in SCOPE control_common:
<br><p><table class=border width=600>
<tr><th>id</th><th>dtype</th><th>rtype</th><th>identifier</th><th>Rvalue</th></tr>
<tr><td> 1039 </td><th> slist</th><td> l</td><td> bundlesequence</td><td> {'testbundle'}</td></tr>
</table>
<p>
Constant variables in SCOPE testbundle:
<br><p><table class=border width=600>
<tr><th>id</th><th>dtype</th><th>rtype</th><th>identifier</th><th>Rvalue</th></tr>
<tr><td> 1386 </td><th> int</th><td> s</td><td> size</td><td> 46k</td></tr>
<tr><td> 2292 </td><th> int</th><td> s</td><td> rand</td><td> 15145</td></tr>
</table>
<p>
Constant variables in SCOPE const:
<br><p><table class=border width=600>
<tr><th>id</th><th>dtype</th><th>rtype</th><th>identifier</th><th>Rvalue</th></tr>
<tr><td> 110 </td><th> string</th><td> s</td><td> n</td><td>
</td></tr>
<tr><td> 114 </td><th> string</th><td> s</td><td> r</td><td>
</td></tr>
<tr><td> 518 </td><th> string</th><td> s</td><td> dollar</td><td> $</td></tr>
<tr><td> 1206 </td><th> string</th><td> s</td><td> endl</td><td>
</td></tr>
</table>
<p>
Constant variables in SCOPE sys:
<br><p><table class=border width=600>
<tr><th>id</th><th>dtype</th><th>rtype</th><th>identifier</th><th>Rvalue</th></tr>
<tr><td> 28 </td><th> string</th><td> s</td><td> long_arch</td><td> linux_x86_64_2_6_22_18_0_2_default__1_SMP_2008_06_09_13_53_20__0200</td></tr>
<tr><td> 116 </td><th> string</th><td> s</td><td> date</td><td> linux</td></tr>
<tr><td> 1071 </td><th> string</th><td> s</td><td> host</td><td> atlas</td></tr>
<tr><td> 1291 </td><th> string</th><td> s</td><td> ostype</td><td> linux_x86_64</td></tr>
<tr><td> 1917 </td><th> string</th><td> s</td><td> os</td><td> linux</td></tr>
<tr><td> 2089 </td><th> string</th><td> s</td><td> class</td><td> linux</td></tr>
<tr><td> 2224 </td><th> string</th><td> s</td><td> release</td><td> 2.6.22.18-0.2-default</td></tr>
<tr><td> 2521 </td><th> string</th><td> s</td><td> arch</td><td> x86_64</td></tr>
</table>
</td></tr>
</table>
</table>
<p>
This is simply a summary of the `compilation'. Nothing has happened yet. To make something
happen, you need to pass these promises to the agent for whom they are intended. Notice that
the promise bundle has a type "agent" and that the output claims that the promises
are intended for "cf-agent". In fact, each component of cfengine looks for promises that
are addressed to it.
(In earlier cfengine versions there were different files for each component. In cfengine 3
you can make promises for any agent in any file, the bundles label which agents
will pay attention to them.)
<p>
So try verifying / keeping the promises in this file.
<pre>
cf-agent -f ./test.cf
cf-agent -f ./test.cf -v
</pre>
You may now look also at <tt>promise_output_agent.html</tt>.
<h1>Cfengine's promise language</h1>
<p>
In the general case, a promise is made from one
autonomous entity to another. The general theory allows for an
in-depth discussion of this, but in cfengine we can often suppress
the recipient of a promise and simply consider all the independent
resources in the system as making promises to the system administrator
or some other external entity. This is not always true, but it is
an okay starting point.
<ul>
<li>Promise object (or promiser)</li>
<li>Recipient (or promisee) -- often suppressed</li>
</ul>
So we can imagine a promise as an arrow from one entity to another.
<pre>
"promiser" -> "promisee"
</pre>
The promisee is usually someone or something that is interested in the
promise and will therefore verify it. For now this value is not used
much in cfengine, but is present for future developments. If left
unspecified it defaults to the agent that is responsible for
processing the current promise-bundle (the bundle type, e.g. "agent"
or "server").
<p>
We need to be able to distinguish different promises from one another
and we do this by defining the <i>promise body</i>, which is
a label on the arrow saying something about what is being promised.
A promise body generally has a promise type or subject and
a choice that is being specified from the set of possible things that
can be promised about that subject. We write this as follows:
<pre>
subject => decision/constraint/compound-attributes
</pre>
<p>
Promises are usually quite complicated and will have several
attributes that make up the complete promise body. e.g.
<pre>
files:
"/home/mark/tmp/testcopy"
copy_from => mycopy("/home/mark/LapTop/words","127.0.0.1"),
perms => system,
depth_search => recurse("inf");
</pre>
<p>
What you should notice about this example is that none of the right
hand sides are actually cfengine keywords, they are all user-defined
templates. Here is one example of such a template:
<pre>
body copyfrom mycopy(from,server)
{
source => "$(from)";
servers => { "$(server)" , "failover_host" };
copy_backup => "true";
purge => "true";
}
</pre>
<p>
This shows a user-defined bundle of attributes that is parameterized
so that it can be re-used. In cfengine 3 you are encouraged to make
libraries of such templates so enrich the expressivity of the basic
language and to simplify the configuration rules.
<p>
Get used to putting quotes around all literal values in cfengine 3.
This is a discipline that allows the compiler to locate errors much more
easily.
<h2>Promise body-aspects common to all</h2>
<p>
Some promise attributes are `common' to any promise, e.g. transaction
details. So any promise can add something of the form
<pre>
action => my_actions_details;
</pre>
where <tt>my_action_details</tt> is the name of a body template,
defined elsewhere. The <tt>action</tt> attribute described the way in
which the resource behaves when it keeps its promises. e.g.
<pre>
body action my_action_details
{
action => "warn";
ifelapsed => "10";
audit => "true";
report_level => "inform";
}
</pre>
<p>
Any promise that includes <tt>action => my_actions_details</tt>
will i) only warn when it is not keeping its promise, not fix the problem automatically,
ii) wait until 10 minutes have elapsed before checking the promise again,
iii) leave a full audit trail of its findings when verifying the promise,
and iv) inform us of any changes that are made.
<p>
Body attributes that are common to all promises include:
<table border=1>
<tr><td>Body lvalue</td><td>Simple/Compound</td><td>Description</td></tr>
<tr><td>comment</td><td>(inline)</td><td> A comment string for internal documentation generation</td></tr>
<tr><td>ifvarclass</td><td>(inline)</td><td> Not used yet.</td></tr>
<tr><td>action</td><td>(body template)</td><td> Aspects controlling the behaviour during the promise verification transaction.</td></tr>
<tr><td>classes</td><td>(body template)</td><td> Defining classes as a result of the outcome or promise verification.</td></tr>
</table>
<h2>Promise bundles</h2>
<p>
Making clearly motivated promises about behaviour can be a potentially
complex thing to do. One way to make promises clearer in a computer
science sense is to make a clean taxonomy of promise subjects or
types. (The word "type" is a bit overloaded in computer science so
let's try to avoid it by using the word "subject".) Cfengine 3 makes
this very powerful by integrating semantic modelling into the langauge
through its new knowledge agent <tt>cfKnow</tt>. But before we get
into those details, it is a good idea to think about how you want to
bundle aspects of management together.
<p>
Before cfengine 3, you only had the option of splitting rules into
different files. Promise bundles now allow you to refer to aspects of
administration in a more sophisticated manner. They also allow
cfengine to give much better and more specific error reporting.
</p>
<p>
A single promise might therefore consist of many subdivisions -- or
smaller promises. We use the term <i>promise bundle</i> (inspired by
geometric fibre bundles or nerve bundles if you prefer) to describe
this. In our research, we have used multiple arrows for such bundles.
In the cfengine language, we simply list related promise bodies after
the promiser:
<pre>
bundle name and details
{
main_subject:
context_for_promise::
"promiser object" -> "promisee"
subsubject_1 => choice_1,
subsubject_2 => choice_2,
...
subsubject_n => choice_n;
...
}
</pre>
This is the general form of a statement in cfengine 3. The form does not
change for different subjects so it is a pattern that pervades
everything. This simplification is the starting point for simpler
configuration descriptions without forced assumptions.
</p>
<h2>Cfengine Mnemonics For Promises</h2>
<p>
So let's write down the cfengine language in its general form.
<ul>
<li>All reserved words are in lower case.</li>
</ul>
The fundamental text is ascii for easy versioning in
SVN, but can be simply compiled into SQL for database
manipulation.
</p>
<h2>Bundles and bodies</h2>
<p>
A promise has the following generic form:
<pre>
context|classes::
"promiser object" -> "promisee"
# constraints
lval1 => rval1,
lval2 => rval2,
...
lvaln => rvaln;
</pre>
A simplified form that is most often used in cfengine omit the
promisee, as this is usually cf-agent itself.
<pre>
context|classes::
"promise object"
# constraints
lval1 => rval1,
lval2 => rval2,
...
lvaln => rvaln;
</pre>
Cfengine arranges promises into "bundles". Sometimes an rvalue
for a given constraint might involve many attributes that are
conveniently grouped together. In this case these are grouped into
a "body" which is simply an aggregate of lval,rval pairs.
<pre>
body name
{
lval1 => rval1;
lval2 => rval2;
}
</pre>
Note the placement of "," and ";" in these.
</p>
<h1>Types in cfengine</h1>
Cfengine 3 introduces datatypes into its language to allow better verification of
promise consistency. There are three kinds of types.
<h2>Bundle types</h2>
Each bundles is addressed to a particular agent or component in cfengine.
This is the "type" of the bundle. The types are:
<ul>
<li>common
<li>agent
<li>server
<li>monitor
<li>executor
<li>knowledge
<li>runagent
</ul>
<h2>Rvalue meta-types</h2>
<p>
An rvalue is something on the right hand side of an assignment.
The cfengine language of promises has four distiguishable meta types of object to remember.
By knowing the difference between these, you will see generic patterns that make syntax
easy to learn.
<ul>
<li> Literal strings e.g. <tt>"some string"</tt></li>
<li> Naked variable values outside of string quotes e.g. <tt>$(scalar)</tt> or <tt>${scalar}</tt> or <tt>@(list)</tt> or <tt>@{list}</tt></li>
<li> Function calls e.g. <tt>isdefined("variable")</tt></li>
<li> Lists e.g. <tt>{ "literal", getuser("101"), @(list), $(scalar) } </tt> (which may contain literal strings and function calls as elements</li>
</ul>
Where lists substituted as rvalues, they must always be enclosed with
curly braces, even if you are substituting a whole-list variable, i.e.
<tt>{ @(list) }</tt> not just <tt>@(list)</tt>, otherwise a warning
will be give about a type mismatch. This is because a list could be of mixed
elements, e.g. <tt>{ @(list), "literal", $(scalar) }</tt>.
<h2>Rvalue data-types</h2>
Literal values can be semantically attributed one of three <i>abstract data types</i>:
<ul>
<li><tt>string</tt></li>
<li><tt>int</tt></li>
<li><tt>real</tt></li>
<li><tt>class</tt></li>
</ul>
The type class is used internally to recognize functions that can be used to set classes
rather than variables.
The corresponding list types for these are
<ul>
<li>slist</li>
<li>ilist</li>
<li>rlist</li>
</ul>
In addition to these basic types there are "opt" variables or menus in which only
a pre-ordained list of selections is a valid rvalue. Cfengine's class identifiers are
also formally a different datatype in cfengine 3.
</p>
<p>
For example
<pre>
vars:
"name" slist = { "one", "2", "three" };
"float" rlist = { "1.0", "2.02", "3.33" };
</pre>
Cfengine validates the values before accepting them.
</p>
Be careful not to confuse meta object types (string, list and function etc) with
abstract types (string,int,real, etc).
<h2>Variables</h2>
Variables in cfengine are local to a <i>context</i>. A variable in the current
context is written
<pre>
$(context)
$(context.name)
</pre>
<p>
Although variables are protected in their contexts, you have access to variables in
any context in any part of cfengine that belongs to a particular agent. Variables
defined in any agent bundle can be accessed by the agent in any other agent bundle,
as long as the full context is specified. If no context is specified then the present
context is assumed.
<p>
Variables defined in common bodies are available to all components of cfengine.
So if you want to share variables, simply define them in a common context, e.g.
<pre>
bundle common v
{
vars:
"global_1" string => "some value";
}
</pre>
Thereafter you can refer to this as <tt>$(v.global_1)</tt> anywhere.
<p>
The context "this" is special. It is a copy of the local context that is augmented
by additional variables. For instance the variable <tt>$(this.promiser)</tt>
should always point to the current object whose promised properties are being
verified. The variables <tt>$(this.0)</tt>, <tt>$(this.1)</tt>, ... etc (or
equivalently <tt>$(0), $(1)</tt>...) represent backreferences to regular
expression matches.
<h2>List substitution</h2>
Because iteration over lists is implicit in cfengine, the semantic
algorithm for handling list variables is important.
<ul>
<li>If a list variable is slotted into a list slot, such as list lval, or as a memer of a list rval, we say that a list has been quenched.
<li>If a list variable is used in the place of a scalar, e.g. a literal string of a scalar lval,
then it is an implied iterator.
</ul>
For example:
<pre>
bundle server main
{
vars:
"list" slist => { "192.168.0.4", "myhost.example.com" };
admit:
"/etc/passwd" -> @(list);
"/etc/services" -> @(list);
# Specific
"/special/$(list)/file -> $(list);
}
</pre>
In the latter case the file
<tt>/special/192.168.0.4/file</tt> is granted to the IP address <tt>192.168.0.4</tt> only,
and <tt>/special/myhost.example.com/file</tt> is granted to <tt>myhost.example.com</tt> only.
Now suppose that we write
<pre>
"/special/$(list)/file -> @(list);
</pre>
This now expands to
<pre>
/special/192.168.0.4/file -> { "192.168.0.4", "myhost.example.com" };
/special/myhost.example.com/file -> { "192.168.0.4", "myhost.example.com" };
</pre>
<h2>Function-like references</h2>
There are two kinds of function-like reference in cfengine 3:
<ul>
<li>Built-in special functions e.g. <tt>randomint("1","4")</tt>
<li>Parameterized body references, e.g. <tt>myaccessbody("077")</tt>
</ul>
The arguments to a function-like reference may be of three types.
<pre>
lval => function("arg1", $(arg2), otherfn("arg3"))
</pre>
<ul>
<li>Literal strings: these should preferably be written in quotes, though single atoms can be written without.
<li>Naked variable values: direct substitution of a variable with no string padding.
<li>Recursive function calls: values returned as a result of other function calls.
</ul>
When a body template receives logical parameters you do not write the "$()" wrapping,
only the identifier name (which is automatically local). e.g.
<pre>
<font color=blue>Call:</font> access => myaccess($(actual)),
<font color=blue>Template:</font> body files myaccess(local)
{
}
</pre>
<h2>Contexts and variables</h2>
This context "this" is always local to the current promise.
The variable
In files promises <tt>$(this.promiser)</tt> is always the name of the
current promising object, even when matching patterns. The aim is to
make this true in all cfengine promises, but this is more complicated
in promises that refer to other kinds of pattern, and less useful.
<h1>Aspects and Service wrappers</h1>
One of the weaknesses of cfengine 2 has been the lack of clear containers for
bundling together related promises, i.e. those which pertain to a particular
aspect of a system, such as provision of a specific service (email, WWW etc),
or security, or backup. There is no unique way of thinking about the different
aspects of a system, so any kind of scheme for organizing promises should be
as flexible as possible and pander to individual tastes.
<p>
In cfengine 3, it is now straightforward to use the concept of a bundle
to represent aspects or parts of aspects. Just as in earlier versions of
cfengine, one can also place aspects in different files.
<p>
We should not think of bundles as private functions in the sense of an
object programming language. Although they can have private variables,
the promises within do not act on private workspace, they can act on
any part of the computer system on which the agent is running. So the
organization is purely formal, for ease of understanding. The
organization or reorganization of promises is entirely cosmetic.
<p>
A final aspect of promise organization is authorization. Because
promises given are not automatically used, unless the recipient
promises to use them, one could split up aspects into files from
different authorized inviduals and place access control on the
possible rules in a file obtained from a given source. Thus one could
deny access to certain users to make certain kinds of promises.
<h1>Some differences in cfengine 3</h1>
<p>
One of the first things you will notice in cfengine 3 is
that the control section has been moved and that there is a new
place to define variables, which is called "vars:".
The reason for this is clarity. In cfengine 2 there is a confusion
between fixed-name identifiers, e.g. <tt>Inform</tt> that set optional
behaviour (the orginal meaning of control) and freely defined
variables that users define and use in rules.
</p>
<p>
In cfengine 3, promises belong to bundles and attributes of promises
belong in bodies. Variables male promises (to associate a name with a value)
so they are placed in bundles:
<pre>
vars:
"identifier" <i>type</i> => "value";
</pre>
Control information is now the province of promise "bodies". A body is
exactly a collection of control attributes, belonging to a promise.
Apart from "control" bodies, all other bodies are tied to user defined
promises. In the case of "control", these are attributes to an implicit
promise by the agent, the server or the monitor, etc.
</p>
<pre>
body agent control
{
access => { "mark", "root" };
}
</pre>
Then there is also a control promise that is common to the entire ensemble.
<pre>
body common control
{
bundlesequence => { update, main, mypromises("2","3") };
inputs => {
"../tests/mod_files.cf",
"../tests/mod_files_copy.cf",
"../tests/mod_exec.cf",
"../tests/mod_process.cf",
"../tests/mod_access.cf"
};
solaris::
# inputs => { "update.cf", "main.cf" , "linux.cf" };
}
</pre>
<h1>Some example configuration</h1>
<pre>
bundle agent main()
{
files:
"/path/file.*"
edit_line => myedit("${this}") ,
access => myaccess,
file_select => myfilter,
changes => tripwire,
recurse => "inf";
"$(filelist)"
edit_xml => insertlist("$(filelist)") ,
edit_line => diddle ,
access => myaccess ,
access => others; # ("white");
"/etc/xyz" -> "cf-agent"
edit_line => myedit("${this}") ,
access => myaccess;
"/usr/local"
linkto => linkdetails("/site/mountpoint/local");
"/var"
recurse => "inf",
name_select=> "fish.*",
tidy => tidymask,
rename => rotateme,
repository => "/override";
}
</pre>
There are many details here to be expanded upon. For that, we
use the body declarations for each promise subject type.
<pre>
#########################################################
body access others(parame)
{
milkyway::
owner => { "root", "wheel", "sudo" };
}
#########################################################
body access myaccess()
{
any::
mode => "+077,-02";
owner => { "mark","siri" };
solaris::
group => readstringlist("filename");
linux::
group => { "root", "wheel" };
}
#########################################################
body linkto linkdetails(tofile)
{
link_type => "symbolic"; # /absolute/abs/hard/relative/rel
copy_patterns => ""; # regex list
deadlinks => "kill"; #/force
when_no_file => "force"; # kill
}
#########################################################
body transaction controlbody
{
loglevel => "usr1";
reportlevel => "inform";
ifelapsed => "10";
expireafter => "20";
}
#########################################################
body changes tripwire
{
hash => "md5";
update => "yes";
}
#########################################################
body file_select myffilter
#
# we can build old "include", "exclude", and "ignore" from these
# as standard patterns
#
{
name => { ".*.asc" }; # regex matching file name
path => { "/var/.*/mail", "/usr/.*/mail" };
mode => "700";
size => irange("10000,10000000");
owner => { "mark", "cell", "motd" };
group => { "ecg", "mark" };
ctime => irange(ondate(2000,1,1,0,0,0),now());
mtime => irange(ago(1,0,0,2,30,0),now);
atime => irange(ondate(1997,2,22,0,0,0),now());
exec_regex => "/usr/bin/file $(this) (.*ascii.*)";
filetypes => { "dir", "link" };
issymlinkto => { "/dev/null", "/dev/tyyS0"};
result => "type&mode";
}
#########################################################
body tidy tidymask
{
age => "0";
size => irange(50000,inf); # number/empty
age_type => "mtime"; #ctime/mtime/atime
dirlinks => "delete"; #keep/tidy/delete
rmdirs => "yes"; #[true/all]/[false/none]/sub
links => "stop"; #stop/keep/traverse/tidy
}
#########################################################
body rename rotateme
{
newname => "filename";
rotate => "4"; # 0 means empty file
size => irange(1,2);
action => "nop"; #disable/warn
}
</pre>
Promises can also be made about entirely abstract matters that do not
require any follow up actions from the strong arm of
cfengine. Unrecognized types are simply ignored, so you can use the
syntax for your own ends (e.g. note-taking with variable expansion)
and perhaps even develop a module to handle such promises as an extension.
<pre>
bundle external ExoCartography()
{
#
# This means nothing, but I can use it to think out loud...
#
graph:
any:: # classes relate to where these promises are known
# not to be confused with the hosts promising
# List the various conduits in the network
# promise from nexus to cube
# Calculate the degree distribution
"sphere" -> "cube"
label => "+nfs",
weight => "0.4";
"sphere" -> "nexus"
label => "+nfs",
weight => "0.4",
# allow all of these simultaneously
s_tau => {},
s_chi => {}, # regex
i_tau => { "1", "2"},
i_chi => {}, # ranges
r_tau => { "0,5"}, # load average
r_chi => { "0,2.2" }; # ranges
"cube" -> { "nexus", "slogans", "others" }
type => "-nfs",
to => "nexus"; # default weight = 1.0
##############################################################
sla:
#
# What can we put in here?
#
"HIO" -> "opera"
tau => "http response time",
real_chi => { "0 , 0.0001" },
str_chi => { "^(NaN)" },
availability => "$(www_avail)";
</pre>
<h1>Roll your own</h1>
<p>
Cfengine 3 has reorganized things to provide fewer but more flexible primitives.
What it gives back is a way to recreate all the old explicitness in a user-defined
way.
<ul>
<li>More powerful variable types and expansion
<li>Unrestricted iteration
<li>Typed templates with variable expansion
</ul>
So although there is no longer anything called
"HashCommentLinesContaining", in "editfiles" you can now create your
own template with this name to implement exactly that function as you see
fit.
<p>
We encourage you to build libraries of templates that you can refer to
to make rules more consistent and readable.
</body>
</html>
|