Skip to content

Commit 0d8ad8c

Browse files
rakillenddsharpe
authored andcommitted
Issue 144 remove named objects (#422)
* Issue #144 - Add removal of named objects to the model * Issue #144 - Add removal of named objects to create; improve logging * Issue #144 - Use WLST name for delete * Issue #144 - Add delete capabilities to JNDI properties and group parameters; updated documentation * Refined logic for get_delete_item_name()
1 parent e8a3a06 commit 0d8ad8c

File tree

8 files changed

+160
-13
lines changed

8 files changed

+160
-13
lines changed

README.md

+32-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Many organizations are using WebLogic Server, with or without other Oracle Fusio
1515
- [Simple Example](#simple-example)
1616
- [Model Names](#model-names)
1717
- [Model Semantics](#model-semantics)
18+
- [Declaring Named MBeans to Delete](#declaring-named-mbeans-to-delete)
1819
- [Administration Server Configuration](site/admin_server.md)
1920
- [Model Security](site/security.md)
2021
- [Modeling Security Providers](site/security_providers.md)
@@ -52,7 +53,7 @@ As new use cases are discovered, new tools will likely be added to cover those o
5253

5354
## The Metadata Model
5455

55-
The metadata model (or model, for short) is a version-independent description of a WebLogic Server domain configuration. The tools are designed to support a sparse model so that the model need only describe what is required for the specific operation without describing other artifacts. For example, to deploy an application that depends on a JDBC data source into an existing domain that may contain other applications or data sources, the model needs to describe only the application and the data source in question. If the datasource was previously created, the `deployApps` tool will not try to recreate it but may update part of that data source's configuration if the model description is different than the existing values. If the application was previously deployed, the `deployApps` tool will compare the binaries to determine if the application needs to be redeployed or not. In short, the `deployApps` tool supports an iterative deployment model so there is no need to change the model to remove pieces that were created in a previous deployment.
56+
The metadata model (or model, for short) is a version-independent description of a WebLogic Server domain configuration. The tools are designed to support a sparse model so that the model need only describe what is required for the specific operation without describing other artifacts. For example, to deploy an application that depends on a JDBC data source into an existing domain that may contain other applications or data sources, the model needs to describe only the application and the data source in question. If the data source was previously created, the `deployApps` tool will not try to recreate it but may update part of that data source's configuration if the model description is different than the existing values. If the application was previously deployed, the `deployApps` tool will compare the binaries to determine if the application needs to be redeployed or not.
5657

5758
The model structure, and its folder and attribute names, are based on the WLST 12.2.1.3 offline structure and names with redundant folders removed to keep the model simple. For example, the WLST path to the URL for a JDBC data source is `/JDBCSystemResource/<data-source-name>/JdbcResource/<data-source-name>/JDBCDriverParams/NO_NAME_0/URL`. In the model, it is `resources:/JDBCSystemResource/<data-source-name>/JdbcResource/JDBCDriverParams/URL` (where `resources` is the top-level model section where all WebLogic Server resources/services are described).
5859

@@ -220,6 +221,36 @@ In the example above, the `Target` attribute is specified three different ways,
220221

221222
One of the primary goals of the WebLogic Deploy Tooling is to support a sparse model where the user can specify just the configuration needed for a particular situation. What this implies varies somewhat between the tools but, in general, this implies that the tools are using an additive model. That is, the tools add to what is already there in the existing domain or domain templates (when creating a new domain) rather than making the domain conform exactly to the specified model. Where it makes sense, a similar, additive approach is taken when setting the value of multi-valued attributes. For example, if the model specified the cluster `mycluster` as the target for an artifact, the tooling will add `mycluster` to any existing list of targets for the artifact. While the development team has tried to mark attributes that do not make sense to merge accordingly in our knowledge base, this behavior can be disabled on an attribute-by-attribute basis, by adding an additional annotation in the knowledge base data files. The development team is already thinking about how to handle situations that require a non-additive, converge-to-the-model approach, and how that might be supported, but this still remains a wish list item. Users with these requirements should raise an issue for this support.
222223

224+
### Declaring Named MBeans to Delete
225+
226+
With WebLogic Deploy Tooling release 1.3.0, you can specify named items in the model to be deleted using the Create Domain, Update Domain, and Deploy Applications Tools. Named items are those that have multiple instances that are distinguished by user-provided names, such as managed servers, data sources, and security realms. Items to be deleted are prepended with an exclamation point (!) in the model.
227+
228+
In this example, the managed server ```obsoleteServer``` will be deleted, and ```newServer``` will be created:
229+
230+
```yaml
231+
Server:
232+
!obsoleteServer:
233+
newServer:
234+
ListenAddress: 127.0.0.1
235+
ListenPort: 9005
236+
```
237+
238+
This feature can also remove items that were created by WebLogic Server templates. For example, the base template creates a default security realm called ```myrealm```. If a user chooses to declare a custom realm, ```myrealm``` is no longer needed. In this example, ```myrealm``` will be deleted, and the custom realm ```newrealm``` will be created, and declared as the default realm:
239+
240+
```yaml
241+
SecurityConfiguration:
242+
DefaultRealm: newrealm
243+
Realm:
244+
!myrealm:
245+
newrealm:
246+
AuthenticationProvider:
247+
...
248+
```
249+
250+
This feature does not apply to named security providers within a realm. These items follow a special set of rules that are required to maintain their ordering. See [Modeling Security Providers](site/security_providers.md) for detailed information.
251+
252+
This feature cannot be use to un-deploy applications or remove libraries.
253+
223254
### Using Multiple Models
224255

225256
The Create Domain, Update Domain, Deploy Applications, and Validate Model Tools allow the specification of multiple models on the command line. For example:

core/src/main/antlr4/oracle/weblogic/deploy/yaml/Yaml.g4

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ fragment ID_START
275275
: [_]
276276
| [A-Z]
277277
| [a-z]
278+
| '!'
278279
;
279280

280281
fragment ID_CONTINUE

core/src/main/python/wlsdeploy/tool/create/creator.py

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ def _create_named_mbeans(self, type_name, model_nodes, base_location, log_create
7171
existing_folder_names = self._get_existing_folders(list_path)
7272
for model_name in model_nodes:
7373
name = self.wlst_helper.get_quoted_name_for_wlst(model_name)
74+
if deployer_utils.is_delete_name(name):
75+
deployer_utils.delete_named_element(location, name, existing_folder_names, self.alias_helper)
76+
continue
7477

7578
if token_name is not None:
7679
location.add_name_token(token_name, name)

core/src/main/python/wlsdeploy/tool/deploy/deployer.py

+4
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ def _add_named_elements(self, type_name, model_nodes, location):
8080

8181
token = self.alias_helper.get_name_token(location)
8282
for name in model_nodes:
83+
if deployer_utils.is_delete_name(name):
84+
deployer_utils.delete_named_element(location, name, existing_names, self.alias_helper)
85+
continue
86+
8387
is_add = name not in existing_names
8488
log_helper.log_updating_named_folder(type_name, name, parent_type, parent_name, is_add, self._class_name,
8589
_method_name)

core/src/main/python/wlsdeploy/tool/deploy/deployer_utils.py

+48
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,54 @@ def merge_lists(existing_list, model_list, separator=',', return_as_string=True)
177177
return result
178178

179179

180+
def is_delete_name(name):
181+
"""
182+
Determines if the specified name is flagged for deletion with the "!" prefix.
183+
:param name: the name to be checked
184+
:return: True if the name is prefixed, false otherwise
185+
"""
186+
return name.startswith("!")
187+
188+
189+
def get_delete_item_name(name):
190+
"""
191+
Returns the WLST name of the item to be deleted.
192+
Removes the "!" prefix from the name. An exception is thrown if the name is not prefixed.
193+
:param name: the prefixed model name of the item to be deleted
194+
:return: the model name of the item to be deleted
195+
"""
196+
_method_name = 'get_delete_item_name'
197+
198+
if is_delete_name(name):
199+
return name[1:]
200+
201+
ex = exception_helper.create_deploy_exception('WLSDPLY-09111', name)
202+
_logger.throwing(ex, class_name=_class_name, method_name=_method_name)
203+
raise ex
204+
205+
206+
def delete_named_element(location, delete_name, existing_names, alias_helper):
207+
"""
208+
Delete the specified named element if present. If the name is not present, log a warning and return.
209+
:param location: the location of the element to be deleted
210+
:param delete_name: the name of the element to be deleted, assumed to include the "!" prefix
211+
:param existing_names: a list of names to check against
212+
:param alias_helper: alias helper for lookups
213+
"""
214+
_method_name = 'delete_named_element'
215+
216+
name = get_delete_item_name(delete_name)
217+
type_name = alias_helper.get_wlst_mbean_type(location)
218+
219+
if name not in existing_names:
220+
_logger.warning('WLSDPLY-09109', type_name, name, class_name=_class_name, method_name=_method_name)
221+
else:
222+
_logger.info('WLSDPLY-09110', type_name, name, class_name=_class_name, method_name=_method_name)
223+
type_path = alias_helper.get_wlst_create_path(location)
224+
_wlst_helper.cd(type_path)
225+
_wlst_helper.delete(name, type_name)
226+
227+
180228
def ensure_no_uncommitted_changes_or_edit_sessions():
181229
"""
182230
Ensure that the domain does not contain any uncommitted changes and there is no existing edit session.

core/src/main/python/wlsdeploy/tool/deploy/jms_resources_deployer.py

+65-12
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ def _add_jndi_properties(self, property_name_nodes, location):
138138
if len(property_name_nodes) == 0:
139139
return
140140

141+
# use a copy of the dictionary to remove items as they are deleted
142+
remaining_name_nodes = property_name_nodes.copy()
143+
141144
parent_type, parent_name = self.get_location_type_and_name(location)
142145
is_online = self.wlst_mode == WlstModes.ONLINE
143146
if is_online and deployer_utils.is_in_resource_group_or_template(location):
@@ -151,10 +154,16 @@ def _add_jndi_properties(self, property_name_nodes, location):
151154
name_attribute = self.alias_helper.get_wlst_attribute_name(properties_location, KEY)
152155
mbean_type = self.alias_helper.get_wlst_mbean_type(properties_location)
153156

157+
for property_name in property_name_nodes:
158+
if deployer_utils.is_delete_name(property_name):
159+
name = deployer_utils.get_delete_item_name(property_name)
160+
self._delete_mapped_mbean(properties_location, properties_token, mbean_type, name_attribute, name)
161+
del remaining_name_nodes[property_name]
162+
154163
# loop once to create and name any missing folders.
155164
folder_map = self._build_folder_map(properties_location, properties_token, name_attribute)
156165

157-
for property_name in property_name_nodes:
166+
for property_name in remaining_name_nodes:
158167
folder_name = dictionary_utils.get_element(folder_map, property_name)
159168
if folder_name is None:
160169
self.wlst_helper.cd(foreign_server_path)
@@ -164,7 +173,7 @@ def _add_jndi_properties(self, property_name_nodes, location):
164173
# loop a second time to set attributes
165174
new_folder_map = self._build_folder_map(properties_location, properties_token, name_attribute)
166175

167-
for property_name in property_name_nodes:
176+
for property_name in remaining_name_nodes:
168177
is_add = property_name not in folder_map
169178
log_helper.log_updating_named_folder(JNDI_PROPERTY, property_name, parent_type, parent_name, is_add,
170179
self._class_name, _method_name)
@@ -173,7 +182,7 @@ def _add_jndi_properties(self, property_name_nodes, location):
173182
properties_location.add_name_token(properties_token, folder_name)
174183
self.wlst_helper.cd(self.alias_helper.get_wlst_attributes_path(properties_location))
175184

176-
property_nodes = property_name_nodes[property_name]
185+
property_nodes = remaining_name_nodes[property_name]
177186
self.set_attributes(properties_location, property_nodes)
178187

179188
def _add_group_params(self, group_name_nodes, location):
@@ -188,6 +197,9 @@ def _add_group_params(self, group_name_nodes, location):
188197
if len(group_name_nodes) == 0:
189198
return
190199

200+
# use a copy of the dictionary to remove items as they are deleted
201+
remaining_name_nodes = group_name_nodes.copy()
202+
191203
parent_type, parent_name = self.get_location_type_and_name(location)
192204
template_path = self.alias_helper.get_wlst_subfolders_path(location)
193205
groups_location = LocationContext(location)
@@ -196,11 +208,20 @@ def _add_group_params(self, group_name_nodes, location):
196208
name_attribute = self.alias_helper.get_wlst_attribute_name(groups_location, SUB_DEPLOYMENT_NAME)
197209
mbean_type = self.alias_helper.get_wlst_mbean_type(groups_location)
198210

211+
for group_name in group_name_nodes:
212+
if deployer_utils.is_delete_name(group_name):
213+
group_nodes = group_name_nodes[group_name]
214+
name = deployer_utils.get_delete_item_name(group_name)
215+
sub_name = self._get_subdeployment_name(group_nodes, name)
216+
self._delete_mapped_mbean(groups_location, groups_token, mbean_type, name_attribute, sub_name)
217+
del remaining_name_nodes[group_name]
218+
199219
# loop once to create and name any missing folders.
200220
folder_map = self._build_folder_map(groups_location, groups_token, name_attribute)
201221

202-
for group_name in group_name_nodes:
203-
sub_deployment_name = self._get_subdeployment_name(group_name_nodes, group_name)
222+
for group_name in remaining_name_nodes:
223+
group_nodes = remaining_name_nodes[group_name]
224+
sub_deployment_name = self._get_subdeployment_name(group_nodes, group_name)
204225
folder_name = dictionary_utils.get_element(folder_map, sub_deployment_name)
205226
if folder_name is None:
206227
self.wlst_helper.cd(template_path)
@@ -210,19 +231,52 @@ def _add_group_params(self, group_name_nodes, location):
210231
# loop a second time to set attributes
211232
new_folder_map = self._build_folder_map(groups_location, groups_token, name_attribute)
212233

213-
for group_name in group_name_nodes:
214-
sub_deployment_name = self._get_subdeployment_name(group_name_nodes, group_name)
234+
for group_name in remaining_name_nodes:
235+
group_nodes = remaining_name_nodes[group_name]
236+
sub_deployment_name = self._get_subdeployment_name(group_nodes, group_name)
215237
is_add = sub_deployment_name not in folder_map
216238
log_helper.log_updating_named_folder(GROUP_PARAMS, group_name, parent_type, parent_name, is_add,
217239
self._class_name, _method_name)
218240

219241
folder_name = dictionary_utils.get_element(new_folder_map, sub_deployment_name)
220242
groups_location.add_name_token(groups_token, folder_name)
221243
self.wlst_helper.cd(self.alias_helper.get_wlst_attributes_path(groups_location))
222-
223-
group_nodes = group_name_nodes[group_name]
224244
self.set_attributes(groups_location, group_nodes)
225245

246+
def _delete_mapped_mbean(self, folder_location, folder_token, mbean_type, name_attribute, name):
247+
"""
248+
Delete the child MBean with an attribute value that matches the specified name.
249+
This requires looping through the child MBeans for each deletion, since the MBean names change on delete.
250+
:param folder_location: the WLST location for the folder with named sub-elements
251+
:param folder_token: the folder token used to iterate through the MBean names
252+
:param mbean_type: the MBean type of the sub-elements
253+
:param name_attribute: the attribute for the name in each sub-element
254+
:param name: the name of the sub-element to be deleted
255+
"""
256+
_method_name = '_delete_mapped_mbean'
257+
258+
original_path = self.wlst_helper.get_pwd()
259+
mapped_folder_name = None
260+
261+
folder_names = deployer_utils.get_existing_object_list(folder_location, self.alias_helper)
262+
for folder_name in folder_names:
263+
folder_location.add_name_token(folder_token, folder_name)
264+
self.wlst_helper.cd(self.alias_helper.get_wlst_attributes_path(folder_location))
265+
attribute_value = self.wlst_helper.get(name_attribute)
266+
if attribute_value == name:
267+
mapped_folder_name = folder_name
268+
break
269+
270+
self.wlst_helper.cd(original_path)
271+
272+
if mapped_folder_name is None:
273+
self.logger.warning('WLSDPLY-09109', mbean_type, name,
274+
class_name=self._class_name, method_name=_method_name)
275+
else:
276+
self.logger.info('WLSDPLY-09110', mbean_type, name,
277+
class_name=self._class_name, method_name=_method_name)
278+
self.wlst_helper.delete(mapped_folder_name, mbean_type)
279+
226280
def _build_folder_map(self, folder_location, folder_token, name_attribute):
227281
"""
228282
Build a map of existing sub-element names to folders.
@@ -245,15 +299,14 @@ def _build_folder_map(self, folder_location, folder_token, name_attribute):
245299
self.wlst_helper.cd(original_path)
246300
return folder_map
247301

248-
def _get_subdeployment_name(self, group_name_nodes, group_name):
302+
def _get_subdeployment_name(self, group_nodes, group_name):
249303
"""
250304
Returns the derived sub-deployment name for the specified group name.
251305
The sub-deployment name attribute is returned if specified, otherwise the group name is returned.
252-
:param group_name_nodes: the group name nodes from the model
306+
:param group_nodes: the group nodes from the model
253307
:param group_name: the group name being examined
254308
:return: the derived sub-deployment name
255309
"""
256-
group_nodes = group_name_nodes[group_name]
257310
sub_deployment_name = dictionary_utils.get_element(group_nodes, SUB_DEPLOYMENT_NAME)
258311
if sub_deployment_name is not None:
259312
return sub_deployment_name

core/src/main/python/wlsdeploy/tool/util/topology_helper.py

+4
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ def create_placeholder_named_elements(self, location, model_type, model_nodes):
113113

114114
name_nodes = dictionary_utils.get_dictionary_element(model_nodes, model_type)
115115
for name in name_nodes:
116+
if deployer_utils.is_delete_name(name):
117+
# don't create placeholder for delete names
118+
continue
119+
116120
if name not in existing_names:
117121
self.logger.info('WLSDPLY-19403', model_type, name, class_name=self.__class_name,
118122
method_name=_method_name)

core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties

+3
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,9 @@ WLSDPLY-09106=Shared library name {0} contained {1} @ signs when only zero or on
936936
WLSDPLY-09107=Shared library name {0} contained {1} # signs when only zero or one are allowed
937937
WLSDPLY-09108=Model attribute {0} at model location {1} with value {2} references a location inside \
938938
the archive file but the archive file was not provided
939+
WLSDPLY-09109=Unable to delete {0} {1}, name does not exist
940+
WLSDPLY-09110=Deleting {0} {1}
941+
WLSDPLY-09111=Unable to get delete item name for {0}
939942

940943
# wlsdeploy/tool/deploy/deployer.py
941944
WLSDPLY-09200=Error setting attribute {0} for {1} {2}: {3}

0 commit comments

Comments
 (0)