Skip to content

Commit f94c105

Browse files
rakillenddsharpe
authored andcommitted
Jira wdt 372 merge models with delete notation (#459)
* JIRA WDT-371 - Use variable map for model merge to find matching names * JIRA WDT-372 - Check for delete notation differences when merging models * JIRA WDT-372 - Changed variable name, clarified comments
1 parent 0208519 commit f94c105

File tree

3 files changed

+132
-17
lines changed

3 files changed

+132
-17
lines changed

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Many organizations are using WebLogic Server, with or without other Oracle Fusio
1616
- [Model Names](#model-names)
1717
- [Model Semantics](#model-semantics)
1818
- [Declaring Named MBeans to Delete](#declaring-named-mbeans-to-delete)
19+
- [Using Multiple Models](#using-multiple-models)
1920
- [Administration Server Configuration](site/admin_server.md)
2021
- [Model Security](site/security.md)
2122
- [Modeling Security Providers](site/security_providers.md)
@@ -300,6 +301,54 @@ topology:
300301
ListenPort: 10000
301302
```
302303

304+
If variable properties are used in element names, such as ```@@PROP:my-server@@```, the names in both models will be resolved and matching elements will be merged.
305+
306+
#### Multiple Models and Delete Notation
307+
308+
A named element using [delete notation](#declaring-named-mbeans-to-delete) will completely replace an element with a matching name and no delete notation in a previous model. For example, if Model 1 looks like:
309+
```yaml
310+
topology:
311+
Server:
312+
m1:
313+
ListenPort: 7000
314+
Notes: "Server 1"
315+
```
316+
and Model 2 looks like:
317+
```yaml
318+
topology:
319+
Server:
320+
!m1:
321+
```
322+
The resulting model would be:
323+
```yaml
324+
topology:
325+
Server:
326+
!m1:
327+
```
328+
329+
Similarly, an element without delete notation will completely replace an element with a matching name that has delete notation in a previous model. For example, if Model 1 looks like:
330+
```yaml
331+
topology:
332+
Server:
333+
!m1:
334+
```
335+
and Model 2 looks like:
336+
```yaml
337+
topology:
338+
Server:
339+
m1:
340+
ListenPort: 7000
341+
Notes: "Server 1"
342+
```
343+
The resulting model would be:
344+
```yaml
345+
topology:
346+
Server:
347+
m1:
348+
ListenPort: 7000
349+
Notes: "Server 1"
350+
```
351+
303352
## Downloading and Installing the Software
304353

305354
The Oracle WebLogic Server Deploy Tooling project repository is located at [`https://github.com/oracle/weblogic-deploy-tooling`](https://github.com/oracle/weblogic-deploy-tooling). Binary distributions of the `weblogic-deploy.zip` installer can be downloaded from the [GitHub Releases page](https://github.com/oracle/weblogic-deploy-tooling/releases). To install the software, simply unzip the `weblogic-deploy.zip` installer on a machine that has the desired versions of WebLogic Server installed. After being unzipped, the software is ready to use, just set the `JAVA_HOME` environment variable to point to a Java 7 or higher JDK and the shell scripts are ready to run.

core/src/main/python/wlsdeploy/util/cla_helper.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from wlsdeploy.exception import exception_helper
1111
from wlsdeploy.logging.platform_logger import PlatformLogger
12+
from wlsdeploy.tool.deploy import deployer_utils
1213
from wlsdeploy.util import cla_utils
1314
from wlsdeploy.util import variables
1415
from wlsdeploy.util.cla_utils import CommandLineArgUtil
@@ -144,9 +145,18 @@ def _merge_dictionaries(dictionary, new_dictionary, variable_map):
144145
"""
145146
for new_key in new_dictionary:
146147
new_value = new_dictionary[new_key]
147-
dictionary_key = _find_dictionary_key(dictionary, new_key, variable_map)
148+
dictionary_key, replace_key = _find_dictionary_merge_key(dictionary, new_key, variable_map)
149+
150+
# the key is not in the original dictionary, just add it
148151
if dictionary_key is None:
149152
dictionary[new_key] = new_value
153+
154+
# the new key should replace the existing one - delete the existing key and add the new one
155+
elif replace_key:
156+
del dictionary[dictionary_key]
157+
dictionary[new_key] = new_value
158+
159+
# the key is in both dictionaries - merge if the values are dictionaries, otherwise replace the value
150160
else:
151161
value = dictionary[dictionary_key]
152162
if isinstance(value, dict) and isinstance(new_value, dict):
@@ -155,24 +165,43 @@ def _merge_dictionaries(dictionary, new_dictionary, variable_map):
155165
dictionary[new_key] = new_value
156166

157167

158-
def _find_dictionary_key(dictionary, new_key, variable_map):
168+
def _find_dictionary_merge_key(dictionary, new_key, variable_map):
159169
"""
160-
Find the specified key in the specified dictionary.
161-
If a direct match is not found, and a variable map is specified, perform a more thorough check,
162-
taking into account variable substitution.
170+
Find the key corresponding to new_key in the specified dictionary.
171+
Determine if the new_key should completely replace the value in the dictionary.
172+
If no direct match is found, and a variable map is specified, perform check with variable substitution.
173+
If keys have the same name, but one has delete notation (!server), that is a match, and replace is true.
163174
:param dictionary: the dictionary to be searched
164175
:param new_key: the key being checked
165176
:param variable_map: variables to be used for name resolution, or None
166-
:return: the corresponding key from the dictionary
177+
:return: tuple - the corresponding key from the dictionary, True if dictionary key should be replaced
167178
"""
168179
if new_key in dictionary:
169-
return new_key
180+
return new_key, False
170181

171182
if variable_map is not None:
172-
actual_new_key = variables.substitute_key(new_key, variable_map)
173-
for key in dictionary.keys():
174-
actual_key = variables.substitute_key(key, variable_map)
175-
if actual_key == actual_new_key:
176-
return key
183+
new_is_delete = deployer_utils.is_delete_name(new_key)
184+
match_new_key = _get_merge_match_key(new_key, variable_map)
185+
186+
for dictionary_key in dictionary.keys():
187+
dictionary_is_delete = deployer_utils.is_delete_name(dictionary_key)
188+
match_dictionary_key = _get_merge_match_key(dictionary_key, variable_map)
189+
if match_dictionary_key == match_new_key:
190+
replace_key = new_is_delete != dictionary_is_delete
191+
return dictionary_key, replace_key
177192

178-
return None
193+
return None, False
194+
195+
196+
def _get_merge_match_key(key, variable_map):
197+
"""
198+
Get the key name to use for matching in model merge.
199+
This includes resolving any variables, and removing delete notation if present.
200+
:param key: the key to be examined
201+
:param variable_map: variable map to use for substitutions
202+
:return: the key to use for matching
203+
"""
204+
match_key = variables.substitute_key(key, variable_map)
205+
if deployer_utils.is_delete_name(match_key):
206+
match_key = deployer_utils.get_delete_item_name(match_key)
207+
return match_key

core/src/test/python/wlsdeploy/util/cla_helper_test.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,44 @@ def testMergeNameToProperty(self):
9999

100100
self._check_merged_server(dictionary, '@@PROP:server1a@@')
101101

102+
# an element with delete notation should replace a base element with a matching name and no notation.
103+
def testMergeDeletedNameToName(self):
104+
dictionary = _build_model_one('m1')
105+
new_dictionary = _build_delete_model('m1')
106+
variables = {}
107+
108+
cla_helper._merge_dictionaries(dictionary, new_dictionary, variables)
109+
# print("Merged model: " + str(dictionary))
110+
111+
server = self._check_single_server(dictionary, '!m1')
112+
self.assertEquals(0, len(server), "delete server should have no attributes")
113+
114+
# an element should replace a base element with a matching name and delete notation.
115+
def testMergeNameToDeletedName(self):
116+
dictionary = _build_delete_model('m1')
117+
new_dictionary = _build_model_two('m1')
118+
variables = {}
119+
120+
cla_helper._merge_dictionaries(dictionary, new_dictionary, variables)
121+
# print("Merged model: " + str(dictionary))
122+
123+
server = self._check_single_server(dictionary, 'm1')
124+
self.assertEquals(2, len(server), "server should have two attributes")
125+
102126
# check that a single server exists in the result, and its attributes were merged correctly
103127
def _check_merged_server(self, dictionary, key):
104-
servers = dictionary['Servers']
105-
self.assertEquals(1, len(servers), "there should be one server")
106-
107-
server = servers[key]
128+
server = self._check_single_server(dictionary, key)
108129
self.assertEquals(9001, server['ListenPort'], "m1 should have listen port 9001")
109130
self.assertEquals(5, server['ListenDelaySecs'], "m1 should have listen delay secs 5")
110131
self.assertEquals(7, server['MaxOpenSockCount'], "m1 should have max open sock count 7")
111132

133+
# check that a single server exists in the result, with the specified name
134+
def _check_single_server(self, dictionary, key):
135+
servers = dictionary['Servers']
136+
self.assertEquals(1, len(servers), "there should be one server")
137+
self.assertEquals(True, key in servers, "the single server name should be " + key)
138+
return servers[key]
139+
112140

113141
# model 1 has one server with attributes
114142
def _build_model_one(key):
@@ -134,6 +162,15 @@ def _build_model_two(key):
134162
}
135163

136164

165+
# the delete model 1 has one server with delete notation, and no attributes
166+
def _build_delete_model(key):
167+
return {
168+
"Servers": {
169+
'!' + key: {}
170+
}
171+
}
172+
173+
137174
# variable map maps two keys to the same server name
138175
def _build_variable_map():
139176
return {

0 commit comments

Comments
 (0)