Skip to content

Commit 9d4a8d0

Browse files
Merge pull request #1017 from oracle/develop-filters
Avoid multiple server ports in cluster, remove online attributes for WKO
2 parents 0279c45 + f2d568b commit 9d4a8d0

File tree

19 files changed

+469
-44
lines changed

19 files changed

+469
-44
lines changed

core/src/main/python/validate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def __perform_model_file_validation(model_file_name, model_context):
122122
# substitute variables before filtering
123123
variables.substitute(model_dictionary, variable_map, model_context)
124124
# apply filters to merged model
125-
if filter_helper.apply_filters(model_dictionary, "validate"):
125+
if filter_helper.apply_filters(model_dictionary, "validate", model_context):
126126
# persist model after filtering
127127
cla_helper.persist_model(model_context, model_dictionary)
128128

core/src/main/python/wlsdeploy/aliases/model_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
AUDITOR = 'Auditor'
4242
AUTHENTICATION_PROVIDER = 'AuthenticationProvider'
4343
AUTHORIZER = 'Authorizer'
44+
CALCULATED_LISTEN_PORTS = 'CalculatedListenPorts'
4445
CAPACITY = 'Capacity'
4546
CDI_CONTAINER = 'CdiContainer'
4647
CERT_PATH_PROVIDER = 'CertPathProvider'

core/src/main/python/wlsdeploy/exception/exception_helper.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
ExceptionType.DISCOVER: 'create_discover_exception',
4141
ExceptionType.ENCRYPTION: 'create_encryption_exception',
4242
ExceptionType.JSON: 'create_json_exception',
43+
ExceptionType.PREPARE: 'create_prepare_exception',
4344
ExceptionType.PY_WLST: 'create_pywlst_exception',
4445
ExceptionType.TRANSLATE: 'create_translate_exception',
4546
ExceptionType.VALIDATE: 'create_validate_exception',

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

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""
2-
Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. All rights reserved.
2+
Copyright (c) 2017, 2021, Oracle Corporation and/or its affiliates. All rights reserved.
33
Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
44
"""
55
import imp
66
import os
77
import sys
88

99
from wlsdeploy.logging.platform_logger import PlatformLogger
10+
from wlsdeploy.tool.util.filters import wko_filter
1011
from wlsdeploy.util import dictionary_utils
1112
from wlsdeploy.util import path_utils
1213
from wlsdeploy.util.model_translator import FileToPython
@@ -17,30 +18,30 @@
1718
TARGET_CONFIG_TOKEN = '@@TARGET_CONFIG_DIR@@'
1819

1920
__id_filter_map = {
20-
# 'filterId': filter_method
21+
'wko_filter': wko_filter.filter_model
2122
}
2223

2324

24-
def apply_filters(model, tool_type, model_context=None):
25+
def apply_filters(model, tool_type, model_context):
2526
"""
2627
Apply any filters configured for the specified tool type to the specified model.
2728
:param model: the model to be filtered
2829
:param tool_type: the name of the filter tool type
29-
:param model_context: optional, used to find target filters
30+
:param model_context: used to find target filters
3031
:return: True if any filter was applied, False otherwise
3132
:raises: BundleAwareException of the specified type: if an error occurs
3233
"""
3334
_method_name = 'apply_filters'
34-
global __filter_file_location
3535

36-
__filter_file_location = path_utils.find_config_path('model_filters.json')
36+
__filter_file_location = None
3737
filter_applied = False
3838

3939
try:
4040
filters_dictionary = {}
4141

4242
# if target specified in model context, use the filters from target config
4343
if model_context and model_context.get_target():
44+
__filter_file_location = model_context.get_target_configuration_file()
4445
filters_dictionary = model_context.get_target_configuration().get_model_filters()
4546
target_path = os.path.join('targets', model_context.get_target())
4647

@@ -52,15 +53,18 @@ def apply_filters(model, tool_type, model_context=None):
5253
filter_path = target_path + filter_path.replace(TARGET_CONFIG_TOKEN, '')
5354
current_filter['path'] = path_utils.find_config_path(filter_path)
5455

55-
elif os.path.isfile(__filter_file_location):
56-
filters_dictionary = FileToPython(__filter_file_location).parse()
5756
else:
58-
__logger.info('WLSDPLY-20017', __filter_file_location, class_name=__class_name, method_name=_method_name)
57+
__filter_file_location = path_utils.find_config_path('model_filters.json')
58+
if os.path.isfile(__filter_file_location):
59+
filters_dictionary = FileToPython(__filter_file_location).parse()
60+
else:
61+
__logger.info('WLSDPLY-20017', __filter_file_location, class_name=__class_name,
62+
method_name=_method_name)
5963

6064
if tool_type in filters_dictionary:
6165
filter_list = filters_dictionary[tool_type]
6266
for filter in filter_list:
63-
filter_applied = _apply_filter(model, filter) or filter_applied
67+
filter_applied = _apply_filter(model, filter, model_context, __filter_file_location) or filter_applied
6468
else:
6569
__logger.info('WLSDPLY-20016', tool_type, __filter_file_location, class_name=__class_name,
6670
method_name=_method_name)
@@ -71,34 +75,39 @@ def apply_filters(model, tool_type, model_context=None):
7175
return filter_applied
7276

7377

74-
def _apply_filter(model, the_filter):
78+
def _apply_filter(model, the_filter, model_context, filter_file_location):
7579
"""
7680
Apply the specified filter to the specified model.
7781
:param model: the model to be filtered
7882
:param the_filter: a dictionary containing the filter parameters
83+
:param model_context: may be used by internal (ID) filters
84+
:param filter_file_location: used for logging
7985
:return: True if the specified filter was applied, False otherwise
8086
:raises: BundleAwareException of the specified type: if an error occurs
8187
"""
8288
_method_name = '_apply_filter'
83-
global __filter_file_location
8489

8590
id = dictionary_utils.get_element(the_filter, 'id')
8691
if id is not None:
87-
return _apply_id_filter(model, id)
92+
__logger.info('WLSDPLY-20034', id, class_name=__class_name, method_name=_method_name)
93+
return _apply_id_filter(model, id, model_context)
8894

8995
path = dictionary_utils.get_element(the_filter, 'path')
9096
if path is not None:
97+
name = dictionary_utils.get_element(the_filter, 'name')
98+
__logger.info('WLSDPLY-20033', name, class_name=__class_name, method_name=_method_name)
9199
return _apply_path_filter(model, path)
92100

93-
__logger.severe('WLSDPLY-20019', str(__filter_file_location), class_name=__class_name, method_name=_method_name)
101+
__logger.severe('WLSDPLY-20019', str(filter_file_location), class_name=__class_name, method_name=_method_name)
94102
return False
95103

96104

97-
def _apply_id_filter(model, id):
105+
def _apply_id_filter(model, id, model_context):
98106
"""
99107
Apply the specified ID filter to the specified model.
100108
:param model: the model to be filtered
101109
:param id: the ID of the filter to be applied
110+
:param model_context: may be used by filters
102111
:return: True if the specified filter was applied, False otherwise
103112
:raises: BundleAwareException of the specified type: if an error occurs
104113
"""
@@ -109,7 +118,7 @@ def _apply_id_filter(model, id):
109118
__logger.severe('WLSDPLY-20020', str(id), class_name=__class_name, method_name=_method_name)
110119
return False
111120
else:
112-
filter_method(model)
121+
filter_method(model, model_context)
113122
return True
114123

115124

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
Copyright (c) 2021, Oracle Corporation and/or its affiliates.
3+
Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
4+
"""
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright (c) 2021, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
from wlsdeploy.aliases.aliases import Aliases
5+
from wlsdeploy.aliases.location_context import LocationContext
6+
from wlsdeploy.aliases.model_constants import APP_DEPLOYMENTS
7+
from wlsdeploy.aliases.model_constants import DOMAIN_INFO
8+
from wlsdeploy.aliases.model_constants import RESOURCES
9+
from wlsdeploy.aliases.model_constants import TOPOLOGY
10+
from wlsdeploy.logging.platform_logger import PlatformLogger
11+
12+
_class_name = 'ModelTraverse'
13+
14+
15+
class ModelTraverse:
16+
"""
17+
This class traverses all model folders and attributes.
18+
Sub-classes can override specific methods to avoid re-implementing the recursive traversal.
19+
"""
20+
def __init__(self, model_context, wlst_mode, exception_type):
21+
self._aliases = Aliases(model_context=model_context, wlst_mode=wlst_mode, exception_type=exception_type)
22+
self._logger = PlatformLogger('wlsdeploy.model_traverse')
23+
24+
def traverse_model(self, root_dict):
25+
section_names = [DOMAIN_INFO, TOPOLOGY, RESOURCES, APP_DEPLOYMENTS]
26+
for section_name in section_names:
27+
self.traverse_section(root_dict, section_name)
28+
29+
def traverse_section(self, model_dict, section_name):
30+
if section_name not in model_dict.keys():
31+
return
32+
33+
# only specific top-level sections have attributes
34+
valid_attribute_names = []
35+
attribute_location = self._aliases.get_model_section_attribute_location(section_name)
36+
if attribute_location is not None:
37+
valid_attribute_names = self._aliases.get_model_attribute_names_and_types(attribute_location)
38+
39+
model_location = LocationContext()
40+
model_section_dict = model_dict[section_name]
41+
valid_folder_names = self._aliases.get_model_section_top_level_folder_names(section_name)
42+
self.traverse_node_elements(model_section_dict, model_location, valid_folder_names, valid_attribute_names)
43+
44+
def traverse_folder(self, model_node, model_location):
45+
"""
46+
Traverse a folder that may have named sub-folders (such as Server), artificial named sub-folders (such
47+
as a security provider type), or its own sub-folders and attributes (such as JTA).
48+
"""
49+
50+
# avoid checking folder type if is_artificial_type_folder
51+
is_multiple = not self._aliases.is_artificial_type_folder(model_location) and \
52+
(self._aliases.supports_multiple_mbean_instances(model_location) or
53+
self._aliases.requires_artificial_type_subfolder_handling(model_location))
54+
55+
if is_multiple:
56+
for name in model_node:
57+
expanded_name = name
58+
new_location = LocationContext(model_location)
59+
name_token = self._aliases.get_name_token(new_location)
60+
if name_token is not None:
61+
new_location.add_name_token(name_token, expanded_name)
62+
value_dict = model_node[name]
63+
self.traverse_node(value_dict, new_location)
64+
else:
65+
name_token = self._aliases.get_name_token(model_location)
66+
if name_token is not None:
67+
name = model_location.get_name_for_token(name_token)
68+
if name is None:
69+
name = '%s-0' % name_token
70+
model_location.add_name_token(name_token, name)
71+
self.traverse_node(model_node, model_location)
72+
73+
def traverse_node(self, model_node, model_location):
74+
valid_folder_names = self._aliases.get_model_subfolder_names(model_location)
75+
valid_attribute_names = self._aliases.get_model_attribute_names(model_location)
76+
self.traverse_node_elements(model_node, model_location, valid_folder_names, valid_attribute_names)
77+
78+
def traverse_node_elements(self, model_node, model_location, valid_folder_names, valid_attribute_names):
79+
"""
80+
Traverse a node that contains only attributes, non-named sub-folders, and artificial type folders.
81+
"""
82+
83+
# use items(), not iteritems(), to avoid ConcurrentModificationException if node is modified
84+
for key, value in model_node.items():
85+
if key in valid_folder_names:
86+
new_location = LocationContext(model_location).append_location(key)
87+
self.traverse_folder(value, new_location)
88+
89+
elif key in valid_attribute_names:
90+
self.traverse_attribute(model_node, key, model_location)
91+
92+
else:
93+
self.unrecognized_field(model_node, key, model_location)
94+
95+
def traverse_attribute(self, model_dict, attribute_name, model_location):
96+
"""
97+
Called for each attribute that is encountered in the model.
98+
"""
99+
pass
100+
101+
def unrecognized_field(self, model_dict, key, model_location):
102+
"""
103+
Called if a key in a model dictionary is not recognized as an attribute or a sub-folder.
104+
This can happen if:
105+
- the key is the name of a custom security provider, usually a full Java class name
106+
- the key is an attribute that is not applicable for the current WLS version or offline/online status
107+
- the field is not a valid attribute or folder name
108+
"""
109+
pass
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Copyright (c) 2021, Oracle Corporation and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
#
4+
# ------------
5+
# Description:
6+
# ------------
7+
# WDT filters to prepare a model for use with WKO, using the createDomain or prepareModel tools.
8+
# These operations can be invoked as a single call, or independently of each other.
9+
10+
from wlsdeploy.aliases import alias_utils
11+
from wlsdeploy.aliases.model_constants import CALCULATED_LISTEN_PORTS
12+
from wlsdeploy.aliases.model_constants import CLUSTER
13+
from wlsdeploy.aliases.model_constants import DYNAMIC_SERVERS
14+
from wlsdeploy.aliases.model_constants import LISTEN_PORT
15+
from wlsdeploy.aliases.model_constants import SERVER
16+
from wlsdeploy.aliases.model_constants import TOPOLOGY
17+
from wlsdeploy.aliases.validation_codes import ValidationCodes
18+
from wlsdeploy.aliases.wlst_modes import WlstModes
19+
from wlsdeploy.exception.expection_types import ExceptionType
20+
from wlsdeploy.logging.platform_logger import PlatformLogger
21+
from wlsdeploy.tool.util.filters.model_traverse import ModelTraverse
22+
from wlsdeploy.util import dictionary_utils
23+
24+
_class_name = 'wko_filter'
25+
_logger = PlatformLogger('wlsdeploy.tool.util')
26+
27+
28+
def filter_model(model, model_context):
29+
"""
30+
Perform the following operations on the specified model:
31+
- Remove any online-only attributes
32+
- Check if servers in a cluster have different ports
33+
:param model: the model to be filtered
34+
:param model_context: used by nested filters
35+
"""
36+
filter_online_attributes(model, model_context)
37+
check_clustered_server_ports(model, model_context)
38+
39+
40+
def filter_online_attributes(model, model_context):
41+
"""
42+
Remove any online-only attributes from the specified model.
43+
:param model: the model to be filtered
44+
:param model_context: used to configure aliases
45+
"""
46+
online_filter = OnlineAttributeFilter(model_context, ExceptionType.PREPARE)
47+
online_filter.traverse_model(model)
48+
49+
50+
def check_clustered_server_ports(model, _model_context):
51+
"""
52+
Set the CalculatedListenPorts attribute to false for dynamic clusters in the specified model.
53+
Warn if servers in a static cluster have different ports in the specified model.
54+
:param model: the model to be filtered
55+
:param _model_context: unused, passed by filter_helper if called independently
56+
"""
57+
_method_name = 'check_clustered_server_ports'
58+
59+
topology_folder = dictionary_utils.get_dictionary_element(model, TOPOLOGY)
60+
61+
# be sure every dynamic cluster has DynamicServers/CalculatedListenPorts set to false
62+
63+
clusters_folder = dictionary_utils.get_dictionary_element(topology_folder, CLUSTER)
64+
for cluster_name, cluster_fields in clusters_folder.items():
65+
dynamic_folder = cluster_fields[DYNAMIC_SERVERS]
66+
if dynamic_folder:
67+
calculated_listen_ports = dynamic_folder[CALCULATED_LISTEN_PORTS]
68+
if (calculated_listen_ports is None) or alias_utils.convert_boolean(calculated_listen_ports):
69+
_logger.info('WLSDPLY-20202', CALCULATED_LISTEN_PORTS, CLUSTER, cluster_name, class_name=_class_name,
70+
method_name=_method_name)
71+
dynamic_folder[CALCULATED_LISTEN_PORTS] = False
72+
73+
# be sure every server assigned to a cluster has the same listen port
74+
75+
server_port_map = {}
76+
servers_folder = dictionary_utils.get_dictionary_element(topology_folder, SERVER)
77+
for server_name, server_fields in servers_folder.items():
78+
server_cluster = server_fields[CLUSTER]
79+
server_port = server_fields[LISTEN_PORT]
80+
81+
if server_cluster and (server_port is not None):
82+
server_port_text = str(server_port)
83+
if '@@' in server_port_text:
84+
# prepareModel filters the model before and after it is tokenized,
85+
# so disregard variable values in the tokenized pass
86+
continue
87+
88+
if server_cluster in server_port_map:
89+
cluster_info = server_port_map[server_cluster]
90+
first_server = cluster_info["firstServer"]
91+
cluster_port = cluster_info["serverPort"]
92+
if server_port_text != cluster_port:
93+
_logger.warning('WLSDPLY-20203', SERVER, first_server, server_name, CLUSTER, server_cluster,
94+
LISTEN_PORT, cluster_port, server_port_text, class_name=_class_name,
95+
method_name=_method_name)
96+
else:
97+
server_port_map[server_cluster] = {"firstServer": server_name, "serverPort": server_port_text}
98+
99+
100+
class OnlineAttributeFilter(ModelTraverse):
101+
"""
102+
Traverse the model and remove any online-only attributes.
103+
"""
104+
105+
def __init__(self, model_context, exception_type):
106+
# use OFFLINE regardless of tool configuration
107+
ModelTraverse.__init__(self, model_context, WlstModes.OFFLINE, exception_type)
108+
109+
# Override
110+
def unrecognized_field(self, model_dict, key, model_location):
111+
"""
112+
If the attribute name has status ValidationCodes.VERSION_INVALID, it is a valid attribute sometimes,
113+
but not for offline mode in this WLS version.
114+
"""
115+
_method_name = 'unrecognized_field'
116+
117+
result, message = self._aliases.is_valid_model_attribute_name(model_location, key)
118+
if result == ValidationCodes.VERSION_INVALID:
119+
path = self._aliases.get_model_folder_path(model_location)
120+
_logger.info('WLSDPLY-20201', key, path, class_name=_class_name, method_name=_method_name)
121+
del model_dict[key]

0 commit comments

Comments
 (0)