1    	from typing import Any, Mapping, Sequence, cast
2    	
3    	from pcs.common import reports
4    	from pcs.lib import validate
5    	from pcs.lib.cib.constraint import common, ticket
6    	from pcs.lib.cib.tools import (
7    	    ElementNotFound,
8    	    IdProvider,
9    	    get_constraints,
10   	    get_element_by_id,
11   	    get_pacemaker_version_by_which_cib_was_validated,
12   	)
13   	from pcs.lib.env import LibraryEnvironment
14   	from pcs.lib.errors import LibraryError
15   	
16   	from .common import _load_resource_set_list, _primitive_resource_set_list
17   	
18   	
19   	def create(
20   	    env: LibraryEnvironment,
21   	    ticket_key: str,
22   	    resource_id: str,
23   	    options: Mapping[str, str],
24   	    resource_in_clone_alowed: bool = False,
(2) Event identifier_use: Example 1: Using identifier "duplication_alowed" (2 total uses in this function).
Also see events: [identifier_typo][remediation]
25   	    duplication_alowed: bool = False,
26   	) -> None:
27   	    """
28   	    create a plain ticket constraint
29   	
30   	    ticket_key -- ticket for constraining a resource
31   	    resource_id -- resource to be constrained
32   	    options -- desired constraint attributes
33   	    resource_in_clone_alowed -- allow to constrain a resource in a clone
34   	    duplication_alowed -- allow to create a duplicate constraint
35   	    """
36   	    cib = env.get_cib()
37   	    id_provider = IdProvider(cib)
38   	    constraint_section = get_constraints(cib)
39   	
40   	    # validation
41   	    constrained_el = None
42   	    try:
43   	        constrained_el = get_element_by_id(cib, resource_id)
44   	    except ElementNotFound:
45   	        env.report_processor.report(
46   	            reports.ReportItem.error(
47   	                reports.messages.IdNotFound(resource_id, [])
48   	            )
49   	        )
50   	
51   	    options_pairs = validate.values_to_pairs(
52   	        options,
53   	        validate.option_value_normalization(
54   	            {
55   	                "loss-policy": lambda value: value.lower(),
56   	                "rsc-role": lambda value: value.capitalize(),
57   	            }
58   	        ),
59   	    )
60   	
61   	    env.report_processor.report_list(
62   	        ticket.validate_create_plain(
63   	            id_provider,
64   	            ticket_key,
65   	            constrained_el,
66   	            options_pairs,
67   	            in_multiinstance_allowed=resource_in_clone_alowed,
68   	        )
69   	    )
70   	
71   	    if env.report_processor.has_errors:
72   	        raise LibraryError()
73   	
74   	    # modify CIB
75   	    new_constraint = ticket.create_plain(
76   	        constraint_section,
77   	        id_provider,
78   	        get_pacemaker_version_by_which_cib_was_validated(cib),
79   	        ticket_key,
80   	        resource_id,
81   	        validate.pairs_to_values(options_pairs),
82   	    )
83   	
84   	    # Check whether the created constraint is a duplicate of an existing one
85   	    env.report_processor.report_list(
86   	        ticket.DuplicatesCheckerTicketPlain().check(
87   	            constraint_section,
88   	            new_constraint,
89   	            {reports.codes.FORCE} if duplication_alowed else set(),
90   	        )
91   	    )
92   	    if env.report_processor.has_errors:
93   	        raise LibraryError()
94   	
95   	    # push CIB
96   	    env.push_cib()
97   	
98   	
99   	def create_with_set(
100  	    env: LibraryEnvironment,
101  	    resource_set_list: Sequence[Mapping[str, Any]],
102  	    constraint_options: Mapping[str, str],
103  	    resource_in_clone_alowed: bool = False,
104  	    duplication_alowed: bool = False,
105  	) -> None:
106  	    """
107  	    create a set ticket constraint
108  	
109  	    resource_set_list -- description of resource sets, for example:
110  	        {"ids": ["A", "B"], "options": {"sequential": "true"}},
111  	    constraint_options -- desired constraint attributes
112  	    resource_in_clone_alowed -- allow to constrain resources in a clone
113  	    duplication_alowed -- allow to create a duplicate constraint
114  	    """
115  	    cib = env.get_cib()
116  	    id_provider = IdProvider(cib)
117  	    constraint_section = get_constraints(cib)
118  	
119  	    # find all specified constrained resources and transform set options to
120  	    # value pairs for normalization and validation
121  	    resource_set_loaded_list = _load_resource_set_list(
122  	        cib,
123  	        env.report_processor,
124  	        # dacite doesn't support TypedDicts (https://github.com/konradhalas/dacite/issues/125)
125  	        # and therefore this command fails when called from APIv2, so we had to
126  	        # use Mapping in the signature of the lib command
127  	        cast(common.CmdInputResourceSetList, resource_set_list),
128  	        validate.option_value_normalization(
129  	            {
130  	                "role": lambda value: value.capitalize(),
131  	            }
132  	        ),
133  	    )
134  	    # Unlike in plain constraints, validation cannot continue if even a single
135  	    # resource could not be found. If such resources were omitted in their sets
136  	    # for purposes of validation, similarly to plain constraint commands, then
137  	    # those sets could become invalid, and thus validating such sets would
138  	    # provide false results.
139  	    if env.report_processor.has_errors:
140  	        raise LibraryError()
141  	
142  	    # transform constraint options to value pairs for normalization and
143  	    # validation
144  	    constraint_options_pairs = validate.values_to_pairs(
145  	        constraint_options,
146  	        validate.option_value_normalization(
147  	            {
148  	                "loss-policy": lambda value: value.lower(),
149  	            }
150  	        ),
151  	    )
152  	
153  	    # validation
154  	    env.report_processor.report_list(
155  	        ticket.validate_create_with_set(
156  	            id_provider,
157  	            resource_set_loaded_list,
158  	            constraint_options_pairs,
159  	            in_multiinstance_allowed=resource_in_clone_alowed,
160  	        )
161  	    )
162  	    if env.report_processor.has_errors:
163  	        raise LibraryError()
164  	
165  	    # modify CIB
166  	    new_constraint = ticket.create_with_set(
167  	        constraint_section,
168  	        id_provider,
169  	        get_pacemaker_version_by_which_cib_was_validated(cib),
170  	        _primitive_resource_set_list(resource_set_loaded_list),
171  	        validate.pairs_to_values(constraint_options_pairs),
172  	    )
173  	
174  	    # Check whether the created constraint is a duplicate of an existing one
175  	    env.report_processor.report_list(
176  	        ticket.DuplicatesCheckerTicketWithSet().check(
177  	            constraint_section,
178  	            new_constraint,
179  	            {reports.codes.FORCE} if duplication_alowed else set(),
180  	        )
181  	    )
182  	    if env.report_processor.has_errors:
183  	        raise LibraryError()
184  	
185  	    # push CIB
186  	    env.push_cib()
187  	
188  	
189  	def remove(env: LibraryEnvironment, ticket_key: str, resource_id: str) -> bool:
190  	    """
191  	    remove all ticket constraint from resource
192  	    If resource is in resource set with another resources then only resource
193  	    ref is removed. If resource is alone in resource set whole constraint is
194  	    removed.
195  	    """
196  	    constraint_section = get_constraints(env.get_cib())
197  	    any_plain_removed = ticket.remove_plain(
198  	        constraint_section, ticket_key, resource_id
199  	    )
200  	    any_with_resource_set_removed = ticket.remove_with_resource_set(
201  	        constraint_section, ticket_key, resource_id
202  	    )
203  	
204  	    env.push_cib()
205  	
206  	    return any_plain_removed or any_with_resource_set_removed
207