forked from bserdar/jcliff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
README
427 lines (344 loc) · 14.8 KB
/
README
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
EAP6 configuration is difficult if you intend to use puppet. The
command line client provides an interactive front-end for
configuration tasks, not easily called from puppet scripts. There is
no easy way to retrieve what's already configured, find the
differences between the current state and the desired state, and come
up with a way to implement those. Jcliff does exactly that. User
supplies the desired configuration, jcliff executes the jboss client
to retrieve the current state, derive deltas, and apply them. What
kind of delta results in what kind of action is defined using a
property file based rule language.
JBoss configuration model:
The JBoss DMR library is used to represent jboss configuration in a
hierarchical markup language.
Look at this logging configuration fragment as example:
> /subsystem=logging:read-resource(recursive=true)
{
"outcome" => "success",
"result" => {
"custom-handler" => undefined,
"async-handler" => {
"blah" => {
"encoding" => undefined,
"filter" => undefined,
"formatter" => "%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n",
"level" => undefined,
"overflow-action" => "BLOCK",
"queue-length" => 1000,
"subhandlers" => undefined
},
"ASYNC" => {
"encoding" => undefined,
"filter" => undefined,
"formatter" => "%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n",
"level" => undefined,
"overflow-action" => "BLOCK",
"queue-length" => 1000,
"subhandlers" => ["FILE"]
}
},
...
}
}
The command
/subsystem=logging:read-resource(recursive=true)
is parsed as follows:
- Select the subsytem whose name is logging. That's a node in the
configuration tree
- :read-resource is an operation defined on that node. Invoke the
operation with attributes (recursive=true)
As for the return result:
- Anything between { and } is an object. So the return result is an
object containing two items, "outcome" and "result".
- "result" is another object, containing custom-handler,
async-handler, etc. all of which are objects
- async-handler contains objects ASYNC and blah.
- ASYNC/subhandlers is a list of strings. Anything between [ and ]
are lists.
Every node in the configuration tree defines a set of operations that
can be run on that node. For instance, to add a new async-handler, you
have to invoke:
/subsystem=logging/async-handler=newHandler:add(queue-length=someNumber)
Here's the primary reasons why it is not easy to automate the
configuration tasks:
- Every node defines a separate set of operations.
- Every operation has a different set of required parameters
That is, there is no standard way of adding/removing nodes to the
configuration tree. For instance, to add an element to the handlers
list of a logger, you have to call assign-subhandler on that node. To
assign a handler to root logger, you have to call
root-logger-assign-subhandler on that root logger.
/subsystem is not a universal prefix. That would be too easy. For
instance, to get system properties:
/core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)
Puppet configuration model:
The idea is to have puppet lay configuration files to a given
directory, and then run jcliff on those files. Jcliff loads the
puppetized configuration files, talks to JBoss, determines what needs
to be changed, and changes them. Each puppetized configuration file
has to tell what it is configuring. For instance, a logging
configuration file looks like:
{ "logging" =>
{
"async-handler" => {
"ASYNC" => {
"subhandlers" => [ "FILE" ],
"queue-length" => 1000,
},
},
"size-rotating-file-handler" => {
"SFILE" => { }
}
}
}
...
Similarly, jdbc drivers:
{ "jdbc-driver" =>
{ "oracle" => {
"driver-name" => "oracle",
"driver-module-name" => "oracle.jdbcx",
"driver-xa-datasource-class-name" => "oracle.jdbc.XADataSource"
}
}
}
Datasources:
{
"datasource" => {
"BSProduct" => {
"jndi-name" => "java:/BSProduct",
"connection-url" => "jdbc:oracle:oci@web",
"driver-name" => "oracle",
"user-name" => "web",
"password" => "web_dev0",
}
}
}
System properties:
{ "system-property" => {
"foo" => "bar",
"bah" => "gah"
}
}
Jcliff does not delete anything from the existing configuration unless
explicitly required. Therefore, not specifying certain properties of
objects will leave them untouched. If you want to delete them, assign
objects/values to "deleted", or undefine them, by assigning them to
undefined.
Deployments:
Jcliff can be used to deploy applications. After applying all the
configuration changes, Jcliff attempts to process deployments of the form:
{ "deployments" => {
"myApp-v2.1.ear" => {
"NAME" => "myApp-v2.1.ear",
"path" => "/var/lib/redhat/deploy/myApp-v2.1.ear",
"replace-name-regex" => "\\QmyApp-v\\E\\..*",
}
}
}
Jcliff runs "deploy -l" to retrieve the list of deployed packages. The
idea is to have Jcliff first undeploy older versions of the same
application, and then to redeploy the new version. The
replace-name-regex and replace-runtime-name-regex regular expressions
are used to locate applications that the new application will
replace. Any applications whose name matches replace-name-regex, or
whose runtime name matches replace-runtime-name-regex will be
undeployed before redeploying the new application. In the above
example, myApp-v2.1.ear would replace an existing mpApp-v2.0.ear.
If myApp-v2.1.ear already is deployed, Jcliff will not attempt to
redeploy it, unless --redeploy flag is passed. So, after deploying all
the applications, running Jcliff without the --redeploy flag with the
existing deployment list will not alter any of the deployments.
The delta and the rules:
Jcliff reads all configuration files, and builds a list of
paths. Every value in the configuration tree is represented by a path
containing all the object names up to that value. For instance:
{ "system-property" => {
"foo" => "bar",
"bah" => "gah"
}
}
Paths:
system-property
system-property/foo
system-property/bah
For the datasource example:
{
"datasource" => {
"BSProduct" => {
"jndi-name" => "java:/BSProduct",
"connection-url" => "jdbc:oracle:oci@web",
"driver-name" => "oracle",
"user-name" => "web",
"password" => "web_dev0",
}
}
}
Paths:
/datasource
/datasource/BSProduct
/datasource/BSProduct/jndi-name
/datasource/BSProduct/connection-url
/datasource/BSProduct/driver-name
/datasource/BSProduct/user-name
/datasource/BSProduct/password
Same thing is also done for the configurations retrieved from JBoss. For instance, when datasource configuration is read,
"result" => {
"ExampleDS" => {
"allocation-retry" => undefined,
"allocation-retry-wait-millis" => undefined,
"allow-multiple-users" => undefined,
"background-validation" => undefined,
"background-validation-millis" => undefined,
"blocking-timeout-wait-millis" => undefined,
"check-valid-connection-sql" => undefined,
"connection-properties" => undefined,
"connection-url" => "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
"datasource-class" => undefined,
"driver-class" => undefined,
"driver-name" => "h2",
"enabled" => true,
"exception-sorter-class-name" => undefined,
"exception-sorter-properties" => undefined,
"flush-strategy" => undefined,
"idle-timeout-minutes" => undefined,
"jndi-name" => "java:jboss/datasources/ExampleDS",
"jta" => true,
"max-pool-size" => undefined,
"min-pool-size" => undefined,
"new-connection-sql" => undefined,
"password" => "sa",
"pool-prefill" => undefined,
"pool-use-strict-min" => undefined,
"prepared-statements-cache-size" => undefined,
"query-timeout" => undefined,
"reauth-plugin-class-name" => undefined,
"reauth-plugin-properties" => undefined,
"security-domain" => undefined,
"set-tx-query-timeout" => false,
"share-prepared-statements" => false,
"spy" => false,
"stale-connection-checker-class-name" => undefined,
"stale-connection-checker-properties" => undefined,
"track-statements" => "NOWARN",
"transaction-isolation" => undefined,
"url-delimiter" => undefined,
"url-selector-strategy-class-name" => undefined,
"use-ccm" => true,
"use-fast-fail" => false,
"use-java-context" => true,
"use-try-lock" => undefined,
"user-name" => "sa",
"valid-connection-checker-class-name" => undefined,
"valid-connection-checker-properties" => undefined,
"validate-on-match" => false,
"statistics" => {
"jdbc" => undefined,
"pool" => undefined
}
}
}
Jcliff uses the value of "result", that is, the object containing the
"ExampleDS". Some hacking is required here, because the configuration
tree starts with "datasource", but the JBoss tree does not. We either
have to remove "datasource" from the configuration tree, or add
"datasource" to JBoss tree. The definition of datasource rules has
this property:
server.preprocess.prepend=/datasource
That is, once loaded the jboss configuration is converted into:
"datasource" => {
"ExampleDS" => {...}
}
All four options are possible:
server.preprocess.strip
server.preprocess.prepend
client.preprocess.strip
client.preprocess.prepend
These directives either insert, or remove levels from the client or
server configuration tree.
After the preprocessing, the paths are built, and a difference is
computed. The differences are:
- add: Add a new configuration item to JBoss tree
- modify: Modify an existing item in JBoss tree. Only the leaf
modified paths appear in the delta. That is, if a datasource
attribute is modified, the datasource node itself is not in the
delta, only the modified attribute is.
- remove: Remove an item from JBoss tree. This is done by assigning
the value of an object to "deleted".
- undefine: Undefine item in JBoss tree.
- listAdd: Add a new element in a list in JBoss tree.
- listRemove: Remove an element from a JBoss list.
Hack: you can't add a nontrivial object, and set its attributes. The
attributes of the nontrivial object don't exist until the object is
created, which results in attribute add operations. Attributes cannot
be added, only modified. So,
1) You have to run the rules that add objects before the ones that
modify the objects properties
2) Once you add a nontrivial object, you have to refresh the JBoss
configuration tree, so that you have a representation of the
existing configuration with all the default values of the newly
added object.
So, after adding a new object, you have to re-read the relevant
configuration from JBoss. This is done by the "refresh" directive. For
instance:
match.addConsoleHandler=add:/console-handler/*
addConsoleHandler.precedence=50
addConsoleHandler.rule=/subsystem=logging/console-handler:${name(.)}:add
addConsoleHandler.refresh=true
match.modifyConsoleHandler=modify:/console-handler/*/*
modifyConsoleHandler.precedence=55
modifyConsoleHandler.rule=/subsystem=logging/console-handler=${name(..)}:write-attribute(name=${name(.)},value=${value(.)})
addConsoleHandler will add a new console handler by only specifying
its name. Once added, all the attributes of the new handler will be
set to their default values. Jcliff will re-read the configuration,
retrieving all the attributes. Then, attribute modification rules are
run. Rules with lower precedence value run before rules with higher
precedence value.
Now the rules themselves:
- The configurable subsytems are defined in the rules file:
configurable.1=system-properties
configurable.2=logging
configurable.3=jdbc-driver
configurable.4=datasource
configurable.5=xadatasource
Each configurable defines a rule file defining the rules to deal with
that particular configurable. The explicit ordering defines the order
in which the subsystems will be configured.
Each rule file contains at least the following:
name=xadatasource
getContents=/subsystem=datasources:read-children-resources(child-type=xa-data-source)
server.preprocess.prepend=/xadatasource
- name: name of the configurable
- getContents: The statement to run to retrieve the contents of this
configurable from JBoss
- preprocessing directives: optional
server/client.preprocess.prepend/strip, defining what to add/remove
from the server/client configuration so that a meaningfull delta can
be computed. One server and one client operation can be specified,
but you can't specify two server or client operations.
Each rule is defined using a "match" property:
match.addDatasource=add:/datasource/*
This match property defines a rule name "addDatasource". It matches
the node on the delta where a path of the form /datasource/<Name> is
added to the JBoss configuration. That is, a datasource exists in the
puppetized configuration, but not in JBoss. So, you define how to add
a new datasource:
addDatasource.rule.1=data-source add --name=${name(.)} --jndi-name=${value(jndi-name)} --driver-name=${value(driver-name)} --connection-url=${value(connection-url)}
addDatasource.rule.2=data-source enable --name=${name(.)}
addDatasource.refresh=true
The construct ${name(<path>)} evaluates to the name of the last
element in <path>. Path can be relative. A relative path is evaluated
with respect to the matched node. Above, if the matching node is
/datasource/BSProduct, ${name(.)} evaluates to BSProduct.
The construct ${value(<path>)} evaluates to the value of the node
denoted by <path>. In the above example, the expression
${value(jndi-name)} evaluates to the content of the path
/datasource/BSProduct/jndi-name, which should give the JNDI name of
the datasource in the puppetized configuration.
Multiple commands can be provided in a rule. In the above example,
addDatasource.rule.1 will create the datasource, and
addDatasource.rule.2 will enable it.
addDatasource.refresh will reload the JBoss configuration for
datasources. This is required after an add operation. The refresh
configuration will have all the datasource attributes initialized to
their default values, and any datasource attribute modification rule
will match after a refresh.