Compare commits
98 Commits
5821b62f39
...
pkg/dev/ri
Author | SHA1 | Date | |
---|---|---|---|
254a861472 | |||
a87485abd2 | |||
84eafb2deb | |||
0e55679e8b | |||
12111397e6 | |||
36827b6869 | |||
e354352dc8 | |||
a0fd998aeb | |||
e5737cd572 | |||
65d40f494f | |||
4fd5d38ca0 | |||
cda68e88a2 | |||
278fdd339a | |||
f4471c4875 | |||
b7cedd85fb | |||
3999969aa0 | |||
1249276b9d | |||
0970eee14e | |||
5add91082f | |||
e9596c2e2a | |||
1c7e2b9f0b | |||
42b67ce575 | |||
17e7f4caa0 | |||
2a77093187 | |||
db69df3b86 | |||
2b2cc4cf20 | |||
20de4443ae | |||
1e6c9060b5 | |||
e66a462710 | |||
9eb6a67ac6 | |||
d895c46729 | |||
63e679036b | |||
12acf2a3f4 | |||
fc686e9b7f | |||
6a12504e8c | |||
e11105db32 | |||
55c3021a5d | |||
5eeaaedded | |||
b049cb9324 | |||
cbd1081371 | |||
af8f0bfc9a | |||
651d89dd04 | |||
f4538004c7 | |||
83156863da | |||
88f864cd2a | |||
3e7ae41061 | |||
46ba8c8067 | |||
54bf1c35a7 | |||
2ca7b9a6fc | |||
38e54fe187 | |||
e6afef6a81 | |||
c149d7c38d | |||
b094e5a6ca | |||
0be7a72d28 | |||
710711b253 | |||
20d8242d47 | |||
807842e680 | |||
eb3720d6bf | |||
ba41b27dbf | |||
99d9fb7e70 | |||
04f8fc048c | |||
ac0e510e40 | |||
41620cc92e | |||
bc25216c27 | |||
0ccd3405db | |||
493aeff04e | |||
88f5b20aa9 | |||
2e16bc2b90 | |||
e35a3cb19d | |||
3b2d96566b | |||
c11ae16ea0 | |||
236dda2e3e | |||
0c4deb1016 | |||
c7ea03b411 | |||
1272524cc8 | |||
85151da58d | |||
e60fd4adbc | |||
bd299e3d2b | |||
0585ab0b11 | |||
6d571d8cac | |||
86ec629ce3 | |||
7eb78e5346 | |||
11553e6f09 | |||
0722e76fcc | |||
adaafde2e5 | |||
19d9fdc705 | |||
11294fdd61 | |||
4351662f2e | |||
5bf8d69e91 | |||
a9c74d68ff | |||
48d190d39b | |||
f474edd8ae | |||
61fc9b15e5 | |||
79312cc8c8 | |||
0da8b868ac | |||
a2e8b5dad4 | |||
58b3880e9b | |||
413ab6dbb0 |
662
debian/agpl-3
vendored
Normal file
662
debian/agpl-3
vendored
Normal file
@ -0,0 +1,662 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
||||
11
|
14
debian/control
vendored
Normal file
14
debian/control
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
Source: rougail
|
||||
Section: admin
|
||||
Priority: extra
|
||||
Maintainer: Cadoles <contact@cadoles.com>
|
||||
Build-depends: debhelper (>=11), python3-all, python3-setuptools, dh-python
|
||||
Standards-Version: 3.9.4
|
||||
Homepage: https://forge.cadoles.com/Infra/rougail
|
||||
|
||||
Package: python3-rougail
|
||||
Architecture: any
|
||||
Pre-Depends: dpkg, python3, ${misc:Pre-Depends}
|
||||
Depends: ${python:Depends}, ${misc:Depends}, python3-cheetah, python3-tiramisu3
|
||||
Description: configuration manager
|
||||
|
22
debian/copyright
vendored
Normal file
22
debian/copyright
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: risotto
|
||||
Upstream-Contact: Cadoles <contact@cadoles.com>
|
||||
Source: https://forge.cadoles.com/Infra/rougail
|
||||
|
||||
Files: *
|
||||
Copyright: 2019-2020 Cadoles <contact@cadoles.com>
|
||||
License: AGPL-3+
|
||||
|
||||
License: AGPL-3+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
3
debian/rougail.dirs
vendored
Normal file
3
debian/rougail.dirs
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/var/rougail/patches
|
||||
/var/rougail/templates
|
||||
/var/rougail/manifests
|
10
debian/rules
vendored
Normal file
10
debian/rules
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/make -f
|
||||
# See debhelper(7) (uncomment to enable)
|
||||
# output every command that modifies files on the build system.
|
||||
#DH_VERBOSE = 1
|
||||
|
||||
export PYBUILD_NAME = rougail
|
||||
export PYBUILD_DISABLE_python3 = test
|
||||
|
||||
%:
|
||||
dh $@ --with python3 --buildsystem=pybuild
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
||||
3.0 (quilt)
|
@ -9,17 +9,8 @@ Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fi
|
||||
|
||||
## Les dictionnaires
|
||||
|
||||
Un dictionnaire est un fichier XML donc la structure est expliqué ci-dessous.
|
||||
|
||||
Un dictionnaire contient en ensemble de variable, utilisable à tout moment, notamment dans des templates.
|
||||
|
||||
Il est possible d'avoir plusieurs espace de nom pour classer les variables (appeler aussi "extra") mais il est aussi possible, à l'interieur de ce espace de nom de mettre des familles pour classer les variables.
|
||||
|
||||
Les familles et les variables peuvent être défini dans plusieurs dictionnaires. Ces dictionnaires s'aggrège alors. Il est possible de rajouter des familles des variables, des services, des éléments à un service et des contraintes.
|
||||
|
||||
Il est également possible de redéfinir des éléments pour changer les comportement d'une variable ou d'un service.
|
||||
|
||||
FIXME expliquer les noms des variables dans les extras
|
||||
- [Les dictionnaires](dictionary/rougail.md)
|
||||
- [Les dictionnaires extra](dictionary/extra.md)
|
||||
|
||||
### Les variables
|
||||
|
||||
@ -30,7 +21,6 @@ FIXME expliquer les noms des variables dans les extras
|
||||
|
||||
- [La gestion d'un fichier](service/file.md)
|
||||
- [La gestion d'un fichier de service systemd](service/override.md)
|
||||
- [La gestion d'un port](service/port.md)
|
||||
- [La gestion d'une ip](service/ip.md)
|
||||
|
||||
### Les contraintes
|
||||
@ -38,8 +28,9 @@ FIXME expliquer les noms des variables dans les extras
|
||||
- [Les calcules automatiques](fill/README.md)
|
||||
- [Les vérifications des valeurs](check/README.md)
|
||||
- [Les conditions](condition/README.md)
|
||||
- [Les variables meneuses ou suiveuses](variable/leadership.md)
|
||||
|
||||
## Les templates
|
||||
|
||||
- Type creole
|
||||
- Type jinja2
|
||||
FIXME ^^
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Les vérifications des valeurs
|
||||
|
||||
- [Fonction de vérification](function.md)
|
||||
- [Les variables à choix](valid_enum.md)
|
||||
- [Réfinition](redefine.md)
|
||||
|
@ -38,6 +38,8 @@ Une fonction de vérification doit prendre en compte 2 aspects important :
|
||||
|
||||
À partir de maintenant seule None et des valeurs en minuscule seront autorisés.
|
||||
|
||||
Il est possible de définir des [paramètres](../param/README.md) à cette fonction.
|
||||
|
||||
## Vérification des valeurs avec avertissement
|
||||
|
||||
Dans la contrainte, il est possible de spécifier le niveau d'erreur et le mettre en avertissement :
|
||||
@ -49,5 +51,3 @@ Dans la contrainte, il est possible de spécifier le niveau d'erreur et le mettr
|
||||
```
|
||||
|
||||
Dans ce cas une valeur avec une majuscule sera accepté, mais un message d'avertissement apparaitra.
|
||||
|
||||
Il est possible de définir des [paramètres](../param/README.md) à cette fonction.
|
||||
|
46
doc/check/valid_enum.md
Normal file
46
doc/check/valid_enum.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Les variables à choix
|
||||
|
||||
Une variable à choix est d'abord une variable avec une [fonction check](function.md).
|
||||
|
||||
## Les variables à choix simple
|
||||
|
||||
Il est possible d'imposer une liste de valeur pour une variable particulière :
|
||||
|
||||
```
|
||||
<check name="valid_enum">
|
||||
<param>yes</param>
|
||||
<param>no</param>
|
||||
<param>maybe</param>
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
```
|
||||
|
||||
Dans ce cas, seule les valeurs proposés sont possible pour cette variable.
|
||||
|
||||
Par défaut, cette variable est obligatoire. Cela signifie qu'il n'est pas possible de spécifier "None" à cette variable.
|
||||
|
||||
## Les variables à choix avec valeur None
|
||||
|
||||
Il y a deux possibilités pour avoir une valeur "None" dans les choix :
|
||||
|
||||
- rendre la variable non obligatoire, cela va ajouter un choix "None" dans la liste :
|
||||
|
||||
```
|
||||
<variable name="my_variable" mandatory="False">
|
||||
```
|
||||
|
||||
Ou en ajoutant le paramètre "None" :
|
||||
|
||||
```
|
||||
<check name="valid_enum">
|
||||
<param>yes</param>
|
||||
<param>no</param>
|
||||
<param type='nil'/>
|
||||
<param>maybe</param>
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
```
|
||||
|
||||
## La valeur par défaut
|
||||
|
||||
Si aucune valeur n'est spécifié pour la variable, automatiquement le premier choix va est placé comme valeur par défaut.
|
@ -1,13 +0,0 @@
|
||||
FIXME
|
||||
<!ATTLIST variable remove_condition (True|False) "False">
|
||||
|
||||
|
||||
<!ELEMENT condition ((target | param)+ )>
|
||||
<!ATTLIST condition name (disabled_if_in|disabled_if_not_in|hidden_if_in|hidden_if_not_in|mandatory_if_in|mandatory_if_not_in) #REQUIRED>
|
||||
<!ATTLIST condition source CDATA #REQUIRED>
|
||||
<!ATTLIST condition fallback (True|False) "False">
|
||||
<!ATTLIST condition force_condition_on_fallback (True|False) "False">
|
||||
<!ATTLIST condition force_inverse_condition_on_fallback (True|False) "False">
|
||||
|
||||
|
||||
on peut mettre plusieurs param (oui ou maybe)
|
5
doc/condition/README.md
Normal file
5
doc/condition/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Les conditions
|
||||
|
||||
- [Déclaration d'une condition](condition.md)
|
||||
- [Les différentes conditions](conditions.md)
|
||||
- [Réfinition](redefine.md)
|
76
doc/condition/condition.md
Normal file
76
doc/condition/condition.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Les conditions
|
||||
|
||||
## Un condition
|
||||
|
||||
Les conditions permettent d'ajouter ou de supprimer des propriétés à une [variable](../variable/README.md), une [famille](../family/README.md), un [service](../service/service.md), un [fichier](../service/file.md) ou une [ip](../service/ip.md) suivant le contexte.
|
||||
|
||||
Nous allons nous concentrer ici sur la condition hidden_if_in, mais [il existe d'autre conditions](conditions.md).
|
||||
|
||||
La condition hidden_if_in permet de cacher une variable où une famille à l'utilisateur, mais cette variable est toujours accessible dans un calcul, un vérification ou dans un template.
|
||||
|
||||
```
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
|
||||
<constraints>
|
||||
<condition name="hidden_if_in" source="condition">
|
||||
<param>True</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Le [paramètres](../param/README.md) de la condition permet de définir les valeurs que doit avoir la source pour appliquer l'action.
|
||||
|
||||
La [cible](../target/README.md) de la condition est ici "my_variable".
|
||||
|
||||
Donc ici la variable est caché à l'utilisateur si la variable "condition" est à True (le paramètre).
|
||||
|
||||
## Un condition avec plusieurs paramètres
|
||||
|
||||
Il est également possible de mettre plusieurs paramètre :
|
||||
|
||||
```
|
||||
<variables>
|
||||
<variable name="condition"/>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
|
||||
<constraints>
|
||||
<condition name="hidden_if_in" source="condition">
|
||||
<param>yes</param>
|
||||
<param>maybe</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
## Une condition optionnelle
|
||||
|
||||
Il est possible de définir une condition avec une variable source qui n'existe pas dans toutes les contextes.
|
||||
|
||||
Dans ce cas, on met la condition en "optionnelle".
|
||||
|
||||
Si la variable source existe, la condition s'applique.
|
||||
|
||||
Si la variable source n'existe pas :
|
||||
|
||||
- si le nom fini en _if_in (par exemple hidden_if_in), l'action est forcée sans condition (les cibles sont hidden)
|
||||
- si le nom fini en _if_not_in (par exemple hidden_if_not_in), la condition est totalement ignorée
|
||||
|
||||
Ces deux comportements peuvent être changé à tout moment avec l'attribut "apply_on_fallback". Dans ce cas :
|
||||
|
||||
- si la valeur de "apply_on_fallback" est "True", l'action est forcée sans condition
|
||||
- si la valeur de "apply_on_fallback" est "False", la condition est totalement ignorée
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
<condition name="hidden_if_in" source="condition" optional="True", apply_on_fallback="False">
|
||||
<param>yes</param>
|
||||
<param>maybe</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
```
|
29
doc/condition/conditions.md
Normal file
29
doc/condition/conditions.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Les conditions
|
||||
|
||||
## Les conditions \_if_in et \_if_not_in
|
||||
|
||||
Il existe deux types de conditions :
|
||||
|
||||
- les conditions dont le nom fini par \_if_in : dans ce cas si la variable source est égal à un des paramètres, l'action est effective
|
||||
- les conditions dont le nom fini par \_if_not_in : dans ce cas si la variable source est différents de tous les paramètres, l'action est effective
|
||||
|
||||
## Désactivation
|
||||
|
||||
Il est possible de désactiver une [variable](../variable/README.md) ou une [famille](../family/README.md) avec les conditions :
|
||||
|
||||
- disabled_if_in
|
||||
- disabled_if_not_in
|
||||
|
||||
## Caché
|
||||
|
||||
Il est possible de cacher une [variable](../variable/README.md), une [famille](../family/README.md), un [service](../service/service.md), un [fichier](../service/file.md) ou une [ip](../service/ip.md) avec les conditions :
|
||||
|
||||
- hidden_if_in
|
||||
- hidden_if_not_in
|
||||
|
||||
## Obligatoire
|
||||
|
||||
Il est possible de rendre obligatoire une [variable](../variable/README.md) avec les conditions :
|
||||
|
||||
- mandatory_if_in
|
||||
- mandatory_if_not_in
|
29
doc/condition/redefine.md
Normal file
29
doc/condition/redefine.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Rédéfinition
|
||||
|
||||
Il se peut que dans un dictionnaire on décide de définir une condition.
|
||||
|
||||
Dans un second dictionnaire il est possible de supprimer cette condition.
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
||||
|
||||
```
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
|
||||
<constraints>
|
||||
<condition name="hidden_if_in" source="condition">
|
||||
<param>True</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Dans un second dictionnaire supprimer ce calcul :
|
||||
|
||||
```
|
||||
<variables>
|
||||
<variable name="condition" redefine="True" remove_condition="True"/>
|
||||
</variables>
|
||||
```
|
@ -104,11 +104,11 @@ Générons le template :
|
||||
import asyncio
|
||||
from example import option_0
|
||||
from tiramisu import Config
|
||||
from rougail import RougailTemplate
|
||||
from rougail import RougailSystemdTemplate
|
||||
|
||||
async def template():
|
||||
config = await Config(option_0)
|
||||
engine = RougailTemplate(config)
|
||||
engine = RougailSystemdTemplate(config)
|
||||
await engine.instance_files()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
@ -14,7 +14,9 @@ Pour modifier il suffit de faire :
|
||||
RougailConfig[key] = value
|
||||
```
|
||||
|
||||
## Les répertoires des dictionnaires
|
||||
## Configuration de chargement des dictionnaires
|
||||
|
||||
### Les répertoires des dictionnaires
|
||||
|
||||
Il existe deux types de répertoires de dictionnaires :
|
||||
|
||||
@ -36,7 +38,7 @@ RougailConfig['extra_dictionaries']['example'] = ['/dir1', '/dir2']
|
||||
|
||||
Les dictionnaires sont chargés dans le même ordre que les dictionnaires principaux.
|
||||
|
||||
## La DTD
|
||||
### La DTD
|
||||
|
||||
Rougail a besoin du fichier de DTD pour lire les fichiers dictionnaire.
|
||||
|
||||
@ -44,28 +46,75 @@ Par défaut le fichier de la DTD est dans le sous répertoire "data" du réperto
|
||||
|
||||
Pour pouvez changer le répertoire de destination de la DTD et le nom du fichier avec la clef "dtdfilename".
|
||||
|
||||
## Le fichier de fonction
|
||||
### Le fichier de fonction
|
||||
|
||||
Le fichier qui contient les fonctions personnalisés est géré dans la clef "functions_file" et a comme valeur par défaut "/srv/rougail/functions.py".
|
||||
|
||||
## Le répertoire des templates
|
||||
### La variable auto_freeze
|
||||
|
||||
La propriété auto_freeze n'est appliqué que une variable spécifique passe à True. Par défaut le nom de la variable est "instancied_module", mais il est possible de changer le nom de cette variable via la clef "auto_freeze_variable".
|
||||
|
||||
### Les modes
|
||||
|
||||
Les modes sont personnalisables dans Rougail. Par défaut les modes sont "basic", "normal" et "expert".
|
||||
Il est possible de changer cette liste via la clef "modes_level".
|
||||
|
||||
Si vous changer ces valeurs, penser à changer les modes par défaut des familles et des variables.
|
||||
|
||||
### Le mode par défaut pour une famille
|
||||
|
||||
Le mode par défaut d'une famille est "basic". Il est possible de changer le mode par défaut d'une famille via la clef "default_family_mode".
|
||||
|
||||
### Le mode par défaut pour une variable
|
||||
|
||||
Le mode par défaut d'une variable est "normal". Il est possible de changer le mode par défaut d'une variable via la clef "default_variable_mode".
|
||||
|
||||
### Le nom des fonctions internes
|
||||
|
||||
Il est possible d'ajouter des fonctions interne via la clef "internal_functions".
|
||||
|
||||
## Configuration de la templatisation
|
||||
|
||||
### Le répertoire des templates
|
||||
|
||||
Le répertoire des templates est géré dans la clef "templates_dir" et a comme valeur par défaut : "/srv/rougail/templates".
|
||||
|
||||
## Le répertoire des patchs
|
||||
### Le répertoire des patchs
|
||||
|
||||
Le répertoire des patches est géré dans la clef "patches_dir" et a comme valeur par défaut : "/srv/rougail/patches".
|
||||
|
||||
## Le répertoire temporaire
|
||||
### Le répertoire temporaire
|
||||
|
||||
Le répertoire temporaire est utile lors de la génération de template. Il contient une copie des templates avec, éventuellement, les patches appliqués sur les templates.
|
||||
|
||||
Le répertoire de temporaire est géré dans la clef "tmp_dir" et a comme valeur par défaut : "/srv/rougail/tmp".
|
||||
|
||||
## Le répertoire de destination des fichiers générés
|
||||
### Le répertoire de destination des fichiers générés
|
||||
|
||||
Le répertoire de destination des fichiers générés est géré dans la clef "destinations_dir" et a comme valeur par défaut : "/srv/rougail/destinations".
|
||||
|
||||
## La variable auto_freeze
|
||||
## La configuration par défaut des fichiers
|
||||
|
||||
La propriété auto_freeze n'est appliqué que une variable spécifique passe à True. Par défaut le nom de la variable est "instancied_module", mais il est possible de changer le nom de cette variable via la clef "auto_freeze_variable".
|
||||
### Le moteur de templates par défaut
|
||||
|
||||
Le moteur de template est géré dans la clef "default_files_engine" et a comme valeur par défaut : "creole". Les valeurs possible sont "none", "creole" ou "jinja2".
|
||||
|
||||
### Les droits par défaut des fichiers
|
||||
|
||||
Les droits des fichiers générés est géré dans la clef "default_files_mode" et a comme valeur par défaut : "0644".
|
||||
|
||||
### Le propriétaire par défaut des fichiers
|
||||
|
||||
Le propriétaire des fichiers générés est géré dans la clef "default_files_owner" et a comme valeur par défaut : "root".
|
||||
Le groupe propriétaire des fichiers générés est géré dans la clef "default_files_group" et a comme valeur par défaut : "root".
|
||||
|
||||
### La méthode d'inclusion par défaut des fichiers
|
||||
|
||||
La méthode d'inclusion des fichiers générés est géré dans la clef "default_files_included" et a comme valeur par défaut : "no". Les valeurs possible sont "no", "name" et "content".
|
||||
'default_files_included': 'no',
|
||||
|
||||
## La configuration par défaut des overrides
|
||||
|
||||
### Le moteur de templates par défaut
|
||||
|
||||
Le moteur de template est géré dans la clef "default_overrides_engine" et a comme valeur par défaut : "creole". Les valeurs possible sont "none", "creole" ou "jinja2".
|
||||
|
12
doc/dictionary/extra.md
Normal file
12
doc/dictionary/extra.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Les dictionnaires extra
|
||||
|
||||
Un extra est un espace de nom différent. L'idée et de pouvoir classer les variables par thématique.
|
||||
|
||||
Les espaces de nom extra doivent être déclaré au moment [de la configuration de Rougail](../dev/config.md).
|
||||
|
||||
Dans cet espace de nom :
|
||||
|
||||
- des variables et des familles peuvent avoir le même nom dans différentes familles
|
||||
- la valeur d'un cible, source, leader ou follower des contraintes doivent être avec un chemin complet
|
||||
- on ne peut pas déclarer des services dans cet espace de nom
|
||||
- dans un template il faut utiliser des chemins complet (%%my_extra.my_family.my_variable ou %%my_extra.my_family.leader.follower pour une variable suiveuse)
|
22
doc/dictionary/rougail.md
Normal file
22
doc/dictionary/rougail.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Les dictionnaires
|
||||
|
||||
## Un dictionnaire ?
|
||||
|
||||
Un dictionnaire est un fichier XML donc la structure est décrite dans cette documentation.
|
||||
|
||||
Un dictionnaire contient en ensemble de variable chargé dans Tiramisu, utilisable à tout moment, notamment dans des templates.
|
||||
|
||||
Les familles, les variables et les contraintes peuvent être défini dans plusieurs dictionnaires. Ces dictionnaires s'aggrège alors.
|
||||
|
||||
Il est également possible de redéfinir des éléments pour changer les comportement d'une variable ou d'un service.
|
||||
|
||||
## L'espace de nom par défaut
|
||||
|
||||
L'espace de nom par défaut s'appelle "rougail" ([ce nom est personnalisable](../dev/config.md)).
|
||||
|
||||
Cet espace de nom est un peu particulier :
|
||||
|
||||
- le nom des variables et des familles doivent être unique pour l'ensemble de cet espace (même si ces variables ou familles sont dans des familles différentes)
|
||||
- la valeur d'un cible, source, leader ou follower des contraintes peuvent être avec nom de la variable ou de la famille ou leurs chemins complet
|
||||
- on peut déclarer des services dans cet espace de nom
|
||||
- dans un template on peut utiliser cette variable sans le chemin complet (%%my_variable) ou avec (%%rougail.my_family.my_variable)
|
@ -2,4 +2,5 @@
|
||||
|
||||
- [Une famille](simple.md)
|
||||
- [Famille crée dynamiquement](auto.md)
|
||||
- [Les variables meneuses ou suiveuses](leadership.md)
|
||||
|
||||
|
56
doc/family/leadership.md
Normal file
56
doc/family/leadership.md
Normal file
@ -0,0 +1,56 @@
|
||||
# Variable meneuse ou suiveuse
|
||||
|
||||
## Variable meneuse
|
||||
|
||||
Une variable meneuse est une variable qui va guider la longueur d'autre variables (appelé variables suiveuse).
|
||||
|
||||
Une variable meneuse est une [variable](../variable/README.md) qui est obligatoirement de type multiple.
|
||||
|
||||
Une variable meneuse peut être obligatoire.
|
||||
|
||||
Le [mode](../mode.md) par défaut correspond au plus petit mode définit par l'utilisateur des variables suiveuses.
|
||||
|
||||
## Variable suiveuse
|
||||
|
||||
Une variable suiveuse est une variable donc la longueur n'est pas déterminé par elle-même, mais est identique à celle de la variable meneuse dont elle dépend.
|
||||
|
||||
Une variable suiveuse est une variable placer juste derrière une variable meneuse ou une autre variable suiveuse.
|
||||
|
||||
L'ordre de définition des variables suiveuses est important.
|
||||
|
||||
Cette variable peut être de type multiple. Dans ce cas, pour un index determiné, il est possible de mettre plusieurs valeurs à une même variable.
|
||||
|
||||
Une variable suiveuse peut être obligatoire. Cela signifie que lorsqu'une variable meneuse est renseigné, il faut obligatoirement que la variable suiveuse est également une valeur à l'index considéré.
|
||||
Si aucune valeur n'est définit pour la variable meneuse, aucune valeur n'est a spécifié pour la variable suiveuse.
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une variable suiveuse correspond au [mode](../mode.md) de la variable meneuse.
|
||||
|
||||
Si une variable meneuse est caché ou désactivé, les variables suiveuses le seront également.
|
||||
|
||||
## Définition des variables meneuse et suiveuse
|
||||
|
||||
Les variables meneuses et suiveuses doivent dans une famille de type "leadership".
|
||||
|
||||
Voici un exemple de définition d'une variable meneuse et de deux variables meneuses :
|
||||
|
||||
```
|
||||
<variables>
|
||||
<family name="family" leadership='True'>
|
||||
<variable name="leader" multi='True'/>
|
||||
<variable name="follower1"/>
|
||||
<variable name="follower2" multi='True'/>
|
||||
</family>
|
||||
</variables>
|
||||
```
|
||||
|
||||
## Ajout d'une nouvelle variable suiveuse
|
||||
|
||||
Pour ajouter, dans un nouveau dictionnaire, une variable suiveuse à notre groupe, rien de plus simple, il suffit définir une ou des nouvelles variables dans la famille :
|
||||
|
||||
```
|
||||
<variables>
|
||||
<family name="family">
|
||||
<variable name="follower3"/>
|
||||
</family>
|
||||
</variables>
|
||||
```
|
@ -44,7 +44,7 @@ En plus de la description, il est possible de préciser une aide complémentaire
|
||||
|
||||
## Mode de la famille
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une famille correspond au [mode](../mode.md) le plus petite des variables ou des familles qui sont contenu dans cette famille.
|
||||
Le [mode](../mode.md) par défaut d'une famille correspond au [mode](../mode.md) du mode le plus petit entre la famille parente, les variables enfants ou des familles enfants qui sont contenu dans cette famille.
|
||||
|
||||
Changer le [mode](../mode.md) d'une famille permet de définir le [mode](../mode.md) par défaut des variables ou des familles inclusent dans cette famille.
|
||||
|
||||
|
@ -49,8 +49,8 @@ Si une condition "hidden_if_in" est spécifié à la variable, la valeur sera mo
|
||||
|
||||
## Variable meneuse ou suiveuse avec valeur calculé
|
||||
|
||||
Une [variable suiveuse](../variable/leadership.md) ne peut pas être calculé automatiquement.
|
||||
Une [variable meneuse](../variable/leadership.md) peut être calculé automatiquement.
|
||||
Une [variable suiveuse](../family/leadership.md) ne peut pas être calculé automatiquement.
|
||||
Une [variable meneuse](../family/leadership.md) peut être calculé automatiquement.
|
||||
Si la variable n'est pas multiple, il ne faut pas que le calcule retourne une liste.
|
||||
|
||||
## Variable dynamique avec une valeur calculée
|
||||
|
@ -1,8 +1,10 @@
|
||||
Mode
|
||||
====
|
||||
|
||||
Il existe trois "mode" dans Rougail :
|
||||
Par défault, il existe trois "mode" dans Rougail :
|
||||
|
||||
- basic : variables indispensables à la mise en place d'un service
|
||||
- normal : variables couramment modifié par l'utilisateur
|
||||
- expert : variables a manipuler avec précausion et en toute connaissance de cause
|
||||
|
||||
Il est possible de personnaliser les modes dans la [configuration de rougail](dev/config.md)
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Paramètre de type information
|
||||
|
||||
Le paramètre peut être la valeur est issue d'une information de la configuration.
|
||||
## Les informations de la configuration
|
||||
|
||||
Le paramètre peut être la valeur est issue d'une information de la configuration :
|
||||
|
||||
```
|
||||
<param type="information">server_name</param>
|
||||
@ -8,3 +10,15 @@ Le paramètre peut être la valeur est issue d'une information de la configurati
|
||||
|
||||
Dans ce cas, l'information de la configuration "server_name" sera utilisé comme valeur du paramètre.
|
||||
Si l'information n'existe pas, la paramètre aura la valeur "None".
|
||||
|
||||
## Les informations de la cible
|
||||
|
||||
Le paramètre peut être la valeur est issue d'une information de la cible du calcul :
|
||||
|
||||
```
|
||||
<param type="target_information">test</param>
|
||||
<param type="target_information">help</param>
|
||||
```
|
||||
|
||||
Dans ce cas, l'information de la configuration "test" ou "help" sera utilisé comme valeur du paramètre.
|
||||
Si l'information n'existe pas, la paramètre aura la valeur "None".
|
||||
|
@ -6,7 +6,7 @@ Imaginons que la variable "my_variable" pré-existe. La valeur de la variable se
|
||||
<param type="variable">my_variable</param>
|
||||
```
|
||||
|
||||
[Les variables meneuses ou suiveuses](../variable/leadership.md) peuvent être utilisé sans soucis comme paramètre.
|
||||
[Les variables meneuses ou suiveuses](../family/leadership.md) peuvent être utilisé sans soucis comme paramètre.
|
||||
|
||||
## Paramètre avec variable potentiellement non existante
|
||||
|
||||
|
@ -11,7 +11,7 @@ Il est nécessaire, au minimum, de spécifier le chemin complet du fichier :
|
||||
```
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file name="/etc/squid/squid.conf"/>
|
||||
<file>/etc/squid/squid.conf</file>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
@ -21,7 +21,7 @@ Dans ce cas, le nom du template est déduit du nom du fichier, ici cela sera "sq
|
||||
Si le template a un nom différent (par exemple si plusieurs template se retrouve avec le même nom), il est possible de changer le nom du template avec l'attribut source :
|
||||
|
||||
```
|
||||
<file name="/etc/squid/squid.conf" source="template-squid.conf"/>
|
||||
<file source="template-squid.conf">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
## Les noms de fichiers dynamique
|
||||
@ -31,7 +31,7 @@ Il est possible également de définir le nom du fichier dans une variable :
|
||||
```
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file name="my_variable" file_type="variable" source="squid.conf"/>
|
||||
<file file_type="variable" source="squid.conf">my_variable</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
@ -41,6 +41,8 @@ Il est possible également de définir le nom du fichier dans une variable :
|
||||
</variables>
|
||||
```
|
||||
|
||||
Attention, la variable doit être de type "filename".
|
||||
|
||||
Dans le cas des fichiers dynamique, la source est obligatoire.
|
||||
|
||||
Il est même possible de définir une variable de type multiple, ce qui génèrera plusiers fichiers :
|
||||
@ -48,7 +50,7 @@ Il est même possible de définir une variable de type multiple, ce qui génère
|
||||
```
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file name="my_variable" file_type="variable" source="squid.conf"/>
|
||||
<file file_type="variable" source="squid.conf">my_variable</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
@ -72,7 +74,7 @@ En effet, il est possible de passer le contenu d'une variable au template :
|
||||
```
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file name="my_variable1" file_type="variable" source="squid.conf" variable="my_variable2"/>
|
||||
<file file_type="variable" source="squid.conf" variable="my_variable2">my_variable1</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
@ -95,12 +97,14 @@ Attention : les deux variables "my_variable1" et "my_variable2" doivent être mu
|
||||
|
||||
Par défaut les droits du fichier généré sont "0644" avec comme utilisateur "root" et groupe "root".
|
||||
|
||||
Il est possible de définir une autre valeur à un ou plusieurs de ces attributs :
|
||||
Il est possible de définir les droits, l'utilisateur ou le groupe d'un fichier généré :
|
||||
|
||||
```
|
||||
<file name="/etc/squid/squid.conf" mode="0640" owner="nobody" group="squid"/>
|
||||
<file mode="0640" owner="nobody" group="squid">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
Il est possible de personnaliser les droits par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
||||
## Désactiver la génération d'un fichier
|
||||
|
||||
Il est possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise fichier :
|
||||
@ -108,13 +112,11 @@ Il est possible de définir une [condition](../condition/README.md) de type "dis
|
||||
```
|
||||
<services>
|
||||
<service name="test">
|
||||
<file name="/etc/squid/squid.conf" filelist="squid"/>
|
||||
<file filelist="squid">/etc/squid/squid.conf</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<family name="general">
|
||||
<variable name="condition" type="boolean"/>
|
||||
</family>
|
||||
<variable name="condition" type="boolean"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
@ -124,22 +126,49 @@ Il est possible de définir une [condition](../condition/README.md) de type "dis
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Dans ce cas, tous les fichiers avec un attribut filelist à "squid" seront désactivé si la variable "condition" est False.
|
||||
Dans ce cas, tous les fichiers avec un attribut filelist à "squid" seront désactivés si la variable "condition" est False.
|
||||
|
||||
## Redéfinir une fichier
|
||||
|
||||
Il est possible de redéfinir les éléments d'un fichier dans un dictionnaire différent en utilisant l'attribut redefine :
|
||||
|
||||
```
|
||||
<file name="/etc/squid/squid.conf" source="template-squid.conf" redefine="True"/>
|
||||
<file source="template-squid.conf" redefine="True">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
## Choix du moteur de templating
|
||||
|
||||
Par défaut, le moteur de templating est le moteur de templating compatible avec "creole".
|
||||
|
||||
Aujourd'hui il est possible de désactiver la templatisation du fichier (il sera alors uniquement copié) :
|
||||
Il est possible de désactiver la templatisation du fichier (il sera alors uniquement copié) :
|
||||
|
||||
```
|
||||
<file name="/etc/squid/squid.conf" templating="none"/>
|
||||
<file engine="none">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
Ou d'utiliser le moteur "jinja2" :
|
||||
|
||||
```
|
||||
<file engine="jinja2">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
Il est possible de personnaliser le moteur par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
||||
## Inclusion de template
|
||||
|
||||
Un attribut "included" permet de définir la nature du fichier. Cet attribut peut avoir trois valeurs :
|
||||
|
||||
- "no" : c'est un fichier normal
|
||||
- "name" : le répertoire de destination est listé dans un autre template, il faut que le fichier soit généré avant cet autre template
|
||||
- "content" : le contenu du fichier est copié dans un autre template, il faut que le fichier soit généré avant cet autre template et ce fichier n'a pas besoin d'être installé sur le serveur cible.
|
||||
|
||||
Exemples :
|
||||
|
||||
```
|
||||
<file included="name">/etc/squid/squid.conf</file>
|
||||
<file included="content">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
Bien entendu, c'est au développeur de lister ou d'inclure le contenu de ce template dans le fichier de destination. Cet attribut permet juste de garantir que le fichier sera fait avant l'autre et de ne pas l'installer sur le serveur si ce n'est pas nécessaire.
|
||||
|
||||
Il est possible de personnaliser les methodes d'inclusion par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
@ -1,15 +1,47 @@
|
||||
# La gestion d'une IP
|
||||
|
||||
## La balise ip
|
||||
## La balise IP
|
||||
|
||||
La gestion des IP se fait dans un conteneur de [service](service.md).
|
||||
|
||||
FIXME
|
||||
La déclaration de l'attribut permet d'associer une IP autorisé à accéder au service.
|
||||
|
||||
<!ELEMENT ip (#PCDATA)>
|
||||
<!ATTLIST ip iplist CDATA #IMPLIED >
|
||||
<!ATTLIST ip ip_type (NetworkOption|variable) "NetworkOption">
|
||||
<!ATTLIST ip interface_type (UnicodeOption|variable) "UnicodeOption">
|
||||
<!ATTLIST ip interface CDATA #REQUIRED>
|
||||
<!ATTLIST ip netmask_type (NetmaskOption|variable) "NetmaskOption">
|
||||
<!ATTLIST ip netmask CDATA "255.255.255.255">
|
||||
Il est nécessaire, au minimum, de spécifier le nom d'une variable de type "IP" :
|
||||
|
||||
```
|
||||
<ip ip_type="variable">variable_ip</ip>
|
||||
```
|
||||
|
||||
## La gestion d'un réseau
|
||||
|
||||
L'adresse peut être de type réseau ("network") :
|
||||
|
||||
```
|
||||
<ip netmask="variable_netmask">variable_ip</ip>
|
||||
```
|
||||
|
||||
Attention, dans ce cas il faut préciser une variable de type "netmask" dans l'attribut netmask.
|
||||
|
||||
## Désactiver la génération d'une IP
|
||||
|
||||
Il est possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise IP :
|
||||
|
||||
```
|
||||
<services>
|
||||
<service name="test">
|
||||
<ip iplist="test_ip">variable_ip</ip>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
<variable name="variable_ip" type="ip"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
<param>False</param>
|
||||
<target type="iplist">test_ip</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Dans ce cas, tous les IP avec un attribut iplist à "test_ip" seront désactivé si la variable "condition" est False.
|
||||
|
@ -30,8 +30,16 @@ Dans ce cas le fichier de destination aura le même nom.
|
||||
|
||||
Par défaut, le moteur de templating est le moteur de templating compatible avec "creole".
|
||||
|
||||
Aujourd'hui il est possible de désactiver la templatisation du fichier (il sera alors uniquement copié) :
|
||||
Il est possible de désactiver la templatisation du fichier (il sera alors uniquement copié) :
|
||||
|
||||
```
|
||||
<override templating="none"/>
|
||||
<override engine="none"/>
|
||||
```
|
||||
|
||||
Ou d'utiliser le moteur "jinja2" :
|
||||
|
||||
```
|
||||
<override engine="jinja2"/>
|
||||
```
|
||||
|
||||
Il est possible de personnaliser le moteur par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
@ -1,63 +0,0 @@
|
||||
# La gestion d'un port
|
||||
|
||||
## La balise port
|
||||
|
||||
La gestion des ports se fait dans un conteneur de [service](service.md).
|
||||
|
||||
La balise port permet d'associer un port à service :
|
||||
|
||||
```
|
||||
<services>
|
||||
<service name="squid">
|
||||
<port>3128</port>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
|
||||
Il est possible de choisir le protocole TCP ou UDP (TCP par défaut) :
|
||||
|
||||
```
|
||||
<port protocol="udp">123</port>
|
||||
```
|
||||
|
||||
## Les numéros de port dynamique
|
||||
|
||||
Il est possible également de définir le port dans une variable :
|
||||
|
||||
```
|
||||
<services>
|
||||
<service name="squid">
|
||||
<port port_type="variable">my_variable</port>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="my_variable" type="port">
|
||||
<value>123</value>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
## Désactiver le port
|
||||
|
||||
Il est possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise port :
|
||||
|
||||
```
|
||||
<services>
|
||||
<service name="test">
|
||||
<port portlist="squid">3128</port>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<family name="general">
|
||||
<variable name="condition" type="boolean"/>
|
||||
</family>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
<param>False</param>
|
||||
<target type="portlist">squid</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Dans ce cas, tous les ports avec un attribut portlist à "squid" seront désactivé si la variable "condition" est False.
|
@ -1,8 +1,10 @@
|
||||
# La gestion d'un service
|
||||
|
||||
## La base service
|
||||
|
||||
Un service est inclut dans un conteneur [services](../services.md).
|
||||
|
||||
Cette balise permet de définir tous les éléments (fichier, port, IP, ...) lié à un service ou à démon.
|
||||
Cette balise permet de définir tous les éléments ([fichier](file.md), [IP](ip.md), ...) liés à un service ou à démon.
|
||||
|
||||
Il faut, à la création du service, préciser son nom :
|
||||
|
||||
@ -14,3 +16,35 @@ Il faut, à la création du service, préciser son nom :
|
||||
</services>
|
||||
</rougail>
|
||||
```
|
||||
|
||||
Un service peut ne pas être géré :
|
||||
|
||||
```
|
||||
<service name="squid" manage="True"/>
|
||||
```
|
||||
|
||||
Un service non géré est généralement une service qui n'existe pas réellement (par exemple si on configure un client).
|
||||
|
||||
Un service non géré ne peut conteneur que des fichiers.
|
||||
|
||||
## Désactiver la génération d'un service
|
||||
|
||||
Il est possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise service :
|
||||
|
||||
```
|
||||
<services>
|
||||
<service name="test">
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
<param>False</param>
|
||||
<target type="servicelist">test</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Dans ce cas, tous les services et les éléments qu'il compose ([fichier](file.md), ...) avec un attribut servicelist à "test" seront désactivés si la variable "condition" est False.
|
||||
|
5
doc/target/README.md
Normal file
5
doc/target/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# La cible
|
||||
|
||||
- [De type variable](../target/variable.md)
|
||||
- [De type famille](../target/family.md)
|
||||
- [De type \*list](../target/list.md)
|
13
doc/target/family.md
Normal file
13
doc/target/family.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Cible de type "variable"
|
||||
|
||||
Une cible peut être de type famille :
|
||||
|
||||
```
|
||||
<target type="family">my_family</target>
|
||||
```
|
||||
|
||||
Mais une target peut être optionnelle. C'est à dire que si la famille n'existe pas, l'action ne sera pas associé à cette famille.
|
||||
|
||||
```
|
||||
<target type="family" optional="True">my_family</target>
|
||||
```
|
35
doc/target/list.md
Normal file
35
doc/target/list.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Cible de type \*list
|
||||
|
||||
## Les différences cible de type \*.list
|
||||
|
||||
### servicelist
|
||||
|
||||
Une cible peut être de type [service](../service/service.md) :
|
||||
|
||||
```
|
||||
<target type="servicelist">example</target>
|
||||
```
|
||||
|
||||
### filelist
|
||||
|
||||
Une cible peut être de type [fichier](../service/file.md) :
|
||||
|
||||
```
|
||||
<target type="filelist">example</target>
|
||||
```
|
||||
|
||||
### iplist
|
||||
|
||||
Une cible peut être de type [ip](../service/ip.md) :
|
||||
|
||||
```
|
||||
<target type="iplist">example</target>
|
||||
```
|
||||
|
||||
## La cible optionnelle
|
||||
|
||||
Mais une target peut être optionnelle. C'est à dire que si la \*list n'existe pas, l'action ne sera pas associé.
|
||||
|
||||
```
|
||||
<target type="filelist" optional="True">unknown</target>
|
||||
```
|
@ -1,4 +1,4 @@
|
||||
# Cible de la fonction de type "variable"
|
||||
# Cible de type "variable"
|
||||
|
||||
Par défaut une cible est de type variable.
|
||||
|
||||
|
@ -1,4 +1,255 @@
|
||||
# Variable
|
||||
|
||||
- [Une variable](simple.md)
|
||||
- [Variable meneuse ou suiveuse](leadership.md)
|
||||
## Un variable
|
||||
|
||||
Une variable est forcement dans [variables](../variables.md) ou dans une [famille](../family/README.md).
|
||||
|
||||
Une variable est déjà un nom. C'est à dire qu'on pourra utiliser plus tard la variable via ce nom.
|
||||
|
||||
```
|
||||
<variables>
|
||||
<variable name="my_variable"/>
|
||||
<family name="my_family">
|
||||
<variable name="my_family_variable"/>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
## Description et aide sur la variable
|
||||
|
||||
En plus d'un nom, il est possible de mettre une "description" à la variable. C'est une information "utilisateur" qui nous permettra d'avoir des informations complémentaires sur le contenu de cette variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" description="This is a greate variable"/>
|
||||
```
|
||||
|
||||
En plus de la description, il est possible de préciser une aide complémentaire :
|
||||
|
||||
```
|
||||
<variable name="my_variable" help="This is a greate variable"/>
|
||||
```
|
||||
|
||||
Cette aide peut être utilisé à tout moment comme valeur [d'un paramètre](../param/information.md).
|
||||
|
||||
## Le type de la variable
|
||||
|
||||
Une variable a un type. Ce type permet de définir les valeurs acceptées par cette variable :
|
||||
|
||||
- string : chaine de caractère (type par défaut)
|
||||
- number : un nombre
|
||||
- float : un chiffre flottant
|
||||
- boolean : "True" ou "False", si aucune valeur n'est défini la valeur par défaut de cette variable sera "True", ces variables sont également obligatoire par défaut
|
||||
- password : un mot de passe
|
||||
- mail : une adresse mail
|
||||
- filename : nom de fichier au sens Unix (exemple : "/etc/passwd")
|
||||
- date : une date au format "%Y-%m-%d" (exemple : "2021-01-30")
|
||||
- unix_user : nom d'utilisateur au sens Unix
|
||||
- ip : n'importe quelle adresse IPv4
|
||||
- cidr : n'importe quelle adresse IPv4 au format CIDR
|
||||
- local_ip : adresse IPv4 sur un réseau local, si l'adresse IPv4 n'est pas local, un warning sera afficher mais la valeur sera accepté tout de même
|
||||
- netmask : masque d'une adresse IPv4
|
||||
- network : adresse réseau
|
||||
- network_cidr : adresse réseau au format CIDR
|
||||
- broadcast : adresse de diffusion
|
||||
- netbios : nom netbios
|
||||
- domain : nom de domaine
|
||||
- hostname : nom d'hôte
|
||||
- web_address : adresse web (http://www.cadoles.com/)
|
||||
- port : port
|
||||
- mac : adresse MAC
|
||||
- schedule : périodicité du schedule, les valeurs possibles sont "none", "daily", "weekly" ou "monthly"
|
||||
- schedulemod : type de schedule, les valeurs possibles sont "pre" ou "post"
|
||||
|
||||
Pour définir le type d'une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" type="number"/>
|
||||
```
|
||||
|
||||
## Variable à valeur multiple
|
||||
|
||||
Par défaut une variable ne peut acceuillir qu'une seule valeur. Il peut être utile de pouvoir spécifier plusieurs valeurs à une même variable.
|
||||
|
||||
Pour définir une variable à valeur multiple :
|
||||
|
||||
```
|
||||
<variable name="my_variable" multi="True"/>
|
||||
```
|
||||
|
||||
## Variable invisible
|
||||
|
||||
Il est possible de cacher une variable.
|
||||
|
||||
Cacher une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service.
|
||||
Par contre cette variable sera accessibles lorsqu'on va l'utiliser.
|
||||
|
||||
Pour cacher une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" hidden="True"/>
|
||||
```
|
||||
|
||||
## Variable désactive
|
||||
|
||||
Il est possible de désactiver une variable.
|
||||
|
||||
Désactiver une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service mais également lorsqu'on va l'utiliser.
|
||||
|
||||
Pour désactiver une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" disabled="True"/>
|
||||
```
|
||||
|
||||
## Variable obligatoire
|
||||
|
||||
Variable dont une valeur est requise :
|
||||
|
||||
```
|
||||
<variable name="my_variable" mandatory="True"/>
|
||||
```
|
||||
|
||||
Les variables booléans sont par défaut obligatoire. Pour qu'une variable booléan ne soit pas obligatoire il faut le préciser explicitement :
|
||||
|
||||
```
|
||||
<variable name="my_variable" type="boolean" mandatory="False"/>
|
||||
```
|
||||
|
||||
Les variables avec une valeur par défaut (non calculée) sont également automatiquement obligatoire.
|
||||
[Les variables à choix](../check/valid_enum.md) sans choix "None" sont également automatiquement obligatoire.
|
||||
|
||||
## Valeur par défaut d'une variable
|
||||
|
||||
Il est possible de fixer les valeurs par défaut d'une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable">
|
||||
<value>value</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
Pour une variable multiple, il est possible de préciser plusieurs valeurs :
|
||||
|
||||
```
|
||||
<variable name="my_variable" multi="True">
|
||||
<value>value 1</value>
|
||||
<value>value 2</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
Si la variable n'est pas pas une [variable meneuse](../family/leadership.md), la première valeur défini dans cette liste sera également la valeur par défaut proposé si on ajoute une nouvelle valeur à cette variable.
|
||||
|
||||
Une valeur par défaut peut également être [une valeur calculer](../fill/README.md).
|
||||
|
||||
## Redéfinir une variable
|
||||
|
||||
Il est possible de définir une variable dans un dictionnaire et de changer son comportement dans une second dictionnaire.
|
||||
|
||||
Attention trois attributs ne sont redéfinisable :
|
||||
|
||||
- name
|
||||
- type
|
||||
- multi
|
||||
|
||||
Créons notre variable :
|
||||
|
||||
<variable name="my_variable"/>
|
||||
|
||||
Et redéfinisons là :
|
||||
|
||||
```
|
||||
<variable name="my_variable" redefine="True" description="New description"/>
|
||||
```
|
||||
|
||||
## Créer une variable inexistante
|
||||
|
||||
Il est parfois utile de créer une variable si elle n'existe pas dans un autre dictionnaire :
|
||||
|
||||
```
|
||||
<variable name="my_variable" exists="False"/>
|
||||
```
|
||||
|
||||
Si cette variable existe dans un autre dictionnaire, elle ne sera pas modifié ni recréé
|
||||
|
||||
## Redéfinir une variable si elle existe
|
||||
|
||||
Parfois on veut pouvoir redéfinir une variable mais seulement dans le cas où elle existe déjà :
|
||||
|
||||
```
|
||||
<variable name="my_variable" redefine="True" exists="True" hidden="True"/>
|
||||
```
|
||||
|
||||
## Variable à valeur automatiquement modifiée
|
||||
|
||||
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considéré comme modifié quand la propriété global "force_store_value" de Tiramisu est mise.
|
||||
|
||||
Voici une variable a valeur automatiquement modifiée :
|
||||
|
||||
```
|
||||
<variable name="my_variable" auto_save="True">
|
||||
<value>my_value</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
Dans ce cas la valeur est fixée à la valeur actuelle.
|
||||
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
|
||||
|
||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont automatiquement modifiées que si elles ont une valeurs.
|
||||
|
||||
Une [variable meneuse ou suiveuse](../family/leadership.md) ne peut pas avoir la propriété auto_save.
|
||||
|
||||
## Variable à valeur en lecture seule automatique
|
||||
|
||||
Une variable avec valeur en lecture seule automatique est une variable dont la valeur ne sera plus modifiable par l'utilisateur quand la [variable "server_deployed" passe à "True"](../dev/config.md).
|
||||
|
||||
Voici un variable à valeur en lecture seule automatique :
|
||||
|
||||
```
|
||||
<variable name="server_deployed" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
<variable name="my_variable" auto_freeze="True"/>
|
||||
```
|
||||
|
||||
Dans ce cas la valeur est fixée à la valeur actuelle et elle ne sera plus modifiable par l'utilisateur.
|
||||
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
|
||||
|
||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont en lecteur seul que si elles ont une valeurs.
|
||||
|
||||
Une [variable meneuse ou suiveuse](../family/leadership.md) ne peut pas avoir la propriété auto_freeze.
|
||||
|
||||
## Information "test"
|
||||
|
||||
L'attribut "test" est un attribut spécial qui permet aux concepteurs d'un dictionnaire d'influancer le robot de test en précisant de valeurs utile à tester.
|
||||
|
||||
Concrêtement, le contenu de cet attribut est enregister dans une "information" de l'option Tiramisu correspondante.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
<variable name="my_variable" test="yes"/>
|
||||
```
|
||||
|
||||
Il est possible de préciser plusieurs valeurs avec le séparateur "|" :
|
||||
|
||||
```
|
||||
<variable name="my_variable" test="yes|no"/>
|
||||
```
|
||||
|
||||
Cette valeur peut être utilisé à tout moment comme valeur [d'un paramètre](../param/information.md).
|
||||
|
||||
## Mode de la variable
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une variable correspond au [mode](../mode.md) de la [famille](../family/README.md).
|
||||
|
||||
Cas particuliers :
|
||||
|
||||
- une variable à valeur automatiquement modifiée ou une variable en lecture seule automatique est par défaut en mode "basic".
|
||||
- si la variable n'est pas dans une famille, la variable aura le mode "normal" par défaut.
|
||||
- une variable obligatoire sans valeur par défaut (calculer ou non) aura le mode "basic".
|
||||
|
||||
Pour définir le [mode](../mode.md) :
|
||||
|
||||
```
|
||||
<variable name="my_variable" mode="expert"/>
|
||||
```
|
||||
|
@ -8,6 +8,8 @@ Une variable meneuse est une [variable](simple.md) qui est obligatoirement de ty
|
||||
|
||||
Une variable meneuse peut être obligatoire.
|
||||
|
||||
Le [mode](../mode.md) par défaut correspond au plus petit mode définit par l'utilisateur des variables suiveuses.
|
||||
|
||||
## Variable suiveuse
|
||||
|
||||
Une variable suiveuse est une variable donc la longueur n'est pas déterminé par elle-même, mais est identique à celle de la variable meneuse dont elle dépend.
|
||||
@ -58,6 +60,30 @@ Il est possible d'en définit d'autres :
|
||||
</constraints>
|
||||
```
|
||||
|
||||
## Définition des variables meneuse et suiveuse dans un dictionnaire extra
|
||||
|
||||
Voici un exemple de définition d'une variable meneuse et de deux variables meneuses dans un espace de nom "example" :
|
||||
|
||||
```
|
||||
<variables>
|
||||
<family name="family">
|
||||
<variable name="leader" multi='True'/>
|
||||
<variable name="follower1"/>
|
||||
<variable name="follower2" multi='True'/>
|
||||
</family>
|
||||
</variables>
|
||||
<constraints>
|
||||
<group leader="example.family.leader">
|
||||
<follower>follower1</follower>
|
||||
<follower>follower2</follower>
|
||||
</group>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
Le chemin de la variable meneuse est traditionnel, par contre le chemin des variables suiveuses est le chemin relatif de la variable.
|
||||
|
||||
Le chemin d'une variable suiveuse est normalement "example.family.leader.follower1" mais la variable n'est pas encore une variable suiveuse à ce moment là du traitement. C'est pour cela qu'il ne faut, uniquement dans les groupes, mettre le nom de la variable suiveuse.
|
||||
|
||||
## Ajout d'une nouvelle variable suiveuse
|
||||
|
||||
Pour ajouter, dans un nouveau dictionnaire, une variable suiveuse à notre groupe, rien de plus simple, il suffit de redéfinir un groupe avec cette (ou ces) nouvelle variable :
|
||||
|
@ -1,222 +0,0 @@
|
||||
# Variable
|
||||
|
||||
## Un variable
|
||||
|
||||
Une variable est forcement dans [variables](../variables.md) ou dans une [famille](../family/README.md).
|
||||
|
||||
Une variable est déjà un nom. C'est à dire qu'on pourra utiliser plus tard la variable via ce nom.
|
||||
|
||||
```
|
||||
<variables>
|
||||
<variable name="my_variable"/>
|
||||
<family name="my_family">
|
||||
<variable name="my_family_variable"/>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
## Description et aide sur la variable
|
||||
|
||||
En plus d'un nom, il est possible de mettre une "description" à la variable. C'est une information "utilisateur" qui nous permettra d'avoir des informations complémentaires sur le contenu de cette variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" description="This is a greate variable"/>
|
||||
```
|
||||
|
||||
En plus de la description, il est possible de préciser une aide complémentaire :
|
||||
|
||||
```
|
||||
<variable name="my_variable" help="This is a greate variable"/>
|
||||
```
|
||||
|
||||
## Le type de la variable
|
||||
|
||||
Une variable a un type. Ce type permet de définir les valeurs acceptées par cette variable :
|
||||
|
||||
- string : chaine de caractère (type par défaut)
|
||||
- number : un nombre
|
||||
- float : un chiffre flottant
|
||||
- boolean : "True" ou "False", si aucune valeur n'est défini la valeur par défaut de cette variable sera "True"
|
||||
- password : un mot de passe
|
||||
- mail : une adresse mail
|
||||
- filename : nom de fichier au sens Unix (exemple : "/etc/passwd")
|
||||
- date : une date au format "%Y-%m-%d" (exemple : "2021-01-30")
|
||||
- unix_user : nom d'utilisateur au sens Unix
|
||||
- ip : n'importe quelle adresse IPv4
|
||||
- cidr : n'importe quelle adresse IPv4 au format CIDR
|
||||
- local_ip : adresse IPv4 sur un réseau local, si l'adresse IPv4 n'est pas local, un warning sera afficher mais la valeur sera accepté tout de même
|
||||
- netmask : masque d'une adresse IPv4
|
||||
- network : adresse réseau
|
||||
- network_cidr : adresse réseau au format CIDR
|
||||
- broadcast : adresse de diffusion
|
||||
- netbios : nom netbios
|
||||
- domain : nom de domaine
|
||||
- hostname : nom d'hôte
|
||||
- web_address : adresse web (http://www.cadoles.com/)
|
||||
- port : port
|
||||
- mac : adresse MAC
|
||||
- schedule : périodicité du schedule, les valeurs possibles sont "none", "daily", "weekly" ou "monthly"
|
||||
- schedulemod : type de schedule, les valeurs possibles sont "pre" ou "post"
|
||||
|
||||
Pour définir le type d'une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" type="number"/>
|
||||
```
|
||||
|
||||
## Variable à valeur multiple
|
||||
|
||||
Par défaut une variable ne peut acceuillir qu'une seule valeur. Il peut être utile de pouvoir spécifier plusieurs valeurs à une même variable.
|
||||
|
||||
Pour définir une variable à valeur multiple :
|
||||
|
||||
```
|
||||
<variable name="my_variable" multi="True"/>
|
||||
```
|
||||
|
||||
## Mode de la variable
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une variable correspond au [mode](../mode.md) de la [famille](../family/README.md).
|
||||
|
||||
Si la variable n'est pas dans une famille, la variable aura le mode "normal" par défaut.
|
||||
|
||||
Pour définir le [mode](../mode.md) :
|
||||
|
||||
```
|
||||
<variable name="my_variable" mode="expert"/>
|
||||
```
|
||||
|
||||
## Variable invisible
|
||||
|
||||
Il est possible de cacher une variable.
|
||||
|
||||
Cacher une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service.
|
||||
Par contre cette variable sera accessibles lorsqu'on va l'utiliser.
|
||||
|
||||
Pour cacher une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" hidden="True"/>
|
||||
```
|
||||
|
||||
## Variable désactive
|
||||
|
||||
Il est possible de désactiver une variable.
|
||||
|
||||
Désactiver une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service mais également lorsqu'on va l'utiliser.
|
||||
|
||||
Pour désactiver une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable" disabled="True"/>
|
||||
```
|
||||
|
||||
## Variable obligatoire
|
||||
|
||||
Variable dont une valeur est requise :
|
||||
|
||||
```
|
||||
<variable name="my_variable" mandatory="True"/>
|
||||
```
|
||||
|
||||
## Valeur par défaut d'une variable
|
||||
|
||||
Il est possible de fixer les valeurs par défaut d'une variable :
|
||||
|
||||
```
|
||||
<variable name="my_variable">
|
||||
<value>value</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
Pour une variable multiple, il est possible de préciser plusieurs valeurs :
|
||||
|
||||
```
|
||||
<variable name="my_variable" multi="True">
|
||||
<value>value 1</value>
|
||||
<value>value 2</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
Si la variable n'est pas pas une [variable meneuse](leadership.md), la première valeur défini dans cette liste sera également la valeur par défaut proposé si on ajoute une nouvelle valeur à cette variable.
|
||||
|
||||
Une valeur par défaut peut également être [une valeur calculer](../fill/README.md).
|
||||
|
||||
## Redéfinir une variable
|
||||
|
||||
Il est possible de définir une variable dans un dictionnaire et de changer son comportement dans une second dictionnaire.
|
||||
|
||||
Attention trois attributs ne sont redéfinisable :
|
||||
|
||||
- name
|
||||
- type
|
||||
- multi
|
||||
|
||||
Créons notre variable :
|
||||
|
||||
<variable name="my_variable"/>
|
||||
|
||||
Et redéfinisons là :
|
||||
|
||||
```
|
||||
<variable name="my_variable" redefine="True" description="New description"/>
|
||||
```
|
||||
|
||||
## Créer une variable inexistante
|
||||
|
||||
Il est parfois utile de créer une variable si elle n'existe pas dans un autre dictionnaire :
|
||||
|
||||
```
|
||||
<variable name="my_variable" exists="False"/>
|
||||
```
|
||||
|
||||
Si cette variable existe dans un autre dictionnaire, elle ne sera pas modifié ni recréé
|
||||
|
||||
## Redéfinir une variable si elle existe
|
||||
|
||||
Parfois on veut pouvoir redéfinir une variable mais seulement dans le cas où elle existe déjà :
|
||||
|
||||
```
|
||||
<variable name="my_variable" redefine="True" exists="True" hidden="True"/>
|
||||
```
|
||||
|
||||
## Variable à valeur automatiquement modifiée
|
||||
|
||||
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considéré comme modifié quand la propriété global "force_store_value" de Tiramisu est mise.
|
||||
|
||||
Voici une variable a valeur automatiquement modifiée :
|
||||
|
||||
```
|
||||
<variable name="my_variable" auto_save="True">
|
||||
<value>my_value</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
Dans ce cas la valeur est fixée à la valeur actuelle.
|
||||
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
|
||||
|
||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont automatiquement modifiées que si elles ont une valeurs.
|
||||
|
||||
Une [variable meneuse ou suiveuse](leadership.md) ne peut pas avoir la propriété auto_save.
|
||||
|
||||
## Variable à valeur en lecture seule automatique
|
||||
|
||||
Une variable avec valeur en lecture seule automatique est une variable dont la valeur ne sera plus modifiable par l'utilisateur quand la [variable "instanciated_module" passe à "True"](../dev/config.md).
|
||||
|
||||
Voici un variable à valeur en lecture seule automatique :
|
||||
|
||||
```
|
||||
<variable name="instanciated_module" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
<variable name="my_variable" auto_freeze="True"/>
|
||||
```
|
||||
|
||||
Dans ce cas la valeur est fixée à la valeur actuelle et elle ne sera plus modifiable par l'utilisateur.
|
||||
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
|
||||
|
||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont en lecteur seul que si elles ont une valeurs.
|
||||
|
||||
Une [variable meneuse ou suiveuse](leadership.md) ne peut pas avoir la propriété auto_freeze.
|
||||
|
||||
FIXME <!ATTLIST variable test CDATA #IMPLIED>
|
@ -25,8 +25,8 @@ along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from .convert import RougailConvert
|
||||
from .template import RougailTemplate
|
||||
from .template.systemd import RougailSystemdTemplate
|
||||
from .config import RougailConfig
|
||||
from .annotator import modes
|
||||
from rougail.update import RougailUpgrade
|
||||
|
||||
__ALL__ = ('RougailConvert', 'RougailTemplate', 'RougailConfig', 'modes')
|
||||
__ALL__ = ('RougailConvert', 'RougailSystemdTemplate', 'RougailConfig', 'RougailUpgrade')
|
||||
|
@ -24,34 +24,59 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from .group import GroupAnnotator
|
||||
from .service import ServiceAnnotator
|
||||
from .variable import VariableAnnotator, CONVERT_OPTION
|
||||
from .check import CheckAnnotator
|
||||
from .value import ValueAnnotator
|
||||
from .condition import ConditionAnnotator
|
||||
from .fill import FillAnnotator
|
||||
from .family import FamilyAnnotator, modes
|
||||
from .property import PropertyAnnotator
|
||||
from .variable import CONVERT_OPTION
|
||||
import importlib.resources
|
||||
from os.path import dirname, join
|
||||
from ..utils import load_modules
|
||||
|
||||
|
||||
ANNOTATORS = None
|
||||
|
||||
|
||||
if not 'files' in dir(importlib.resources):
|
||||
# old python version
|
||||
class fake_files:
|
||||
def __init__(self, package):
|
||||
self.mod = []
|
||||
dir_package = dirname(importlib.resources._get_package(package).__file__)
|
||||
for mod in importlib.resources.contents(package):
|
||||
self.mod.append(join(dir_package, mod))
|
||||
|
||||
def iterdir(self):
|
||||
return self.mod
|
||||
importlib.resources.files = fake_files
|
||||
|
||||
|
||||
def get_level(module):
|
||||
return module.level
|
||||
|
||||
|
||||
def get_annotators(annotators, module_name):
|
||||
annotators[module_name] = []
|
||||
for pathobj in importlib.resources.files(module_name).iterdir():
|
||||
path = str(pathobj)
|
||||
if not path.endswith('__') and not path.endswith('__.py'):
|
||||
module = load_modules(path)
|
||||
if 'Annotator' in dir(module):
|
||||
annotators[module_name].append(module.Annotator)
|
||||
|
||||
|
||||
class SpaceAnnotator: # pylint: disable=R0903
|
||||
"""Transformations applied on a CreoleObjSpace instance
|
||||
"""Transformations applied on a object instance
|
||||
"""
|
||||
def __init__(self, objectspace, eosfunc_file):
|
||||
self.objectspace = objectspace
|
||||
GroupAnnotator(objectspace)
|
||||
ServiceAnnotator(objectspace)
|
||||
VariableAnnotator(objectspace)
|
||||
CheckAnnotator(objectspace,
|
||||
eosfunc_file,
|
||||
)
|
||||
ConditionAnnotator(objectspace)
|
||||
FillAnnotator(objectspace,
|
||||
eosfunc_file,
|
||||
)
|
||||
ValueAnnotator(objectspace)
|
||||
FamilyAnnotator(objectspace)
|
||||
PropertyAnnotator(objectspace)
|
||||
global ANNOTATORS
|
||||
if ANNOTATORS is None:
|
||||
ANNOTATORS = {}
|
||||
get_annotators(ANNOTATORS, 'rougail.annotator')
|
||||
for extra_annotator in objectspace.rougailconfig['extra_annotators']:
|
||||
get_annotators(ANNOTATORS, extra_annotator)
|
||||
annotators = ANNOTATORS['rougail.annotator'].copy()
|
||||
for extra_annotator in objectspace.rougailconfig['extra_annotators']:
|
||||
annotators.extend(ANNOTATORS[extra_annotator])
|
||||
annotators = sorted(annotators, key=get_level)
|
||||
for annotator in annotators:
|
||||
annotator(objectspace, eosfunc_file)
|
||||
|
||||
|
||||
__all__ = ('SpaceAnnotator', 'CONVERT_OPTION', 'modes')
|
||||
__all__ = ('SpaceAnnotator', 'CONVERT_OPTION')
|
||||
|
@ -26,21 +26,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import List, Any
|
||||
|
||||
from .target import TargetAnnotator
|
||||
from .param import ParamAnnotator
|
||||
from rougail.annotator.target import TargetAnnotator
|
||||
from rougail.annotator.param import ParamAnnotator
|
||||
|
||||
from ..utils import load_modules
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError, display_xmlfiles
|
||||
from rougail.utils import load_modules
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError, display_xmlfiles
|
||||
|
||||
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
|
||||
|
||||
class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
class Annotator(TargetAnnotator, ParamAnnotator):
|
||||
"""Annotate check
|
||||
"""
|
||||
level = 40
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
eosfunc_file,
|
||||
*args,
|
||||
):
|
||||
if not hasattr(objectspace.space, 'constraints') or \
|
||||
not hasattr(objectspace.space.constraints, 'check'):
|
||||
@ -50,7 +52,9 @@ class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
self.only_variable = True
|
||||
self.functions = dir(load_modules(eosfunc_file))
|
||||
self.functions.extend(INTERNAL_FUNCTIONS)
|
||||
self.functions.extend(self.objectspace.rougailconfig['internal_functions'])
|
||||
self.target_is_uniq = False
|
||||
self.allow_function = True
|
||||
self.convert_target(self.objectspace.space.constraints.check)
|
||||
self.convert_param(self.objectspace.space.constraints.check)
|
||||
self.check_check()
|
||||
@ -109,7 +113,7 @@ class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
# check value
|
||||
self.check_valid_enum_value(target.name, values)
|
||||
else:
|
||||
# no value, set the first choice has default value
|
||||
# no value, set the first choice as default value
|
||||
new_value = self.objectspace.value(check.xmlfiles)
|
||||
new_value.name = values[0]
|
||||
new_value.type = variable_type
|
||||
@ -123,8 +127,6 @@ class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
variable,
|
||||
check,
|
||||
) -> List[Any]:
|
||||
# value for choice's variable is mandatory
|
||||
variable.mandatory = True
|
||||
# build choice
|
||||
variable.values = []
|
||||
variable.ori_type = variable.type
|
||||
@ -132,11 +134,24 @@ class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
|
||||
has_variable = False
|
||||
values = []
|
||||
has_nil = False
|
||||
is_function = False
|
||||
for param in check.param:
|
||||
if has_variable:
|
||||
msg = _(f'only one "variable" parameter is allowed for valid_enum '
|
||||
f'of variable "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 5, param.xmlfiles)
|
||||
if param.type == 'function':
|
||||
is_function = True
|
||||
choice = self.objectspace.choice(variable.xmlfiles)
|
||||
choice.name = param.text
|
||||
choice.type = 'function'
|
||||
choice.param = []
|
||||
variable.values.append(choice)
|
||||
continue
|
||||
if is_function:
|
||||
variable.values[0].param.append(param)
|
||||
continue
|
||||
param_type = variable.ori_type
|
||||
if param.type == 'variable':
|
||||
has_variable = True
|
||||
@ -148,21 +163,29 @@ class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
msg = _(f'only multi "variable" parameter is allowed for valid_enum '
|
||||
f'of variable "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 6, param.xmlfiles)
|
||||
param_type = 'calculation'
|
||||
param_type = 'variable'
|
||||
elif param.type == 'nil':
|
||||
has_nil = True
|
||||
values.append(param.text)
|
||||
choice = self.objectspace.choice(variable.xmlfiles)
|
||||
choice.name = param.text
|
||||
choice.type = param_type
|
||||
variable.values.append(choice)
|
||||
|
||||
if is_function:
|
||||
return None
|
||||
if 'mandatory' not in vars(variable):
|
||||
variable.mandatory = not has_nil
|
||||
elif variable.mandatory is False:
|
||||
choice = self.objectspace.choice(variable.xmlfiles)
|
||||
choice.name = None
|
||||
choice.type = 'nil'
|
||||
variable.values.append(choice)
|
||||
if has_variable:
|
||||
return None
|
||||
|
||||
for target in check.target:
|
||||
self.objectspace.valid_enums[target.name.path] = {'type': variable.ori_type,
|
||||
'values': values,
|
||||
'xmlfiles': check.xmlfiles,
|
||||
}
|
||||
self.objectspace.valid_enums[variable.path] = {'type': variable.ori_type,
|
||||
'values': values,
|
||||
'xmlfiles': check.xmlfiles,
|
||||
}
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
|
@ -24,23 +24,24 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import List, Any
|
||||
from typing import List
|
||||
|
||||
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from ..config import RougailConfig
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
from .target import TargetAnnotator
|
||||
from .param import ParamAnnotator
|
||||
from .variable import Walk
|
||||
from rougail.annotator.target import TargetAnnotator
|
||||
from rougail.annotator.param import ParamAnnotator
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
|
||||
class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
class Annotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
"""Annotate condition
|
||||
"""
|
||||
level = 50
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
):
|
||||
self.objectspace = objectspace
|
||||
self.force_service_value = {}
|
||||
@ -51,8 +52,9 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
return
|
||||
self.target_is_uniq = False
|
||||
self.only_variable = False
|
||||
self.allow_function = False
|
||||
self.convert_target(self.objectspace.space.constraints.condition)
|
||||
self.check_condition_fallback()
|
||||
self.check_condition_optional()
|
||||
self.convert_condition_source()
|
||||
self.convert_param(self.objectspace.space.constraints.condition)
|
||||
self.check_source_target()
|
||||
@ -73,21 +75,24 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
only if auto_freeze_variable is True this variable is frozen
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
if not variable.auto_freeze:
|
||||
if not variable.auto_freeze and not variable.auto_save:
|
||||
continue
|
||||
#if variable.namespace != self.objectspace.rougailconfig['variable_namespace']:
|
||||
# msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}"')
|
||||
# raise DictConsistencyError(msg, 49, variable.xmlfiles)
|
||||
variable.force_store_value = True
|
||||
if variable.auto_save:
|
||||
continue
|
||||
if variable.namespace != RougailConfig['variable_namespace']:
|
||||
msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}"')
|
||||
raise DictConsistencyError(msg, 49, variable.xmlfiles)
|
||||
new_condition = self.objectspace.condition(variable.xmlfiles)
|
||||
new_condition.name = 'auto_frozen_if_not_in'
|
||||
new_condition.name = 'auto_frozen_if_in'
|
||||
new_condition.namespace = variable.namespace
|
||||
new_condition.source = RougailConfig['auto_freeze_variable']
|
||||
new_condition.source = self.objectspace.rougailconfig['auto_freeze_variable']
|
||||
new_param = self.objectspace.param(variable.xmlfiles)
|
||||
new_param.text = True
|
||||
new_condition.param = [new_param]
|
||||
new_target = self.objectspace.target(variable.xmlfiles)
|
||||
new_target.type = 'variable'
|
||||
new_target.name = variable.name
|
||||
new_target.name = variable.path
|
||||
new_condition.target = [new_target]
|
||||
if not hasattr(self.objectspace.space, 'constraints'):
|
||||
self.objectspace.space.constraints = self.objectspace.constraints(variable.xmlfiles)
|
||||
@ -106,21 +111,18 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
f'{condition.source.path}')
|
||||
raise DictConsistencyError(msg, 11, condition.xmlfiles)
|
||||
|
||||
def check_condition_fallback(self):
|
||||
"""a condition with a fallback **and** the source variable doesn't exist
|
||||
def check_condition_optional(self):
|
||||
"""a condition with a optional **and** the source variable doesn't exist
|
||||
"""
|
||||
remove_conditions = []
|
||||
for idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
||||
# fallback
|
||||
if condition.fallback is False or \
|
||||
if condition.optional is False or \
|
||||
self.objectspace.paths.path_is_defined(condition.source):
|
||||
continue
|
||||
if condition.name in ['disabled_if_in', 'mandatory_if_in', 'hidden_if_in']:
|
||||
apply_action = not condition.force_condition_on_fallback
|
||||
else:
|
||||
apply_action = condition.force_inverse_condition_on_fallback
|
||||
remove_conditions.append(idx)
|
||||
if apply_action:
|
||||
if (hasattr(condition, 'apply_on_fallback') and condition.apply_on_fallback) or \
|
||||
(not hasattr(condition, 'apply_on_fallback') and \
|
||||
condition.name.endswith('_if_in')):
|
||||
self.force_actions_to_variable(condition)
|
||||
remove_conditions.sort(reverse=True)
|
||||
for idx in remove_conditions:
|
||||
@ -150,8 +152,8 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
"""
|
||||
if condition_name.startswith('hidden_if_'):
|
||||
return ['hidden', 'frozen', 'force_default_on_freeze']
|
||||
if condition_name == 'auto_frozen_if_not_in':
|
||||
return ['auto_frozen']
|
||||
if condition_name == 'auto_frozen_if_in':
|
||||
return ['frozen']
|
||||
return [condition_name.split('_', 1)[0]]
|
||||
|
||||
def _get_family_variables_from_target(self,
|
||||
@ -162,13 +164,12 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
return target.name, [target.name]
|
||||
# it's a leader, so apply property to leadership
|
||||
family_name = self.objectspace.paths.get_variable_family_path(target.name.path)
|
||||
family = self.objectspace.paths.get_family(family_name,
|
||||
target.name.namespace,
|
||||
)
|
||||
return family, family.variable
|
||||
# it's a family
|
||||
variable = self.objectspace.paths.get_family(target.name.path,
|
||||
target.namespace,
|
||||
namespace = target.name.namespace
|
||||
else:
|
||||
family_name = target.name.path
|
||||
namespace = target.namespace
|
||||
variable = self.objectspace.paths.get_family(family_name,
|
||||
namespace,
|
||||
)
|
||||
if hasattr(variable, 'variable'):
|
||||
return variable, list(variable.variable.values())
|
||||
@ -190,6 +191,9 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
listvars,
|
||||
fills,
|
||||
)
|
||||
elif not target.optional:
|
||||
msg = f'cannot found target "{target.type}" "{target.name}"'
|
||||
raise DictConsistencyError(_(msg), 2, target.xmlfiles)
|
||||
remove_targets.append(target_idx)
|
||||
remove_targets.sort(reverse=True)
|
||||
for target_idx in remove_targets:
|
||||
@ -237,17 +241,23 @@ class ConditionAnnotator(TargetAnnotator, ParamAnnotator, Walk):
|
||||
or_needed = len(condition.param) != 1
|
||||
if len(condition.param) == 1:
|
||||
values = getattr(condition.param[0], 'text', None)
|
||||
param_type = condition.param[0].type
|
||||
else:
|
||||
values = tuple([getattr(param, 'text', None) for param in condition.param])
|
||||
param_type = None
|
||||
for param in condition.param:
|
||||
if param_type is None or param_type == 'nil':
|
||||
param_type = param.type
|
||||
param3 = self.objectspace.param(target.xmlfiles)
|
||||
param3.name = f'condition_{fill.index}'
|
||||
param3.type = 'variable'
|
||||
param3.text = condition.source.path
|
||||
param3.propertyerror = False
|
||||
fill.param.append(param3)
|
||||
param4 = self.objectspace.param(target.xmlfiles)
|
||||
param4.name = f'expected_{fill.index}'
|
||||
param4.text = values
|
||||
param4.type = condition.param[0].type
|
||||
param4.type = param_type
|
||||
fill.param.append(param4)
|
||||
if condition.name != 'disabled_if_in':
|
||||
param5 = self.objectspace.param(target.xmlfiles)
|
||||
|
@ -24,24 +24,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from ..utils import normalize_family
|
||||
from .variable import Walk
|
||||
|
||||
|
||||
#mode order is important
|
||||
modes_level = ('basic', 'normal', 'expert')
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
|
||||
class Mode: # pylint: disable=R0903
|
||||
"""Class to manage mode level
|
||||
"""
|
||||
def __init__(self,
|
||||
name: str,
|
||||
level: int,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.level = level
|
||||
|
||||
def __gt__(self,
|
||||
@ -50,18 +43,18 @@ class Mode: # pylint: disable=R0903
|
||||
return other.level < self.level
|
||||
|
||||
|
||||
modes = {name: Mode(name, idx) for idx, name in enumerate(modes_level)}
|
||||
|
||||
|
||||
class FamilyAnnotator(Walk):
|
||||
class Annotator(Walk):
|
||||
"""Annotate family
|
||||
"""
|
||||
level = 80
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
):
|
||||
self.objectspace = objectspace
|
||||
if not hasattr(self.objectspace.space, 'variables'):
|
||||
return
|
||||
self.modes = {name: Mode(idx) for idx, name in enumerate(self.objectspace.rougailconfig['modes_level'])}
|
||||
self.remove_empty_families()
|
||||
self.family_names()
|
||||
self.change_modes()
|
||||
@ -83,6 +76,7 @@ class FamilyAnnotator(Walk):
|
||||
def remove_empty_families(self) -> None:
|
||||
"""Remove all families without any variable
|
||||
"""
|
||||
#FIXME pas sous family
|
||||
for families in self.objectspace.space.variables.values():
|
||||
removed_families = []
|
||||
for family_name, family in families.variable.items():
|
||||
@ -99,96 +93,174 @@ class FamilyAnnotator(Walk):
|
||||
family.description = family.name
|
||||
family.doc = family.description
|
||||
del family.description
|
||||
family.name = normalize_family(family.name)
|
||||
|
||||
def change_modes(self):
|
||||
"""change the mode of variables
|
||||
"""
|
||||
# default is high level
|
||||
new_family_mode = modes_level[-1]
|
||||
for family in self.objectspace.space.variables.values():
|
||||
self.change_family_mode(family,
|
||||
new_family_mode,
|
||||
first=True,
|
||||
)
|
||||
modes_level = self.objectspace.rougailconfig['modes_level']
|
||||
default_variable_mode = self.objectspace.rougailconfig['default_variable_mode']
|
||||
if default_variable_mode not in modes_level:
|
||||
msg = _(f'default variable mode "{default_variable_mode}" is not a valid mode, '
|
||||
f'valid modes are {modes_level}')
|
||||
raise DictConsistencyError(msg, 72, None)
|
||||
default_family_mode = self.objectspace.rougailconfig['default_family_mode']
|
||||
if default_family_mode not in modes_level:
|
||||
msg = _(f'default family mode "{default_family_mode}" is not a valid mode, '
|
||||
f'valid modes are {modes_level}')
|
||||
raise DictConsistencyError(msg, 73, None)
|
||||
families = list(self.get_families())
|
||||
for family in families:
|
||||
self.valid_mode(family)
|
||||
self._set_default_mode(family)
|
||||
families.reverse()
|
||||
for family in families:
|
||||
self._change_family_mode(family)
|
||||
|
||||
def change_family_mode(self,
|
||||
family: 'self.objectspace.family',
|
||||
new_family_mode: str,
|
||||
first: bool=False,
|
||||
) -> str:
|
||||
def valid_mode(self,
|
||||
obj,
|
||||
) -> None:
|
||||
modes_level = self.objectspace.rougailconfig['modes_level']
|
||||
if self._has_mode(obj) and obj.mode not in modes_level:
|
||||
msg = _(f'mode "{obj.mode}" for "{obj.name}" is not a valid mode, '
|
||||
f'valid modes are {modes_level}')
|
||||
raise DictConsistencyError(msg, 71, obj.xmlfiles)
|
||||
|
||||
def _set_default_mode(self,
|
||||
family: 'self.objectspace.family',
|
||||
) -> None:
|
||||
if not hasattr(family, 'variable'):
|
||||
return
|
||||
if self._has_mode(family):
|
||||
family_mode = family.mode
|
||||
else:
|
||||
family_mode = None
|
||||
leader = None
|
||||
for variable in family.variable.values():
|
||||
if leader is None and hasattr(family, 'leadership') and family.leadership:
|
||||
leader = variable
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
# set default mode a subfamily
|
||||
if family_mode and not self._has_mode(variable):
|
||||
self._set_auto_mode(variable, family_mode)
|
||||
else:
|
||||
# set default mode to a variable
|
||||
self.valid_mode(variable)
|
||||
if leader:
|
||||
self._set_default_mode_leader(leader, variable)
|
||||
self._set_default_mode_variable(variable, family_mode)
|
||||
if leader:
|
||||
# here because follower can change leader mode
|
||||
self._set_auto_mode(family, leader.mode)
|
||||
|
||||
@staticmethod
|
||||
def _has_mode(obj) -> bool:
|
||||
return 'mode' in vars(obj) and not hasattr(obj, 'mode_auto')
|
||||
|
||||
def _set_default_mode_variable(self,
|
||||
variable: 'self.objectspace.variable',
|
||||
family_mode: str,
|
||||
) -> None:
|
||||
# auto_save or auto_freeze variable is set to 'basic' mode
|
||||
# if its mode is not defined by the user
|
||||
if not self._has_mode(variable) and \
|
||||
(variable.auto_save is True or variable.auto_freeze is True):
|
||||
variable.mode = self.objectspace.rougailconfig['modes_level'][0]
|
||||
# mandatory variable without value is a basic variable
|
||||
elif not self._has_mode(variable) and \
|
||||
variable.mandatory is True and \
|
||||
not hasattr(variable, 'default') and \
|
||||
not hasattr(variable, 'default_multi'):
|
||||
variable.mode = self.objectspace.rougailconfig['modes_level'][0]
|
||||
elif family_mode and not self._has_mode(variable):
|
||||
self._set_auto_mode(variable, family_mode)
|
||||
|
||||
@staticmethod
|
||||
def _set_auto_mode(obj, mode: str) -> None:
|
||||
obj.mode = mode
|
||||
obj.mode_auto = True
|
||||
|
||||
def _set_default_mode_leader(self,
|
||||
leader: 'self.objectspace.variable',
|
||||
follower: 'self.objectspace.variable',
|
||||
) -> None:
|
||||
if follower.auto_save is True:
|
||||
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
|
||||
raise DictConsistencyError(msg, 29, follower.xmlfiles)
|
||||
if follower.auto_freeze is True:
|
||||
msg = f'leader/followers "{follower.name}" could not be auto_freeze'
|
||||
raise DictConsistencyError(_(msg), 30, follower.xmlfiles)
|
||||
if leader == follower:
|
||||
# it's a leader
|
||||
if not hasattr(leader, 'mode'):
|
||||
self._set_auto_mode(leader, self.objectspace.rougailconfig['default_variable_mode'])
|
||||
return
|
||||
if self._has_mode(follower):
|
||||
follower_mode = follower.mode
|
||||
else:
|
||||
follower_mode = self.objectspace.rougailconfig['default_variable_mode']
|
||||
if self.modes[leader.mode] > self.modes[follower_mode]:
|
||||
if self._has_mode(follower) and not self._has_mode(leader):
|
||||
# if follower has mode but not the leader
|
||||
self._set_auto_mode(leader, follower_mode)
|
||||
else:
|
||||
# leader's mode is minimum level
|
||||
if self._has_mode(follower):
|
||||
msg = _(f'the follower "{follower.name}" is in "{follower_mode}" mode '
|
||||
f'but leader have the higher mode "{leader.mode}"')
|
||||
raise DictConsistencyError(msg, 63, follower.xmlfiles)
|
||||
self._set_auto_mode(follower, leader.mode)
|
||||
|
||||
def _change_family_mode(self,
|
||||
family: 'self.objectspace.family',
|
||||
) -> None:
|
||||
if hasattr(family, 'mode'):
|
||||
family_mode = family.mode
|
||||
else:
|
||||
family_mode = new_family_mode
|
||||
family_mode = self.objectspace.rougailconfig['default_family_mode']
|
||||
min_variable_mode = self.objectspace.rougailconfig['modes_level'][-1]
|
||||
# change variable mode, but not if variables are not in a family
|
||||
is_leadership = hasattr(family, 'leadership') and family.leadership
|
||||
if hasattr(family, 'variable'):
|
||||
# change variable mode, but not if variables are not in a family
|
||||
for variable in family.variable.values():
|
||||
for idx, variable in enumerate(family.variable.values()):
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
new_family_mode = self.change_family_mode(variable,
|
||||
new_family_mode,
|
||||
)
|
||||
if not hasattr(variable, 'mode'):
|
||||
variable.mode = self.objectspace.rougailconfig['default_family_mode']
|
||||
elif idx == 0 and is_leadership:
|
||||
variable.mode = None
|
||||
continue
|
||||
if first:
|
||||
continue
|
||||
if isinstance(variable, self.objectspace.leadership):
|
||||
func = self._change_variable_mode_leader
|
||||
else:
|
||||
func = self._change_variabe_mode
|
||||
func(variable,
|
||||
family_mode,
|
||||
)
|
||||
if modes[new_family_mode] > modes[variable.mode]:
|
||||
new_family_mode = variable.mode
|
||||
if not first:
|
||||
self._change_variable_mode(variable, family_mode, is_leadership)
|
||||
if self.modes[min_variable_mode] > self.modes[variable.mode]:
|
||||
min_variable_mode = variable.mode
|
||||
if not isinstance(family, self.objectspace.family) or is_leadership:
|
||||
# it's Variable, Service, ... and leadership
|
||||
return
|
||||
if not hasattr(family, 'mode'):
|
||||
# set the lower variable mode to family
|
||||
family.mode = new_family_mode
|
||||
return new_family_mode
|
||||
self._set_auto_mode(family, min_variable_mode)
|
||||
if family.mode != min_variable_mode:
|
||||
msg = _(f'the family "{family.name}" is in "{family.mode}" mode but variables and '
|
||||
f'families inside have the higher modes "{min_variable_mode}"')
|
||||
raise DictConsistencyError(msg, 62, family.xmlfiles)
|
||||
|
||||
def _change_variabe_mode(self,
|
||||
def _change_variable_mode(self,
|
||||
variable,
|
||||
family_mode: str,
|
||||
is_follower=False,
|
||||
is_follower: bool,
|
||||
) -> None:
|
||||
# auto_save or auto_freeze variable is set to 'basic' mode
|
||||
# if its mode is not defined by the user
|
||||
if 'mode' not in vars(variable) and \
|
||||
(variable.auto_save is True or variable.auto_freeze is True):
|
||||
variable.mode = modes_level[0]
|
||||
# mandatory variable without value is a basic variable
|
||||
if variable.mandatory is True and not hasattr(variable, 'default'):
|
||||
variable.mode = modes_level[0]
|
||||
if hasattr(variable, 'mode'):
|
||||
variable_mode = variable.mode
|
||||
else:
|
||||
variable_mode = self.objectspace.rougailconfig['default_variable_mode']
|
||||
# none basic variable in high level family has to be in high level
|
||||
if modes[variable.mode] < modes[family_mode] and \
|
||||
(not is_follower or variable.mode != modes_level[0]):
|
||||
variable.mode = family_mode
|
||||
|
||||
def _change_variable_mode_leader(self,
|
||||
leadership,
|
||||
family_mode: str,
|
||||
) -> None:
|
||||
is_follower = False
|
||||
leader_mode = None
|
||||
for follower in leadership.variable:
|
||||
if follower.auto_save is True:
|
||||
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
|
||||
raise DictConsistencyError(msg, 29, leadership.xmlfiles)
|
||||
if follower.auto_freeze is True:
|
||||
msg = f'leader/followers "{follower.name}" could not be auto_freeze'
|
||||
raise DictConsistencyError(_(msg), 30, leadership.xmlfiles)
|
||||
self._change_variabe_mode(follower,
|
||||
family_mode,
|
||||
is_follower,
|
||||
)
|
||||
if leader_mode is None:
|
||||
leader_mode = leadership.variable[0].mode
|
||||
leadership.variable[0].mode = None
|
||||
else:
|
||||
# leader's mode is minimum level
|
||||
if modes[leader_mode] > modes[follower.mode]:
|
||||
follower.mode = leader_mode
|
||||
is_follower = True
|
||||
leadership.mode = leader_mode
|
||||
if not is_follower and self.modes[variable_mode] < self.modes[family_mode]:
|
||||
if self._has_mode(variable):
|
||||
msg = _(f'the variable "{variable.name}" is in "{variable_mode}" mode '
|
||||
f'but family has the higher family mode "{family_mode}"')
|
||||
raise DictConsistencyError(msg, 61, variable.xmlfiles)
|
||||
self._set_auto_mode(variable, family_mode)
|
||||
if not hasattr(variable, 'mode'):
|
||||
variable.mode = variable_mode
|
||||
|
||||
def dynamic_families(self):
|
||||
"""link dynamic families to object
|
||||
@ -203,7 +275,7 @@ class FamilyAnnotator(Walk):
|
||||
f'to multi variable')
|
||||
raise DictConsistencyError(msg, 16, family.xmlfiles)
|
||||
for variable in family.variable.values():
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
if isinstance(variable, self.objectspace.family) and not variable.leadership:
|
||||
msg = _(f'dynamic family "{family.name}" cannot contains another family')
|
||||
raise DictConsistencyError(msg, 22, family.xmlfiles)
|
||||
|
||||
|
@ -24,52 +24,70 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from ..utils import load_modules
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from rougail.utils import load_modules
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
from .target import TargetAnnotator
|
||||
from .param import ParamAnnotator
|
||||
from rougail.annotator.target import TargetAnnotator
|
||||
from rougail.annotator.param import ParamAnnotator
|
||||
|
||||
|
||||
class FillAnnotator(TargetAnnotator, ParamAnnotator):
|
||||
CALC_MULTI = ('calc_value', 'calc_list', 'get_range', 'calc_val_first_value', 'unbound_filename')
|
||||
|
||||
|
||||
class Annotator(TargetAnnotator, ParamAnnotator):
|
||||
"""Fill annotator
|
||||
"""
|
||||
level = 60
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
eosfunc_file,
|
||||
*args,
|
||||
):
|
||||
self.objectspace = objectspace
|
||||
if not hasattr(objectspace.space, 'constraints') or \
|
||||
not hasattr(self.objectspace.space.constraints, 'fill'):
|
||||
not hasattr(objectspace.space.constraints, 'fill'):
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.functions = dir(load_modules(eosfunc_file))
|
||||
self.functions.extend(self.objectspace.rougailconfig['internal_functions'])
|
||||
self.target_is_uniq = True
|
||||
self.only_variable = True
|
||||
self.allow_function = False
|
||||
self.convert_target(self.objectspace.space.constraints.fill)
|
||||
self.convert_param(self.objectspace.space.constraints.fill)
|
||||
self.fill_to_value()
|
||||
del self.objectspace.space.constraints.fill
|
||||
|
||||
def calc_is_multi(self, variable: 'self.objectspace.variable') -> bool:
|
||||
multi = variable.multi
|
||||
if multi is False:
|
||||
return multi
|
||||
if multi == 'submulti':
|
||||
return True
|
||||
return not self.objectspace.paths.is_follower(variable.path)
|
||||
|
||||
def fill_to_value(self) -> None:
|
||||
"""valid and manage <fill>
|
||||
"""
|
||||
for fill in self.objectspace.space.constraints.fill:
|
||||
# test if the function exists
|
||||
if fill.name not in self.functions:
|
||||
msg = _(f'cannot find fill function "{fill.name}"')
|
||||
raise DictConsistencyError(msg, 25, fill.xmlfiles)
|
||||
for target in fill.target:
|
||||
# test if the function exists
|
||||
if fill.name not in self.functions:
|
||||
msg = _(f'cannot find fill function "{fill.name}"')
|
||||
raise DictConsistencyError(msg, 25, fill.xmlfiles)
|
||||
|
||||
# create an object value
|
||||
value = self.objectspace.value(fill.xmlfiles)
|
||||
value.type = 'calculation'
|
||||
value.name = fill.name
|
||||
if fill.name not in CALC_MULTI:
|
||||
is_calc_multi = self.calc_is_multi(target.name)
|
||||
else:
|
||||
is_calc_multi = False
|
||||
value.calc_multi = is_calc_multi
|
||||
if target.name.namespace == 'services':
|
||||
target.name.default = value
|
||||
else:
|
||||
target.name.value = [value]
|
||||
|
||||
# manage params
|
||||
if hasattr(fill, 'param') and fill.param:
|
||||
value.param = fill.param
|
||||
|
@ -24,133 +24,54 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.utils import normalize_family
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
|
||||
class GroupAnnotator:
|
||||
class Annotator(Walk):
|
||||
"""Annotate group
|
||||
"""
|
||||
level = 10
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
):
|
||||
self.objectspace = objectspace
|
||||
if not hasattr(self.objectspace.space, 'constraints') or \
|
||||
not hasattr(self.objectspace.space.constraints, 'group'):
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.convert_groups()
|
||||
|
||||
def convert_groups(self): # pylint: disable=C0111
|
||||
"""convert groups
|
||||
"""
|
||||
# store old leaders family name
|
||||
cache_paths = {}
|
||||
for group in self.objectspace.space.constraints.group:
|
||||
if group.leader in cache_paths:
|
||||
leader_fam_path = cache_paths[group.leader]
|
||||
else:
|
||||
leader_fam_path = self.objectspace.paths.get_variable_family_path(group.leader)
|
||||
cache_paths[group.leader] = leader_fam_path
|
||||
follower_names = list(group.follower.keys())
|
||||
leader = self.objectspace.paths.get_variable(group.leader)
|
||||
ori_leader_family = self.objectspace.paths.get_family(leader_fam_path,
|
||||
leader.namespace,
|
||||
)
|
||||
has_a_leader = False
|
||||
for variable in list(ori_leader_family.variable.values()):
|
||||
if isinstance(variable, self.objectspace.leadership) and \
|
||||
variable.variable[0].name == leader.name:
|
||||
# append follower to an existed leadership
|
||||
leader_space = variable
|
||||
has_a_leader = True
|
||||
elif variable.name == leader.name:
|
||||
for family in self.get_families():
|
||||
if not isinstance(family, self.objectspace.family):
|
||||
continue
|
||||
if not family.leadership:
|
||||
continue
|
||||
if hasattr(family, 'dynamic'):
|
||||
msg = _(f'the family "{family.name}" cannot be leadership and dynamic together')
|
||||
raise DictConsistencyError(msg, 31, family.xmlfiles)
|
||||
for idx, variable in enumerate(family.variable.values()):
|
||||
if idx == 0:
|
||||
# it's a leader
|
||||
leader_space = self.manage_leader(variable,
|
||||
group,
|
||||
ori_leader_family,
|
||||
)
|
||||
has_a_leader = True
|
||||
elif has_a_leader:
|
||||
# it's should be a follower
|
||||
self.manage_follower(follower_names.pop(0),
|
||||
leader_fam_path,
|
||||
variable,
|
||||
leader_space,
|
||||
)
|
||||
# this variable is not more in ori_leader_family
|
||||
ori_leader_family.variable.pop(variable.name)
|
||||
if follower_names == []:
|
||||
# no more follower
|
||||
break
|
||||
else:
|
||||
joined = '", "'.join(follower_names)
|
||||
msg = _(f'when parsing leadership, we espect to find those followers "{joined}"')
|
||||
raise DictConsistencyError(msg, 31, variable.xmlfiles)
|
||||
del self.objectspace.space.constraints.group
|
||||
|
||||
def manage_leader(self,
|
||||
variable: 'Variable',
|
||||
group: 'Group',
|
||||
ori_leader_family,
|
||||
) -> 'Leadership':
|
||||
"""manage leader's variable
|
||||
"""
|
||||
if variable.multi is not True:
|
||||
msg = _(f'the variable "{variable.name}" in a group must be multi')
|
||||
raise DictConsistencyError(msg, 32, variable.xmlfiles)
|
||||
if hasattr(group, 'name'):
|
||||
leadership_name = group.name
|
||||
else:
|
||||
leadership_name = variable.name
|
||||
leader_space = self.objectspace.leadership(variable.xmlfiles)
|
||||
leader_space.variable = []
|
||||
leader_space.name = leadership_name
|
||||
leader_space.hidden = variable.hidden
|
||||
if variable.hidden:
|
||||
variable.frozen = True
|
||||
variable.force_default_on_freeze = True
|
||||
variable.hidden = None
|
||||
if hasattr(group, 'description'):
|
||||
leader_space.doc = group.description
|
||||
elif variable.name == leadership_name and hasattr(variable, 'description'):
|
||||
leader_space.doc = variable.description
|
||||
else:
|
||||
leader_space.doc = leadership_name
|
||||
leadership_path = ori_leader_family.path + '.' + leadership_name
|
||||
self.objectspace.paths.add_leadership(variable.namespace,
|
||||
leadership_path,
|
||||
leader_space,
|
||||
)
|
||||
leader_family = self.objectspace.paths.get_family(ori_leader_family.path,
|
||||
ori_leader_family.namespace,
|
||||
)
|
||||
leader_family.variable[variable.name] = leader_space
|
||||
leader_space.variable.append(variable)
|
||||
self.objectspace.paths.set_leader(variable.namespace,
|
||||
ori_leader_family.path,
|
||||
leadership_name,
|
||||
variable.name,
|
||||
)
|
||||
return leader_space
|
||||
|
||||
def manage_follower(self,
|
||||
follower_name: str,
|
||||
leader_family_name: str,
|
||||
variable: 'Variable',
|
||||
leader_space: 'Leadership',
|
||||
) -> None:
|
||||
"""manage follower
|
||||
"""
|
||||
if variable.name != follower_name:
|
||||
msg = _('when parsing leadership, we expect to find the follower '
|
||||
f'"{follower_name}" but we found "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 33, variable.xmlfiles)
|
||||
self.objectspace.paths.set_leader(variable.namespace,
|
||||
leader_family_name,
|
||||
leader_space.name,
|
||||
variable.name,
|
||||
)
|
||||
if leader_space.hidden:
|
||||
variable.frozen = True
|
||||
variable.force_default_on_freeze = True
|
||||
leader_space.variable.append(variable)
|
||||
if variable.multi is not True:
|
||||
msg = _(f'the variable "{variable.name}" in a leadership must be multi')
|
||||
raise DictConsistencyError(msg, 32, variable.xmlfiles)
|
||||
family.hidden = variable.hidden
|
||||
if variable.hidden:
|
||||
variable.frozen = True
|
||||
variable.force_default_on_freeze = True
|
||||
variable.hidden = None
|
||||
else:
|
||||
# it's a follower
|
||||
if family.hidden:
|
||||
variable.frozen = True
|
||||
variable.force_default_on_freeze = True
|
||||
if variable.multi is True:
|
||||
variable.multi = 'submulti'
|
||||
else:
|
||||
variable.multi = True
|
||||
|
@ -24,16 +24,26 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from .variable import CONVERT_OPTION
|
||||
try:
|
||||
import tiramisu3 as tiramisu
|
||||
except ModuleNotFoundError:
|
||||
import tiramisu
|
||||
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from rougail.annotator.variable import CONVERT_OPTION
|
||||
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
class ParamAnnotator:
|
||||
def valid_type_validation(self,
|
||||
obj,
|
||||
) -> None:
|
||||
"""Param annotator
|
||||
"""
|
||||
objectspace = None
|
||||
|
||||
@staticmethod
|
||||
def valid_type_validation(obj) -> None:
|
||||
"""Function to valid type (redefine in fill/condition/check classes)
|
||||
"""
|
||||
return None
|
||||
|
||||
def convert_param(self, objects) -> None:
|
||||
@ -45,54 +55,93 @@ class ParamAnnotator:
|
||||
param_to_delete = []
|
||||
variable_type = self.valid_type_validation(obj)
|
||||
for param_idx, param in enumerate(obj.param):
|
||||
if param.type == 'suffix':
|
||||
if hasattr(param, 'text'):
|
||||
if hasattr(param, 'text'):
|
||||
if param.type in ['suffix', 'index']:
|
||||
msg = _(f'"{param.type}" parameter must not have a value')
|
||||
raise DictConsistencyError(msg, 28, obj.xmlfiles)
|
||||
if param.type == 'nil':
|
||||
if param.text is not None:
|
||||
msg = _(f'"{param.type}" parameter must not have a value')
|
||||
raise DictConsistencyError(msg, 40, obj.xmlfiles)
|
||||
elif param.type == 'variable':
|
||||
try:
|
||||
path, suffix = self.objectspace.paths.get_variable_path(param.text,
|
||||
obj.namespace,
|
||||
param.xmlfiles,
|
||||
)
|
||||
param.text = self.objectspace.paths.get_variable(path)
|
||||
if variable_type and param.text.type != variable_type:
|
||||
msg = _(f'"{obj.name}" has type "{variable_type}" but param '
|
||||
f'has type "{param.text.type}"')
|
||||
raise DictConsistencyError(msg, 26, param.xmlfiles)
|
||||
if suffix:
|
||||
param.suffix = suffix
|
||||
family_path = self.objectspace.paths.get_variable_family_path(path)
|
||||
namespace = param.text.namespace
|
||||
param.family = self.objectspace.paths.get_family(family_path,
|
||||
namespace,
|
||||
)
|
||||
except DictConsistencyError as err:
|
||||
if err.errno != 42 or not param.optional:
|
||||
raise err
|
||||
param_to_delete.append(param_idx)
|
||||
elif param.type == 'function':
|
||||
if not self.allow_function:
|
||||
msg = _(f'cannot use "function" type')
|
||||
raise DictConsistencyError(msg, 74, param.xmlfiles)
|
||||
if not param.text in self.functions:
|
||||
msg = _(f'cannot find function "{param.text}"')
|
||||
raise DictConsistencyError(msg, 67, param.xmlfiles)
|
||||
if param_idx != 0:
|
||||
msg = _(f'function "{param.text}" must only set has first parameter')
|
||||
raise DictConsistencyError(msg, 75, param.xmlfiles)
|
||||
elif variable_type:
|
||||
self._convert_with_variable_type(variable_type, param)
|
||||
continue
|
||||
# no param.text
|
||||
if param.type == 'suffix':
|
||||
for target in obj.target:
|
||||
if not self.objectspace.paths.variable_is_dynamic(target.name.path):
|
||||
msg = _(f'"suffix" parameter cannot be set with target "{target.name}"'
|
||||
f' which is not a dynamic variable')
|
||||
msg = _(f'"{param.type}" parameter cannot be set with target '
|
||||
f'"{target.name}" which is not a dynamic variable')
|
||||
raise DictConsistencyError(msg, 53, obj.xmlfiles)
|
||||
elif not hasattr(param, 'text'):
|
||||
if not param.type == 'nil':
|
||||
msg = _(f'"{param.type}" parameter must have a value')
|
||||
raise DictConsistencyError(msg, 27, obj.xmlfiles)
|
||||
param.text = None
|
||||
elif param.type == 'index':
|
||||
for target in obj.target:
|
||||
if not self.objectspace.paths.is_follower(target.name.path):
|
||||
msg = _(f'"{param.type}" parameter cannot be set with target '
|
||||
f'"{target.name.name}" which is not a follower variable')
|
||||
raise DictConsistencyError(msg, 60, obj.xmlfiles)
|
||||
elif param.type == 'nil':
|
||||
msg = _(f'"{param.type}" parameter must not have a value')
|
||||
raise DictConsistencyError(msg, 40, obj.xmlfiles)
|
||||
elif param.type == 'variable':
|
||||
try:
|
||||
path, suffix = self.objectspace.paths.get_variable_path(param.text,
|
||||
obj.namespace,
|
||||
)
|
||||
param.text = self.objectspace.paths.get_variable(path)
|
||||
if variable_type and param.text.type != variable_type:
|
||||
msg = _(f'"{obj.name}" has type "{variable_type}" but param has type "{param.text.type}"')
|
||||
raise DictConsistencyError(msg, 26, param.xmlfiles)
|
||||
if suffix:
|
||||
param.suffix = suffix
|
||||
family_path = self.objectspace.paths.get_variable_family_path(path)
|
||||
param.family = self.objectspace.paths.get_family(family_path,
|
||||
param.text.namespace,
|
||||
)
|
||||
except DictConsistencyError as err:
|
||||
if err.errno != 42 or not param.optional:
|
||||
raise err
|
||||
param_to_delete.append(param_idx)
|
||||
elif variable_type:
|
||||
if 'type' in vars(param) and variable_type != param.type:
|
||||
msg = _(f'parameter has incompatible type "{param.type}" '
|
||||
f'with type "{variable_type}"')
|
||||
raise DictConsistencyError(msg, 7, param.xmlfiles)
|
||||
try:
|
||||
param.text = CONVERT_OPTION[variable_type].get('func', str)(param.text)
|
||||
except ValueError as err:
|
||||
msg = _(f'unable to change type of "{param.text}" '
|
||||
f'is not a valid "{variable_type}"')
|
||||
raise DictConsistencyError(msg, 13, param.xmlfiles) from err
|
||||
param.type = variable_type
|
||||
param.text = None
|
||||
elif param.type == 'string':
|
||||
param.text = ''
|
||||
if variable_type:
|
||||
self._convert_with_variable_type(variable_type, param)
|
||||
else:
|
||||
msg = _(f'"{param.type}" parameter must have a value')
|
||||
raise DictConsistencyError(msg, 27, obj.xmlfiles)
|
||||
param_to_delete.sort(reverse=True)
|
||||
for param_idx in param_to_delete:
|
||||
obj.param.pop(param_idx)
|
||||
|
||||
@staticmethod
|
||||
def _convert_with_variable_type(variable_type: str,
|
||||
param: 'self.objectspace.param',
|
||||
) -> None:
|
||||
if 'type' in vars(param) and variable_type != param.type:
|
||||
msg = _(f'parameter has incompatible type "{param.type}" '
|
||||
f'with type "{variable_type}"')
|
||||
raise DictConsistencyError(msg, 7, param.xmlfiles)
|
||||
try:
|
||||
option = CONVERT_OPTION[variable_type]
|
||||
param.text = option.get('func', str)(param.text)
|
||||
getattr(tiramisu, option['opttype'])('test',
|
||||
'Object to valid value',
|
||||
param.text,
|
||||
**option.get('initkwargs', {}),
|
||||
)
|
||||
except ValueError as err:
|
||||
msg = _(f'unable to change type of value "{param.text}" '
|
||||
f'is not a valid "{variable_type}"')
|
||||
raise DictConsistencyError(msg, 13, param.xmlfiles) from err
|
||||
param.type = variable_type
|
||||
|
@ -24,22 +24,23 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from .variable import Walk
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
|
||||
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
|
||||
PROPERTIES = ('hidden', 'frozen', 'force_default_on_freeze',
|
||||
'force_store_value', 'disabled', 'mandatory')
|
||||
CONVERT_PROPERTIES = {'auto_save': ['force_store_value'],
|
||||
'auto_freeze': ['force_store_value', 'auto_freeze'],
|
||||
}
|
||||
|
||||
|
||||
class PropertyAnnotator(Walk):
|
||||
class Annotator(Walk):
|
||||
"""Annotate properties
|
||||
"""
|
||||
def __init__(self, objectspace):
|
||||
level = 90
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args
|
||||
) -> None:
|
||||
self.objectspace = objectspace
|
||||
if hasattr(self.objectspace.space, 'services'):
|
||||
self.convert_services()
|
||||
@ -53,8 +54,10 @@ class PropertyAnnotator(Walk):
|
||||
"""convert properties
|
||||
"""
|
||||
# hidden variable is also frozen
|
||||
if isinstance(variable, self.objectspace.variable) and variable.hidden is True:
|
||||
variable.frozen = True
|
||||
if isinstance(variable, self.objectspace.variable) and variable.hidden is True and \
|
||||
variable.name != self.objectspace.rougailconfig['auto_freeze_variable']:
|
||||
if not variable.auto_freeze:
|
||||
variable.frozen = True
|
||||
if not variable.auto_save and \
|
||||
not variable.auto_freeze and \
|
||||
'force_default_on_freeze' not in vars(variable):
|
||||
@ -64,8 +67,8 @@ class PropertyAnnotator(Walk):
|
||||
for prop in PROPERTIES:
|
||||
if hasattr(variable, prop):
|
||||
if getattr(variable, prop) is True:
|
||||
for subprop in CONVERT_PROPERTIES.get(prop, [prop]):
|
||||
variable.properties.append(subprop)
|
||||
# for subprop in CONVERT_PROPERTIES.get(prop, [prop]):
|
||||
variable.properties.append(prop)
|
||||
setattr(variable, prop, None)
|
||||
if hasattr(variable, 'mode') and variable.mode:
|
||||
variable.properties.append(variable.mode)
|
||||
@ -73,7 +76,7 @@ class PropertyAnnotator(Walk):
|
||||
if 'force_store_value' in variable.properties and \
|
||||
'force_default_on_freeze' in variable.properties: # pragma: no cover
|
||||
# should not appened
|
||||
msg = _('cannot have auto_freeze or auto_store with the hidden '
|
||||
msg = _('cannot have auto_freeze or auto_save with the hidden '
|
||||
f'variable "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 50, variable.xmlfiles)
|
||||
if not variable.properties:
|
||||
@ -95,14 +98,13 @@ class PropertyAnnotator(Walk):
|
||||
self.convert_property(variable)
|
||||
|
||||
def convert_family(self) -> None:
|
||||
"""convert variables
|
||||
"""convert families
|
||||
"""
|
||||
for family in self.get_families():
|
||||
self.convert_property(family)
|
||||
|
||||
def convert_variable(self) -> None:
|
||||
for variable in self.get_variables(with_leadership=True):
|
||||
if isinstance(variable, self.objectspace.leadership):
|
||||
for follower in variable.variable:
|
||||
self.convert_property(follower)
|
||||
"""convert variables
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
self.convert_property(variable)
|
||||
|
@ -27,40 +27,37 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
from os.path import basename
|
||||
from typing import Tuple
|
||||
|
||||
from ..i18n import _
|
||||
from ..utils import normalize_family
|
||||
from ..error import DictConsistencyError
|
||||
# a CreoleObjSpace's attribute has some annotations
|
||||
from rougail.i18n import _
|
||||
from rougail.utils import normalize_family
|
||||
from rougail.error import DictConsistencyError
|
||||
# a object's attribute has some annotations
|
||||
# that shall not be present in the exported (flatened) XML
|
||||
ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace',
|
||||
ERASED_ATTRIBUTES = ('redefine', 'exists', 'optional', 'remove_check', 'namespace',
|
||||
'remove_condition', 'path', 'instance_mode', 'index',
|
||||
'level', 'remove_fill', 'xmlfiles', 'type', 'reflector_name',
|
||||
'reflector_object',)
|
||||
ALLOW_ATTRIBUT_NOT_MANAGE = ['file']
|
||||
|
||||
|
||||
KEY_TYPE = {'variable': 'symlink',
|
||||
'PortOption': 'port',
|
||||
'UnicodeOption': 'string',
|
||||
'NetworkOption': 'network',
|
||||
'NetmaskOption': 'netmask',
|
||||
'URLOption': 'web_address',
|
||||
'FilenameOption': 'filename',
|
||||
}
|
||||
|
||||
|
||||
class ServiceAnnotator:
|
||||
class Annotator:
|
||||
"""Manage service's object
|
||||
for example::
|
||||
<services>
|
||||
<service name="test">
|
||||
<service_access service='ntp'>
|
||||
<port protocol='udp' service_accesslist='ntp_udp'>123</port>
|
||||
</service_access>
|
||||
</service>
|
||||
</services>
|
||||
"""
|
||||
def __init__(self, objectspace):
|
||||
level = 20
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
) -> None:
|
||||
self.objectspace = objectspace
|
||||
self.uniq_overrides = []
|
||||
if 'network_type' not in self.objectspace.types:
|
||||
self.objectspace.types['network_type'] = self.objectspace.types['ip_type']
|
||||
if hasattr(self.objectspace.space, 'services'):
|
||||
if not hasattr(self.objectspace.space.services, 'service'):
|
||||
del self.objectspace.space.services
|
||||
@ -75,15 +72,35 @@ class ServiceAnnotator:
|
||||
self.objectspace.space.services.doc = 'services'
|
||||
self.objectspace.space.services.path = 'services'
|
||||
for service_name, service in self.objectspace.space.services.service.items():
|
||||
service.information = self.objectspace.information(service.xmlfiles)
|
||||
activate_obj = self._generate_element('boolean',
|
||||
None,
|
||||
None,
|
||||
'activate',
|
||||
True,
|
||||
service,
|
||||
'.'.join(['services', normalize_family(service_name), 'activate']),
|
||||
)
|
||||
for elttype, values in dict(vars(service)).items():
|
||||
if elttype == 'servicelist':
|
||||
self.objectspace.list_conditions.setdefault('servicelist',
|
||||
{}).setdefault(
|
||||
values,
|
||||
[]).append(activate_obj)
|
||||
continue
|
||||
if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES:
|
||||
continue
|
||||
eltname = elttype + 's'
|
||||
if not service.manage and elttype not in ALLOW_ATTRIBUT_NOT_MANAGE:
|
||||
msg = _(f'unmanage service cannot have "{elttype}"')
|
||||
raise DictConsistencyError(msg, 66, service.xmlfiles)
|
||||
if elttype != 'ip':
|
||||
eltname = elttype + 's'
|
||||
else:
|
||||
eltname = elttype
|
||||
path = '.'.join(['services', normalize_family(service_name), eltname])
|
||||
family = self._gen_family(eltname,
|
||||
path,
|
||||
service.xmlfiles,
|
||||
with_informations=False,
|
||||
)
|
||||
if isinstance(values, dict):
|
||||
values = list(values.values())
|
||||
@ -93,6 +110,15 @@ class ServiceAnnotator:
|
||||
path,
|
||||
)
|
||||
setattr(service, elttype, family)
|
||||
manage = self._generate_element('boolean',
|
||||
None,
|
||||
None,
|
||||
'manage',
|
||||
service.manage,
|
||||
service,
|
||||
'.'.join(['services', normalize_family(service_name), 'manage']),
|
||||
)
|
||||
service.variable = [activate_obj, manage]
|
||||
service.doc = service.name
|
||||
|
||||
def make_group_from_elts(self,
|
||||
@ -122,9 +148,11 @@ class ServiceAnnotator:
|
||||
)
|
||||
family.variable = []
|
||||
activate_obj = self._generate_element('boolean',
|
||||
None,
|
||||
None,
|
||||
'activate',
|
||||
True,
|
||||
elt.xmlfiles,
|
||||
elt,
|
||||
'.'.join([subpath, 'activate']),
|
||||
)
|
||||
for key in dir(elt):
|
||||
@ -137,15 +165,25 @@ class ServiceAnnotator:
|
||||
value,
|
||||
[]).append(activate_obj)
|
||||
continue
|
||||
family.variable.append(self._generate_element(self._get_type(key,
|
||||
elttype,
|
||||
elt,
|
||||
),
|
||||
key,
|
||||
value,
|
||||
elt.xmlfiles,
|
||||
f'{subpath}.{key}'
|
||||
))
|
||||
if key == 'name':
|
||||
dtd_key_type = elttype + '_type'
|
||||
else:
|
||||
dtd_key_type = key + '_type'
|
||||
elt_type = getattr(elt, dtd_key_type, None)
|
||||
if elt_type:
|
||||
if elt_type == 'variable':
|
||||
elt_type = 'symlink'
|
||||
family.variable.append(self._generate_element(elt_type,
|
||||
dtd_key_type,
|
||||
elttype,
|
||||
key,
|
||||
value,
|
||||
elt,
|
||||
f'{subpath}.{key}'
|
||||
))
|
||||
else:
|
||||
setattr(family.information, key, value)
|
||||
|
||||
family.variable.append(activate_obj)
|
||||
families.append(family)
|
||||
return families
|
||||
@ -155,7 +193,7 @@ class ServiceAnnotator:
|
||||
path: str,
|
||||
) -> Tuple[str, str]:
|
||||
# create element name, if already exists, add _xx to be uniq
|
||||
if hasattr(elt, 'source'):
|
||||
if hasattr(elt, 'source') and elt.source:
|
||||
name = elt.source
|
||||
else:
|
||||
name = elt.name
|
||||
@ -175,7 +213,8 @@ class ServiceAnnotator:
|
||||
def _gen_family(self,
|
||||
name,
|
||||
path,
|
||||
xmlfiles
|
||||
xmlfiles,
|
||||
with_informations=True,
|
||||
):
|
||||
family = self.objectspace.family(xmlfiles)
|
||||
family.name = normalize_family(name)
|
||||
@ -186,22 +225,32 @@ class ServiceAnnotator:
|
||||
family,
|
||||
None,
|
||||
)
|
||||
if with_informations:
|
||||
family.information = self.objectspace.information(xmlfiles)
|
||||
return family
|
||||
|
||||
def _generate_element(self,
|
||||
type_,
|
||||
dtd_key_type,
|
||||
elttype,
|
||||
key,
|
||||
value,
|
||||
xmlfiles,
|
||||
elt,
|
||||
path,
|
||||
): # pylint: disable=R0913
|
||||
variable = self.objectspace.variable(xmlfiles)
|
||||
variable = self.objectspace.variable(elt.xmlfiles)
|
||||
variable.name = normalize_family(key)
|
||||
variable.mode = None
|
||||
variable.type = type_
|
||||
if type_ == 'symlink':
|
||||
variable.opt = self.objectspace.paths.get_variable(value)
|
||||
variable.multi = None
|
||||
needed_type = self.objectspace.types[dtd_key_type]
|
||||
if needed_type not in ('variable', variable.opt.type):
|
||||
msg = _(f'"{value}" in "{elttype}" must be a variable with type '
|
||||
f'"{needed_type}" not "{variable.opt.type}"')
|
||||
raise DictConsistencyError(msg, 58, elt.xmlfiles)
|
||||
|
||||
else:
|
||||
variable.doc = key
|
||||
variable.default = value
|
||||
@ -214,43 +263,48 @@ class ServiceAnnotator:
|
||||
)
|
||||
return variable
|
||||
|
||||
def _get_type(self,
|
||||
key: str,
|
||||
elt_name: str,
|
||||
elt,
|
||||
) -> str:
|
||||
if key == 'name':
|
||||
dtd_key_type = elt_name + '_type'
|
||||
else:
|
||||
dtd_key_type = key + '_type'
|
||||
if key in self.objectspace.booleans_attributs:
|
||||
return 'boolean'
|
||||
if hasattr(elt, dtd_key_type):
|
||||
return KEY_TYPE[getattr(elt, dtd_key_type)]
|
||||
return 'string'
|
||||
|
||||
def _update_override(self,
|
||||
file_,
|
||||
override,
|
||||
service_name,
|
||||
):
|
||||
file_.name = f'/systemd/system/{service_name}.service.d/rougail.conf'
|
||||
# retrieve default value from File object
|
||||
for attr in ['owner', 'group', 'mode']:
|
||||
setattr(file_, attr, getattr(self.objectspace.file, attr))
|
||||
if not hasattr(file_, 'source'):
|
||||
file_.source = f'{service_name}.service'
|
||||
self._update_file(file_,
|
||||
service_name,
|
||||
)
|
||||
|
||||
def _update_file(self,
|
||||
file_,
|
||||
if service_name in self.uniq_overrides:
|
||||
msg = _('only one override is allowed by service, '
|
||||
'please use a variable multiple if you want have more than one IP')
|
||||
raise DictConsistencyError(msg, 69, override.xmlfiles)
|
||||
self.uniq_overrides.append(service_name)
|
||||
override.name = service_name
|
||||
if not hasattr(override, 'source'):
|
||||
override.source = f'{service_name}.service'
|
||||
|
||||
@staticmethod
|
||||
def _update_file(file_,
|
||||
service_name,
|
||||
):
|
||||
if not hasattr(file_, 'file_type') or file_.file_type == "UnicodeOption":
|
||||
if not hasattr(file_, 'file_type') or file_.file_type == "filename":
|
||||
if not hasattr(file_, 'source'):
|
||||
file_.source = basename(file_.name)
|
||||
elif not hasattr(file_, 'source'):
|
||||
msg = _(f'attribute "source" is mandatory for the file "{file_.name}" '
|
||||
f'"({service_name})"')
|
||||
raise DictConsistencyError(msg, 34, file_.xmlfiles)
|
||||
|
||||
def _update_ip(self,
|
||||
ip,
|
||||
service_name,
|
||||
) -> None:
|
||||
variable = self.objectspace.paths.get_variable(ip.name, ip.xmlfiles)
|
||||
if variable.type not in ['ip', 'network', 'network_cidr']:
|
||||
msg = _(f'ip cannot be linked to "{variable.type}" variable "{ip.name}"')
|
||||
raise DictConsistencyError(msg, 70, ip.xmlfiles)
|
||||
if variable.type in ['ip', 'network_cidr'] and hasattr(ip, 'netmask'):
|
||||
msg = _(f'ip with ip_type "{variable.type}" must not have netmask')
|
||||
raise DictConsistencyError(msg, 59, ip.xmlfiles)
|
||||
if variable.type == 'network' and not hasattr(ip, 'netmask'):
|
||||
msg = _(f'ip with ip_type "{variable.type}" must have netmask')
|
||||
raise DictConsistencyError(msg, 64, ip.xmlfiles)
|
||||
if hasattr(ip, 'netmask'):
|
||||
netmask = self.objectspace.paths.get_variable(ip.netmask, ip.xmlfiles)
|
||||
if netmask.type != 'netmask':
|
||||
msg = _(f'netmask in ip must have type "netmask", not "{netmask.type}"')
|
||||
raise DictConsistencyError(msg, 65, ip.xmlfiles)
|
||||
|
@ -24,11 +24,13 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from ..i18n import _
|
||||
from ..error import DictConsistencyError
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
|
||||
class TargetAnnotator:
|
||||
"""Target annotator
|
||||
"""
|
||||
def convert_target(self,
|
||||
objects,
|
||||
) -> None:
|
||||
@ -62,19 +64,26 @@ class TargetAnnotator:
|
||||
f'is not allowed')
|
||||
raise DictConsistencyError(msg, 8, obj.xmlfiles)
|
||||
if target.type == 'family':
|
||||
if obj.name not in ['disabled_if_in',
|
||||
'disabled_if_not_in',
|
||||
'hidden_if_in',
|
||||
'hidden_if_not_in',
|
||||
]:
|
||||
msg = _(f'target "{target.type}" not allow with "{obj.name}"')
|
||||
raise DictConsistencyError(msg, 51, target.xmlfiles)
|
||||
target.name = self.objectspace.paths.get_family(target.name,
|
||||
obj.namespace,
|
||||
)
|
||||
elif target.type.endswith('list') and \
|
||||
obj.name not in ['disabled_if_in', 'disabled_if_not_in']:
|
||||
msg = _(f'target "{target.type}" not allow')
|
||||
msg = _(f'target "{target.type}" not allow with "{obj.name}"')
|
||||
raise DictConsistencyError(msg, 10, target.xmlfiles)
|
||||
except DictConsistencyError as err:
|
||||
if err.errno != 42:
|
||||
raise err
|
||||
# for optional variable
|
||||
if not target.optional:
|
||||
msg = f'cannot found target "{target.name}"'
|
||||
msg = f'cannot found target "{target.type}" "{target.name}"'
|
||||
raise DictConsistencyError(_(msg), 12, target.xmlfiles) from err
|
||||
remove_targets.append(index)
|
||||
remove_targets.sort(reverse=True)
|
||||
|
@ -24,13 +24,18 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from .variable import Walk
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
class ValueAnnotator(Walk): # pylint: disable=R0903
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
|
||||
class Annotator(Walk): # pylint: disable=R0903
|
||||
"""Annotate value
|
||||
"""
|
||||
level = 70
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
) -> None:
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
return
|
||||
@ -40,20 +45,11 @@ class ValueAnnotator(Walk): # pylint: disable=R0903
|
||||
def convert_value(self) -> None:
|
||||
"""convert value
|
||||
"""
|
||||
for variable in self.get_variables(with_leadership=True):
|
||||
if isinstance(variable, self.objectspace.leadership):
|
||||
variable_type = 'leader'
|
||||
for follower in variable.variable:
|
||||
self._convert_value(follower,
|
||||
variable_type,
|
||||
)
|
||||
variable_type = 'follower'
|
||||
else:
|
||||
self._convert_value(variable)
|
||||
for variable in self.get_variables():
|
||||
self._convert_value(variable)
|
||||
|
||||
def _convert_value(self,
|
||||
variable,
|
||||
variable_type: str=None,
|
||||
) -> None:
|
||||
# a boolean must have value, the default value is "True"
|
||||
if not hasattr(variable, 'value') and variable.type == 'boolean':
|
||||
@ -61,9 +57,8 @@ class ValueAnnotator(Walk): # pylint: disable=R0903
|
||||
new_value.name = True
|
||||
new_value.type = 'boolean'
|
||||
variable.value = [new_value]
|
||||
"""if the variable is mandatory and doesn't have any value
|
||||
then the variable's mode is set to 'basic'
|
||||
"""
|
||||
# if the variable is mandatory and doesn't have any value
|
||||
# then the variable's mode is set to 'basic'
|
||||
# variable with default value is mandatory
|
||||
if hasattr(variable, 'value') and variable.value:
|
||||
has_value = True
|
||||
@ -71,22 +66,25 @@ class ValueAnnotator(Walk): # pylint: disable=R0903
|
||||
if value.type == 'calculation':
|
||||
has_value = False
|
||||
break
|
||||
if has_value:
|
||||
if has_value and 'mandatory' not in vars(variable):
|
||||
# if has value without any calculation
|
||||
variable.mandatory = True
|
||||
if not hasattr(variable, 'value'):
|
||||
return
|
||||
if variable.value[0].type == 'calculation':
|
||||
variable.default = variable.value[0]
|
||||
elif variable.multi:
|
||||
if not self.objectspace.paths.is_follower(variable.path):
|
||||
variable.default = [value.name for value in variable.value]
|
||||
if not self.objectspace.paths.is_leader(variable.path):
|
||||
if variable.multi == 'submulti':
|
||||
variable.default_multi = [value.name for value in variable.value]
|
||||
else:
|
||||
variable.default_multi = variable.value[0].name
|
||||
else:
|
||||
if variable.multi:
|
||||
if variable_type != 'follower':
|
||||
variable.default = [value.name for value in variable.value]
|
||||
if variable_type != 'leader':
|
||||
if variable.multi == 'submulti':
|
||||
variable.default_multi = [value.name for value in variable.value]
|
||||
else:
|
||||
variable.default_multi = variable.value[0].name
|
||||
else:
|
||||
variable.default = variable.value[0].name
|
||||
if len(variable.value) > 1:
|
||||
msg = _(f'the non multi variable "{variable.name}" cannot have '
|
||||
'more than one value')
|
||||
raise DictConsistencyError(msg, 68, variable.xmlfiles)
|
||||
variable.default = variable.value[0].name
|
||||
del variable.value
|
||||
|
@ -25,7 +25,9 @@ along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from ..objspace import convert_boolean
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.objspace import convert_boolean
|
||||
|
||||
|
||||
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
|
||||
@ -47,7 +49,7 @@ CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
|
||||
'broadcast': dict(opttype="BroadcastOption"),
|
||||
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios',
|
||||
'warnings_only': True}),
|
||||
'domain': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname',
|
||||
'domainname': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname',
|
||||
'allow_ip': False}),
|
||||
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname',
|
||||
'allow_ip': False}),
|
||||
@ -68,27 +70,23 @@ FORCE_CHOICE = {'schedule': ['none', 'daily', 'weekly', 'monthly'],
|
||||
class Walk:
|
||||
"""Walk to objectspace to find variable or family
|
||||
"""
|
||||
def get_variables(self,
|
||||
with_leadership: bool=False,
|
||||
):
|
||||
objectspace = None
|
||||
|
||||
def get_variables(self):
|
||||
"""Iter all variables from the objectspace
|
||||
"""
|
||||
for family in self.objectspace.space.variables.values():
|
||||
yield from self._get_variables(family, with_leadership)
|
||||
yield from self._get_variables(family)
|
||||
|
||||
def _get_variables(self,
|
||||
family: 'self.objectspace.family',
|
||||
with_leadership: bool
|
||||
):
|
||||
if hasattr(family, 'variable'):
|
||||
for variable in family.variable.values():
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
yield from self._get_variables(variable, with_leadership)
|
||||
continue
|
||||
if not with_leadership and isinstance(variable, self.objectspace.leadership):
|
||||
for follower in variable.variable:
|
||||
yield follower
|
||||
continue
|
||||
if not hasattr(family, 'variable'):
|
||||
return
|
||||
for variable in family.variable.values():
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
yield from self._get_variables(variable)
|
||||
else:
|
||||
yield variable
|
||||
|
||||
def get_families(self):
|
||||
@ -101,21 +99,27 @@ class Walk:
|
||||
family: 'self.objectspace.family',
|
||||
):
|
||||
yield family
|
||||
if hasattr(family, 'variable'):
|
||||
for fam in family.variable.values():
|
||||
if isinstance(fam, self.objectspace.family):
|
||||
yield from self._get_families(fam)
|
||||
if not hasattr(family, 'variable'):
|
||||
return
|
||||
for fam in family.variable.values():
|
||||
if isinstance(fam, self.objectspace.family):
|
||||
yield from self._get_families(fam)
|
||||
|
||||
|
||||
class VariableAnnotator(Walk): # pylint: disable=R0903
|
||||
class Annotator(Walk): # pylint: disable=R0903
|
||||
"""Annotate variable
|
||||
"""
|
||||
level = 30
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
):
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.forbidden_name = ['services', self.objectspace.rougailconfig['variable_namespace']]
|
||||
for extra in self.objectspace.rougailconfig['extra_dictionaries']:
|
||||
self.forbidden_name.append(extra)
|
||||
self.convert_variable()
|
||||
self.convert_test()
|
||||
self.convert_help()
|
||||
@ -123,24 +127,17 @@ class VariableAnnotator(Walk): # pylint: disable=R0903
|
||||
def convert_variable(self):
|
||||
"""convert variable
|
||||
"""
|
||||
for variable in self.get_variables(with_leadership=True):
|
||||
if isinstance(variable, self.objectspace.leadership):
|
||||
# first variable is a leader, others are follower
|
||||
variable_type = 'leader'
|
||||
for follower in variable.variable:
|
||||
self._convert_variable(follower,
|
||||
variable_type,
|
||||
)
|
||||
variable_type = 'follower'
|
||||
else:
|
||||
self._convert_variable(variable,
|
||||
'variable',
|
||||
)
|
||||
for variable in self.get_variables():
|
||||
self._convert_variable(variable)
|
||||
|
||||
def _convert_variable(self,
|
||||
variable,
|
||||
variable_type: str,
|
||||
) -> None:
|
||||
if variable.namespace == self.objectspace.rougailconfig['variable_namespace'] and \
|
||||
variable.name in self.forbidden_name:
|
||||
msg = _(f'the name of the variable "{variable.name}" cannot be the same as the name'
|
||||
' of a namespace')
|
||||
raise DictConsistencyError(msg, 54, variable.xmlfiles)
|
||||
if variable.type != 'symlink' and not hasattr(variable, 'description'):
|
||||
variable.description = variable.name
|
||||
if hasattr(variable, 'value'):
|
||||
@ -159,11 +156,6 @@ class VariableAnnotator(Walk): # pylint: disable=R0903
|
||||
del variable.value
|
||||
variable.doc = variable.description
|
||||
del variable.description
|
||||
if variable_type == 'follower':
|
||||
if variable.multi is True:
|
||||
variable.multi = 'submulti'
|
||||
else:
|
||||
variable.multi = True
|
||||
self._convert_valid_enum(variable)
|
||||
|
||||
def _convert_valid_enum(self,
|
||||
@ -197,34 +189,27 @@ class VariableAnnotator(Walk): # pylint: disable=R0903
|
||||
"""Convert variable tests value
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
self._convert_test(variable)
|
||||
|
||||
def _convert_test(self,
|
||||
variable,
|
||||
) -> None:
|
||||
if not hasattr(variable, 'information'):
|
||||
variable.information = self.objectspace.information(variable.xmlfiles)
|
||||
if hasattr(variable, 'test'):
|
||||
if variable.test:
|
||||
values = variable.test.split('|')
|
||||
new_values = []
|
||||
for value in values:
|
||||
if value == '':
|
||||
value = None
|
||||
else:
|
||||
value = CONVERT_OPTION.get(variable.type, {}).get('func', str)(value)
|
||||
new_values.append(value)
|
||||
variable.information.test = tuple(new_values)
|
||||
del variable.test
|
||||
if not hasattr(variable, 'test') or not variable.test:
|
||||
# with we want remove test, we set "" has test value
|
||||
continue
|
||||
new_values = []
|
||||
for value in variable.test.split('|'):
|
||||
if value == '':
|
||||
value = None
|
||||
else:
|
||||
value = CONVERT_OPTION.get(variable.type, {}).get('func', str)(value)
|
||||
new_values.append(value)
|
||||
if not hasattr(variable, 'information'):
|
||||
variable.information = self.objectspace.information(variable.xmlfiles)
|
||||
variable.information.test = tuple(new_values)
|
||||
|
||||
def convert_help(self):
|
||||
"""Convert variable help
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
self._convert_help(variable)
|
||||
|
||||
@staticmethod
|
||||
def _convert_help(variable) -> None:
|
||||
if hasattr(variable, 'help'):
|
||||
if not hasattr(variable, 'help'):
|
||||
continue
|
||||
if not hasattr(variable, 'information'):
|
||||
variable.information = self.objectspace.information(variable.xmlfiles)
|
||||
variable.information.help = variable.help
|
||||
del variable.help
|
||||
|
@ -41,5 +41,16 @@ RougailConfig = {'dictionaries_dir': [join(ROUGAILROOT, 'dictionaries')],
|
||||
'dtdfilename': join(DTDDIR, 'rougail.dtd'),
|
||||
'functions_file': join(ROUGAILROOT, 'functions.py'),
|
||||
'variable_namespace': 'rougail',
|
||||
'auto_freeze_variable': 'instanciated_module',
|
||||
'auto_freeze_variable': 'server_deployed',
|
||||
'internal_functions': [],
|
||||
'extra_annotators': [],
|
||||
'modes_level': ['basic', 'normal', 'expert'],
|
||||
'default_family_mode': 'basic',
|
||||
'default_variable_mode': 'normal',
|
||||
'default_files_engine': 'creole',
|
||||
'default_files_mode': '0644',
|
||||
'default_files_owner': 'root',
|
||||
'default_files_group': 'root',
|
||||
'default_files_included': 'no',
|
||||
'default_overrides_engine': 'creole',
|
||||
}
|
||||
|
@ -27,12 +27,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Sample usage::
|
||||
|
||||
>>> from rougail import Rougail
|
||||
>>> rougail = Rougail()
|
||||
>>> rougail.load_dictionaries(['/usr/share/rougail/dicos'])
|
||||
>>> rougail.load_extra_dictionaries('extra1', ['/usr/share/rougail/extra1'])
|
||||
>>> rougail.space_visitor('/usr/share/rougail/funcs.py')
|
||||
>>> tiramisu = rougail.save()
|
||||
>>> from rougail import RougailConvert
|
||||
>>> rougail = RougailConvert()
|
||||
>>> tiramisu = rougail.save('tiramisu.py')
|
||||
|
||||
The Rougail
|
||||
|
||||
@ -58,16 +55,22 @@ from .error import DictConsistencyError
|
||||
class RougailConvert:
|
||||
"""Rougail object
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
xmlreflector = XMLReflector()
|
||||
rougailobjspace = RougailObjSpace(xmlreflector)
|
||||
def __init__(self,
|
||||
rougailconfig: RougailConfig=None,
|
||||
) -> None:
|
||||
if rougailconfig is None:
|
||||
rougailconfig = RougailConfig
|
||||
xmlreflector = XMLReflector(rougailconfig)
|
||||
rougailobjspace = RougailObjSpace(xmlreflector,
|
||||
rougailconfig,
|
||||
)
|
||||
self._load_dictionaries(xmlreflector,
|
||||
rougailobjspace,
|
||||
RougailConfig['variable_namespace'],
|
||||
RougailConfig['dictionaries_dir'],
|
||||
rougailconfig['variable_namespace'],
|
||||
rougailconfig['dictionaries_dir'],
|
||||
)
|
||||
for namespace, extra_dir in RougailConfig['extra_dictionaries'].items():
|
||||
if namespace in ['services', RougailConfig['variable_namespace']]:
|
||||
for namespace, extra_dir in rougailconfig['extra_dictionaries'].items():
|
||||
if namespace in ['services', rougailconfig['variable_namespace']]:
|
||||
msg = _(f'Namespace name "{namespace}" is not allowed')
|
||||
raise DictConsistencyError(msg, 21, None)
|
||||
self._load_dictionaries(xmlreflector,
|
||||
@ -75,7 +78,7 @@ class RougailConvert:
|
||||
namespace,
|
||||
extra_dir,
|
||||
)
|
||||
functions_file = RougailConfig['functions_file']
|
||||
functions_file = rougailconfig['functions_file']
|
||||
SpaceAnnotator(rougailobjspace,
|
||||
functions_file,
|
||||
)
|
||||
@ -83,8 +86,8 @@ class RougailConvert:
|
||||
functions_file,
|
||||
).get_text() + '\n'
|
||||
|
||||
def _load_dictionaries(self,
|
||||
xmlreflector: XMLReflector,
|
||||
@staticmethod
|
||||
def _load_dictionaries(xmlreflector: XMLReflector,
|
||||
rougailobjspace: RougailObjSpace,
|
||||
namespace: str,
|
||||
xmlfolders: List[str],
|
||||
|
@ -37,7 +37,8 @@
|
||||
<!-- root element -->
|
||||
<!-- =============== -->
|
||||
|
||||
<!ELEMENT rougail (services|variables|constraints|help)*>
|
||||
<!ELEMENT rougail (services|variables|constraints)*>
|
||||
<!ATTLIST rougail version (0.10) #REQUIRED>
|
||||
|
||||
<!-- ============== -->
|
||||
<!-- files element -->
|
||||
@ -45,38 +46,33 @@
|
||||
|
||||
<!ELEMENT services (service*)>
|
||||
|
||||
<!ELEMENT service ((port*|ip*|file*|override*)*)>
|
||||
<!ELEMENT service ((ip*|file*|override*)*)>
|
||||
<!ATTLIST service name CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT port (#PCDATA)>
|
||||
<!ATTLIST port port_type (PortOption|variable) "PortOption">
|
||||
<!ATTLIST port portlist CDATA #IMPLIED>
|
||||
<!ATTLIST port protocol (tcp|udp) "tcp">
|
||||
<!ATTLIST service manage (True|False) "True">
|
||||
<!ATTLIST service servicelist CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT ip (#PCDATA)>
|
||||
<!ATTLIST ip iplist CDATA #IMPLIED>
|
||||
<!ATTLIST ip ip_type (NetworkOption|variable) "NetworkOption">
|
||||
<!ATTLIST ip interface_type (UnicodeOption|variable) "UnicodeOption">
|
||||
<!ATTLIST ip interface CDATA #REQUIRED>
|
||||
<!ATTLIST ip netmask_type (NetmaskOption|variable) "NetmaskOption">
|
||||
<!ATTLIST ip netmask CDATA "255.255.255.255">
|
||||
<!ATTLIST ip ip_type (variable) "variable">
|
||||
<!ATTLIST ip netmask_type (variable) "variable">
|
||||
<!ATTLIST ip netmask CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT file EMPTY>
|
||||
<!ATTLIST file name CDATA #REQUIRED>
|
||||
<!ATTLIST file file_type (UnicodeOption|variable) "UnicodeOption">
|
||||
<!ELEMENT file (#PCDATA)>
|
||||
<!ATTLIST file file_type (filename|variable) "filename">
|
||||
<!ATTLIST file variable CDATA #IMPLIED>
|
||||
<!ATTLIST file variable_type (variable) "variable">
|
||||
<!ATTLIST file source CDATA #IMPLIED>
|
||||
<!ATTLIST file mode CDATA "0644">
|
||||
<!ATTLIST file owner CDATA "root">
|
||||
<!ATTLIST file group CDATA "root">
|
||||
<!ATTLIST file mode CDATA #IMPLIED>
|
||||
<!ATTLIST file owner CDATA #IMPLIED>
|
||||
<!ATTLIST file group CDATA #IMPLIED>
|
||||
<!ATTLIST file filelist CDATA #IMPLIED>
|
||||
<!ATTLIST file redefine (True|False) "False">
|
||||
<!ATTLIST file templating (creole|none) "creole">
|
||||
<!ATTLIST file engine (none|creole|jinja2|creole_legacy) #IMPLIED>
|
||||
<!ATTLIST file included (no|name|content) #IMPLIED>
|
||||
|
||||
<!ELEMENT override EMPTY>
|
||||
<!ATTLIST override source CDATA #IMPLIED>
|
||||
<!ATTLIST override templating (creole|none) "creole">
|
||||
<!ATTLIST override engine (none|creole|jinja2) #IMPLIED>
|
||||
|
||||
<!ELEMENT variables ((variable*|family*)*)>
|
||||
|
||||
@ -84,13 +80,15 @@
|
||||
<!ATTLIST family name CDATA #REQUIRED>
|
||||
<!ATTLIST family description CDATA #IMPLIED>
|
||||
<!ATTLIST family help CDATA #IMPLIED>
|
||||
<!ATTLIST family mode (basic|normal|expert) "basic">
|
||||
<!ATTLIST family mode CDATA #IMPLIED>
|
||||
<!ATTLIST family hidden (True|False) "False">
|
||||
<!ATTLIST family dynamic CDATA #IMPLIED>
|
||||
<!ATTLIST family leadership (True|False) "False">
|
||||
<!ATTLIST family provider CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT variable (value*)>
|
||||
<!ATTLIST variable name CDATA #REQUIRED>
|
||||
<!ATTLIST variable type (number|float|string|password|mail|boolean|filename|date|unix_user|ip|local_ip|netmask|network|broadcast|netbios|domain|hostname|web_address|port|mac|cidr|network_cidr|schedule|schedulemod) "string">
|
||||
<!ATTLIST variable type (number|float|string|password|mail|boolean|filename|date|unix_user|ip|local_ip|netmask|network|broadcast|netbios|domainname|hostname|web_address|port|mac|cidr|network_cidr|schedule|schedulemod) "string">
|
||||
<!ATTLIST variable description CDATA #IMPLIED>
|
||||
<!ATTLIST variable help CDATA #IMPLIED>
|
||||
<!ATTLIST variable hidden (True|False) "False">
|
||||
@ -101,15 +99,16 @@
|
||||
<!ATTLIST variable mandatory (True|False) "False">
|
||||
<!ATTLIST variable auto_freeze (True|False) "False">
|
||||
<!ATTLIST variable auto_save (True|False) "False">
|
||||
<!ATTLIST variable mode (basic|normal|expert) "normal">
|
||||
<!ATTLIST variable mode CDATA #IMPLIED>
|
||||
<!ATTLIST variable remove_check (True|False) "False">
|
||||
<!ATTLIST variable remove_condition (True|False) "False">
|
||||
<!ATTLIST variable remove_fill (True|False) "False">
|
||||
<!ATTLIST variable provider CDATA #IMPLIED>
|
||||
<!ATTLIST variable test CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT value (#PCDATA)>
|
||||
|
||||
<!ELEMENT constraints ((fill*|check*|condition*|group*)*)>
|
||||
<!ELEMENT constraints ((fill*|check*|condition*)*)>
|
||||
|
||||
<!ELEMENT fill ((target|param)+)>
|
||||
<!ATTLIST fill name CDATA #REQUIRED>
|
||||
@ -121,23 +120,15 @@
|
||||
<!ELEMENT condition ((target|param)+)>
|
||||
<!ATTLIST condition name (disabled_if_in|disabled_if_not_in|hidden_if_in|hidden_if_not_in|mandatory_if_in|mandatory_if_not_in) #REQUIRED>
|
||||
<!ATTLIST condition source CDATA #REQUIRED>
|
||||
<!ATTLIST condition fallback (True|False) "False">
|
||||
<!ATTLIST condition force_condition_on_fallback (True|False) "False">
|
||||
<!ATTLIST condition force_inverse_condition_on_fallback (True|False) "False">
|
||||
<!ATTLIST condition optional (True|False) "False">
|
||||
<!ATTLIST condition apply_on_fallback (True|False) #IMPLIED>
|
||||
|
||||
<!ELEMENT param (#PCDATA)>
|
||||
<!ATTLIST param type (string|number|nil|boolean|variable|information|suffix) "string">
|
||||
<!ATTLIST param type (string|number|nil|boolean|variable|function|information|target_information|suffix|index) "string">
|
||||
<!ATTLIST param name CDATA #IMPLIED>
|
||||
<!ATTLIST param propertyerror (True|False) "True">
|
||||
<!ATTLIST param optional (True|False) "False">
|
||||
|
||||
<!ELEMENT target (#PCDATA)>
|
||||
<!ATTLIST target type (family|variable|filelist|iplist|portlist) "variable">
|
||||
<!ATTLIST target type (variable|family|servicelist|filelist|iplist) "variable">
|
||||
<!ATTLIST target optional (True|False) "False">
|
||||
|
||||
<!ELEMENT group (follower+)>
|
||||
<!ATTLIST group leader CDATA #REQUIRED>
|
||||
<!ATTLIST group name CDATA #IMPLIED>
|
||||
<!ATTLIST group description CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT follower (#PCDATA)>
|
||||
|
@ -70,3 +70,8 @@ class DictConsistencyError(Exception):
|
||||
msg = _(f'{msg} in {display_xmlfiles(xmlfiles)}')
|
||||
super().__init__(msg)
|
||||
self.errno = errno
|
||||
|
||||
|
||||
class UpgradeError(Exception):
|
||||
"""Error during XML upgrade
|
||||
"""
|
||||
|
@ -29,19 +29,18 @@ from typing import Optional
|
||||
|
||||
from .i18n import _
|
||||
from .xmlreflector import XMLReflector
|
||||
from .utils import normalize_family
|
||||
from .utils import valid_variable_family_name
|
||||
from .error import SpaceObjShallNotBeUpdated, DictConsistencyError
|
||||
from .path import Path
|
||||
from .config import RougailConfig
|
||||
|
||||
# RougailObjSpace's elements that shall be forced to the Redefinable type
|
||||
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
|
||||
# RougailObjSpace's elements that shall be forced to the UnRedefinable type
|
||||
FORCE_UNREDEFINABLES = ('value',)
|
||||
# RougailObjSpace's elements that shall not be modify
|
||||
UNREDEFINABLE = ('multi', 'type')
|
||||
UNREDEFINABLE = ('multi', 'type',)
|
||||
# RougailObjSpace's elements that did not created automaticly
|
||||
FORCE_ELEMENTS = ('choice', 'property_', 'leadership', 'information')
|
||||
FORCE_ELEMENTS = ('choice', 'property_', 'information')
|
||||
# XML text are convert has name
|
||||
FORCED_TEXT_ELTS_AS_NAME = ('choice', 'property', 'value',)
|
||||
|
||||
@ -101,17 +100,20 @@ class RougailObjSpace:
|
||||
|
||||
def __init__(self,
|
||||
xmlreflector: XMLReflector,
|
||||
rougailconfig: 'RougailConfig',
|
||||
) -> None:
|
||||
self.space = ObjSpace()
|
||||
self.paths = Path()
|
||||
self.paths = Path(rougailconfig)
|
||||
|
||||
self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME)
|
||||
self.list_conditions = {}
|
||||
self.valid_enums = {}
|
||||
self.booleans_attributs = []
|
||||
self.has_dyn_option = False
|
||||
self.types = {}
|
||||
|
||||
self.make_object_space_classes(xmlreflector)
|
||||
self.rougailconfig = rougailconfig
|
||||
|
||||
def make_object_space_classes(self,
|
||||
xmlreflector: XMLReflector,
|
||||
@ -140,6 +142,8 @@ class RougailObjSpace:
|
||||
if dtd_attr.name in self.booleans_attributs:
|
||||
default_value = convert_boolean(default_value)
|
||||
attrs[dtd_attr.name] = default_value
|
||||
if dtd_attr.name.endswith('_type'):
|
||||
self.types[dtd_attr.name] = default_value
|
||||
if dtd_attr.name == 'redefine':
|
||||
# has a redefine attribute, so it's a Redefinable object
|
||||
clstype = Redefinable
|
||||
@ -193,16 +197,16 @@ class RougailObjSpace:
|
||||
if child.tag == 'family':
|
||||
if child.attrib['name'] in family_names:
|
||||
msg = _(f'Family "{child.attrib["name"]}" is set several times')
|
||||
raise DictConsistencyError(msg, 44, xmlfile)
|
||||
raise DictConsistencyError(msg, 44, [xmlfile])
|
||||
family_names.append(child.attrib['name'])
|
||||
try:
|
||||
# variable objects creation
|
||||
variableobj = self.get_variableobj(xmlfile,
|
||||
child,
|
||||
space,
|
||||
namespace,
|
||||
redefine_variables,
|
||||
)
|
||||
exists, variableobj = self.get_variableobj(xmlfile,
|
||||
child,
|
||||
space,
|
||||
namespace,
|
||||
redefine_variables,
|
||||
)
|
||||
except SpaceObjShallNotBeUpdated:
|
||||
continue
|
||||
self.set_text(child,
|
||||
@ -216,11 +220,12 @@ class RougailObjSpace:
|
||||
variableobj,
|
||||
redefine_variables,
|
||||
)
|
||||
self.set_path(namespace,
|
||||
document,
|
||||
variableobj,
|
||||
space,
|
||||
)
|
||||
if not exists:
|
||||
self.set_path(namespace,
|
||||
document,
|
||||
variableobj,
|
||||
space,
|
||||
)
|
||||
self.add_to_tree_structure(variableobj,
|
||||
space,
|
||||
child,
|
||||
@ -260,11 +265,12 @@ class RougailObjSpace:
|
||||
if child.tag in vars(space):
|
||||
# Atom instance has to be a singleton here
|
||||
# we do not re-create it, we reuse it
|
||||
return getattr(space, child.tag)
|
||||
return obj(xmlfile, name)
|
||||
return False, getattr(space, child.tag)
|
||||
return False, obj(xmlfile, name)
|
||||
# UnRedefinable object
|
||||
if child.tag not in vars(space):
|
||||
setattr(space, child.tag, [])
|
||||
return obj(xmlfile, name)
|
||||
return False, obj(xmlfile, name)
|
||||
|
||||
def _get_name(self,
|
||||
child,
|
||||
@ -290,6 +296,7 @@ class RougailObjSpace:
|
||||
"""A redefinable object could be created or updated
|
||||
"""
|
||||
existed_var = self.get_existed_obj(name,
|
||||
xmlfile,
|
||||
space,
|
||||
child,
|
||||
namespace,
|
||||
@ -301,12 +308,12 @@ class RougailObjSpace:
|
||||
redefine = convert_boolean(subspace.get('redefine', default_redefine))
|
||||
if redefine is True:
|
||||
if isinstance(existed_var, self.variable): # pylint: disable=E1101
|
||||
if namespace == RougailConfig['variable_namespace']:
|
||||
if namespace == self.rougailconfig['variable_namespace']:
|
||||
redefine_variables.append(name)
|
||||
else:
|
||||
redefine_variables.append(space.path + '.' + name)
|
||||
existed_var.xmlfiles.append(xmlfile)
|
||||
return existed_var
|
||||
return True, existed_var
|
||||
exists = convert_boolean(subspace.get('exists', True))
|
||||
if exists is False:
|
||||
raise SpaceObjShallNotBeUpdated()
|
||||
@ -322,15 +329,16 @@ class RougailObjSpace:
|
||||
if redefine is True:
|
||||
# cannot redefine an inexistant object
|
||||
msg = _(f'Redefined object: "{name}" does not exist yet')
|
||||
raise DictConsistencyError(msg, 46, xmlfile)
|
||||
raise DictConsistencyError(msg, 46, [xmlfile])
|
||||
tag = FORCE_TAG.get(child.tag, child.tag)
|
||||
if tag not in vars(space):
|
||||
setattr(space, tag, {})
|
||||
obj = getattr(self, child.tag)(xmlfile, name)
|
||||
return obj
|
||||
return False, obj
|
||||
|
||||
def get_existed_obj(self,
|
||||
name: str,
|
||||
xmlfile: str,
|
||||
space: str,
|
||||
child,
|
||||
namespace: str,
|
||||
@ -338,22 +346,22 @@ class RougailObjSpace:
|
||||
"""if an object exists, return it
|
||||
"""
|
||||
if child.tag in ['variable', 'family']:
|
||||
name = normalize_family(name)
|
||||
valid_variable_family_name(name, [xmlfile])
|
||||
if child.tag == 'variable': # pylint: disable=E1101
|
||||
if namespace != RougailConfig['variable_namespace']:
|
||||
if namespace != self.rougailconfig['variable_namespace']:
|
||||
name = space.path + '.' + name
|
||||
if not self.paths.path_is_defined(name):
|
||||
return None
|
||||
old_family_name = self.paths.get_variable_family_path(name)
|
||||
if space.path != old_family_name:
|
||||
msg = _(f'Variable was previously create in family "{old_family_name}", '
|
||||
msg = _(f'Variable "{name}" was previously create in family "{old_family_name}", '
|
||||
f'now it is in "{space.path}"')
|
||||
raise DictConsistencyError(msg, 47, space.xmlfiles)
|
||||
return self.paths.get_variable(name)
|
||||
# it's not a family
|
||||
tag = FORCE_TAG.get(child.tag, child.tag)
|
||||
children = getattr(space, tag, {})
|
||||
if name in children:
|
||||
if name in children and isinstance(children[name], getattr(self, child.tag)):
|
||||
return children[name]
|
||||
return None
|
||||
|
||||
@ -462,20 +470,25 @@ class RougailObjSpace:
|
||||
"""
|
||||
if isinstance(variableobj, self.variable): # pylint: disable=E1101
|
||||
if 'name' in document.attrib:
|
||||
family_name = normalize_family(document.attrib['name'])
|
||||
family_name = document.attrib['name']
|
||||
else:
|
||||
family_name = namespace
|
||||
|
||||
if isinstance(space, self.family) and space.leadership:
|
||||
leader = space.path
|
||||
else:
|
||||
leader = None
|
||||
self.paths.add_variable(namespace,
|
||||
normalize_family(variableobj.name),
|
||||
variableobj.name,
|
||||
space.path,
|
||||
document.attrib.get('dynamic') is not None,
|
||||
variableobj,
|
||||
leader,
|
||||
)
|
||||
elif isinstance(variableobj, self.family): # pylint: disable=E1101
|
||||
family_name = normalize_family(variableobj.name)
|
||||
if namespace != RougailConfig['variable_namespace']:
|
||||
family_name = namespace + '.' + family_name
|
||||
family_name = variableobj.name
|
||||
if namespace != self.rougailconfig['variable_namespace']:
|
||||
family_name = space.path + '.' + family_name
|
||||
self.paths.add_family(namespace,
|
||||
family_name,
|
||||
variableobj,
|
||||
@ -496,8 +509,6 @@ class RougailObjSpace:
|
||||
if isinstance(variableobj, Redefinable):
|
||||
name = variableobj.name
|
||||
tag = FORCE_TAG.get(child.tag, child.tag)
|
||||
if child.tag in ['family', 'variable']:
|
||||
name = normalize_family(name)
|
||||
getattr(space, tag)[name] = variableobj
|
||||
elif isinstance(variableobj, UnRedefinable):
|
||||
getattr(space, child.tag).append(variableobj)
|
||||
|
@ -24,23 +24,25 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import List
|
||||
from .i18n import _
|
||||
from .error import DictConsistencyError
|
||||
from .config import RougailConfig
|
||||
from .utils import normalize_family
|
||||
|
||||
|
||||
class Path:
|
||||
"""Helper class to handle the `path` attribute of a CreoleObjSpace
|
||||
instance.
|
||||
"""Helper class to handle the `path` attribute.
|
||||
|
||||
sample: path="creole.general.condition"
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self,
|
||||
rougailconfig: 'RougailConfig',
|
||||
) -> None:
|
||||
self.variables = {}
|
||||
self.families = {}
|
||||
self.full_paths_families = {}
|
||||
self.full_paths_variables = {}
|
||||
self.variable_namespace = rougailconfig['variable_namespace']
|
||||
|
||||
# Family
|
||||
def add_family(self,
|
||||
@ -51,8 +53,11 @@ class Path:
|
||||
) -> str: # pylint: disable=C0111
|
||||
"""Add a new family
|
||||
"""
|
||||
if namespace == RougailConfig['variable_namespace']:
|
||||
if namespace == self.variable_namespace:
|
||||
full_name = '.'.join([subpath, name])
|
||||
if name in self.full_paths_families:
|
||||
msg = _(f'Duplicate family name "{name}"')
|
||||
raise DictConsistencyError(msg, 55, variableobj.xmlfiles)
|
||||
self.full_paths_families[name] = full_name
|
||||
else:
|
||||
if '.' not in name: # pragma: no cover
|
||||
@ -61,26 +66,17 @@ class Path:
|
||||
full_name = name
|
||||
if full_name in self.families and \
|
||||
self.families[full_name]['variableobj'] != variableobj: # pragma: no cover
|
||||
raise DictConsistencyError(_(f'Duplicate family name "{name}"'), 37, variableobj.xmlfiles)
|
||||
msg = _(f'Duplicate family name "{name}"')
|
||||
raise DictConsistencyError(msg, 37, variableobj.xmlfiles)
|
||||
if full_name in self.variables:
|
||||
msg = _(f'A variable and a family has the same path "{full_name}"')
|
||||
raise DictConsistencyError(msg, 56, variableobj.xmlfiles)
|
||||
self.families[full_name] = dict(name=name,
|
||||
namespace=namespace,
|
||||
variableobj=variableobj,
|
||||
)
|
||||
variableobj.path = full_name
|
||||
|
||||
def add_leadership(self,
|
||||
namespace: str,
|
||||
path: str,
|
||||
variableobj: str,
|
||||
) -> str: # pylint: disable=C0111
|
||||
"""add a new leadership
|
||||
"""
|
||||
self.families[path] = dict(name=path,
|
||||
namespace=namespace,
|
||||
variableobj=variableobj,
|
||||
)
|
||||
variableobj.path = path
|
||||
|
||||
def get_family(self,
|
||||
name: str,
|
||||
current_namespace: str,
|
||||
@ -93,33 +89,13 @@ class Path:
|
||||
if name not in self.families:
|
||||
raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
|
||||
dico = self.families[name]
|
||||
if current_namespace not in [RougailConfig['variable_namespace'], 'services'] and \
|
||||
if current_namespace not in [self.variable_namespace, 'services'] and \
|
||||
current_namespace != dico['namespace']:
|
||||
msg = _(f'A family located in the "{dico["namespace"]}" namespace '
|
||||
f'shall not be used in the "{current_namespace}" namespace')
|
||||
raise DictConsistencyError(msg, 38, [])
|
||||
return dico['variableobj']
|
||||
|
||||
# Leadership
|
||||
def set_leader(self,
|
||||
namespace: str,
|
||||
leader_family_name: str,
|
||||
leadership_name: str,
|
||||
name: str,
|
||||
) -> None: # pylint: disable=C0111
|
||||
"""set a variable a leadership member
|
||||
"""
|
||||
# need rebuild path and move object in new path
|
||||
old_path = leader_family_name + '.' + name
|
||||
leadership_path = leader_family_name + '.' + leadership_name
|
||||
new_path = leadership_path + '.' + name
|
||||
self.variables[new_path] = self.variables.pop(old_path)
|
||||
self.variables[new_path]['leader'] = leadership_path
|
||||
self.variables[new_path]['variableobj'].path = new_path
|
||||
self.variables[new_path]['family'] = leadership_path
|
||||
if namespace == RougailConfig['variable_namespace']:
|
||||
self.full_paths_variables[name] = new_path
|
||||
|
||||
def is_leader(self, path): # pylint: disable=C0111
|
||||
"""Is the variable is a leader
|
||||
"""
|
||||
@ -127,7 +103,16 @@ class Path:
|
||||
if not variable['leader']:
|
||||
return False
|
||||
leadership = self.get_family(variable['leader'], variable['variableobj'].namespace)
|
||||
return leadership.variable[0].path == path
|
||||
return next(iter(leadership.variable.values())).path == path
|
||||
|
||||
def is_follower(self, path):
|
||||
"""Is the variable is a follower
|
||||
"""
|
||||
variable = self._get_variable(path)
|
||||
if not variable['leader']:
|
||||
return False
|
||||
leadership = self.get_family(variable['leader'], variable['variableobj'].namespace)
|
||||
return next(iter(leadership.variable.values())).path != path
|
||||
|
||||
# Variable
|
||||
def add_variable(self, # pylint: disable=R0913
|
||||
@ -136,55 +121,63 @@ class Path:
|
||||
family: str,
|
||||
is_dynamic: bool,
|
||||
variableobj,
|
||||
leader: 'self.objectspace.family'=None,
|
||||
) -> str: # pylint: disable=C0111
|
||||
"""Add a new variable (with path)
|
||||
"""
|
||||
if '.' not in name:
|
||||
full_path = '.'.join([family, name])
|
||||
if namespace == RougailConfig['variable_namespace']:
|
||||
if namespace == self.variable_namespace:
|
||||
self.full_paths_variables[name] = full_path
|
||||
else:
|
||||
full_path = name
|
||||
variableobj.path = full_path
|
||||
if full_path in self.families:
|
||||
msg = _(f'A family and a variable has the same path "{full_path}"')
|
||||
raise DictConsistencyError(msg, 57, variableobj.xmlfiles)
|
||||
self.variables[full_path] = dict(name=name,
|
||||
family=family,
|
||||
leader=None,
|
||||
leader=leader,
|
||||
is_dynamic=is_dynamic,
|
||||
variableobj=variableobj,
|
||||
)
|
||||
|
||||
def get_variable(self,
|
||||
name: str,
|
||||
xmlfiles: List[str]=[],
|
||||
) -> 'Variable': # pylint: disable=C0111
|
||||
"""Get variable object from a path
|
||||
"""
|
||||
variable, suffix = self._get_variable(name, with_suffix=True)
|
||||
variable, suffix = self._get_variable(name, with_suffix=True, xmlfiles=xmlfiles)
|
||||
if suffix:
|
||||
raise DictConsistencyError(_(f"{name} is a dynamic variable"), 36, [])
|
||||
return variable['variableobj']
|
||||
|
||||
def get_variable_family_path(self,
|
||||
name: str,
|
||||
xmlfiles: List[str]=False,
|
||||
) -> str: # pylint: disable=C0111
|
||||
"""Get the full path of a family
|
||||
"""
|
||||
return self._get_variable(name)['family']
|
||||
return self._get_variable(name, xmlfiles=xmlfiles)['family']
|
||||
|
||||
def get_variable_path(self,
|
||||
name: str,
|
||||
current_namespace: str,
|
||||
xmlfiles: List[str]=[],
|
||||
) -> str: # pylint: disable=C0111
|
||||
"""get full path of a variable
|
||||
"""
|
||||
dico, suffix = self._get_variable(name,
|
||||
with_suffix=True,
|
||||
xmlfiles=xmlfiles,
|
||||
)
|
||||
namespace = dico['variableobj'].namespace
|
||||
if namespace not in [RougailConfig['variable_namespace'], 'services'] and \
|
||||
if namespace not in [self.variable_namespace, 'services'] and \
|
||||
current_namespace != namespace:
|
||||
msg = _(f'A variable located in the "{namespace}" namespace shall not be used '
|
||||
f'in the "{current_namespace}" namespace')
|
||||
raise DictConsistencyError(msg, 41, [])
|
||||
raise DictConsistencyError(msg, 41, xmlfiles)
|
||||
return dico['variableobj'].path, suffix
|
||||
|
||||
def path_is_defined(self,
|
||||
@ -206,6 +199,7 @@ class Path:
|
||||
def _get_variable(self,
|
||||
name: str,
|
||||
with_suffix: bool=False,
|
||||
xmlfiles: List[str]=[],
|
||||
) -> str:
|
||||
name = '.'.join([normalize_family(subname) for subname in name.split('.')])
|
||||
if name not in self.variables:
|
||||
@ -218,7 +212,7 @@ class Path:
|
||||
if variable['is_dynamic']:
|
||||
return variable, name[len(var_name):]
|
||||
if name not in self.variables:
|
||||
raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
|
||||
raise DictConsistencyError(_(f'unknown option "{name}"'), 42, xmlfiles)
|
||||
if with_suffix:
|
||||
return self.variables[name], None
|
||||
return self.variables[name]
|
||||
|
@ -1,397 +0,0 @@
|
||||
"""Template langage for Rougail
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2019-2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from shutil import copy
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
from subprocess import call
|
||||
from os import listdir, makedirs, getcwd, chdir
|
||||
from os.path import dirname, join, isfile, abspath, normpath, isdir
|
||||
|
||||
from Cheetah.Template import Template as ChtTemplate
|
||||
from Cheetah.NameMapper import NotFound as CheetahNotFound
|
||||
|
||||
try:
|
||||
from tiramisu3 import Config
|
||||
from tiramisu3.error import PropertiesOptionError # pragma: no cover
|
||||
except ModuleNotFoundError: # pragma: no cover
|
||||
from tiramisu import Config
|
||||
from tiramisu.error import PropertiesOptionError
|
||||
|
||||
from .config import RougailConfig
|
||||
from .error import FileNotFound, TemplateError
|
||||
from .i18n import _
|
||||
from .utils import normalize_family, load_modules
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
@classmethod
|
||||
def cl_compile(kls, *args, **kwargs):
|
||||
"""Rewrite compile methode to force some settings
|
||||
"""
|
||||
kwargs['compilerSettings'] = {'directiveStartToken' : '%',
|
||||
'cheetahVarStartToken' : '%%',
|
||||
'EOLSlurpToken' : '%',
|
||||
'PSPStartToken' : 'µ' * 10,
|
||||
'PSPEndToken' : 'µ' * 10,
|
||||
'commentStartToken' : 'µ' * 10,
|
||||
'commentEndToken' : 'µ' * 10,
|
||||
'multiLineCommentStartToken' : 'µ' * 10,
|
||||
'multiLineCommentEndToken' : 'µ' * 10}
|
||||
return kls.old_compile(*args, **kwargs) # pylint: disable=E1101
|
||||
ChtTemplate.old_compile = ChtTemplate.compile
|
||||
ChtTemplate.compile = cl_compile
|
||||
|
||||
|
||||
class CheetahTemplate(ChtTemplate): # pylint: disable=W0223
|
||||
"""Construct a cheetah templating object
|
||||
"""
|
||||
def __init__(self,
|
||||
filename: str,
|
||||
context,
|
||||
eosfunc: Dict,
|
||||
extra_context: Dict,
|
||||
):
|
||||
"""Initialize Creole CheetahTemplate
|
||||
"""
|
||||
ChtTemplate.__init__(self,
|
||||
file=filename,
|
||||
searchList=[context, eosfunc, extra_context])
|
||||
|
||||
# FORK of Cheetah function, do not replace '\\' by '/'
|
||||
def serverSidePath(self,
|
||||
path=None,
|
||||
normpath=normpath,
|
||||
abspath=abspath
|
||||
): # pylint: disable=W0621
|
||||
|
||||
# strange...
|
||||
if path is None and isinstance(self, str):
|
||||
path = self
|
||||
if path: # pylint: disable=R1705
|
||||
return normpath(abspath(path))
|
||||
# original code return normpath(abspath(path.replace("\\", '/')))
|
||||
elif hasattr(self, '_filePath') and self._filePath: # pragma: no cover
|
||||
return normpath(abspath(self._filePath))
|
||||
else: # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
class RougailLeaderIndex:
|
||||
"""This object is create when access to a specified Index of the variable
|
||||
"""
|
||||
def __init__(self,
|
||||
value,
|
||||
follower,
|
||||
index,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._follower = follower
|
||||
self._index = index
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name not in self._follower:
|
||||
raise AttributeError()
|
||||
value = self._follower[name]
|
||||
if isinstance(value, PropertiesOptionError):
|
||||
raise AttributeError()
|
||||
return value
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __lt__(self, value):
|
||||
return self._value.__lt__(value)
|
||||
|
||||
def __le__(self, value):
|
||||
return self._value.__le__(value)
|
||||
|
||||
def __eq__(self, value):
|
||||
return self._value.__eq__(value)
|
||||
|
||||
def __ne__(self, value):
|
||||
return self._value.__ne__(value)
|
||||
|
||||
def __gt__(self, value):
|
||||
return self._value.__gt__(value)
|
||||
|
||||
def __ge__(self, value):
|
||||
return self._value >= value
|
||||
|
||||
def __add__(self, value):
|
||||
return self._value.__add__(value)
|
||||
|
||||
def __radd__(self, value):
|
||||
return value + self._value
|
||||
|
||||
|
||||
class RougailLeader:
|
||||
"""Implement access to leader and follower variable
|
||||
For examples: %%leader, %%leader[0].follower1
|
||||
"""
|
||||
def __init__(self,
|
||||
value,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._follower = {}
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Get a leader.follower at requested index.
|
||||
"""
|
||||
followers = {key: values[index] for key, values in self._follower.items()}
|
||||
return RougailLeaderIndex(self._value[index],
|
||||
followers,
|
||||
index,
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over leader.follower.
|
||||
|
||||
Return synchronised value of leader.follower.
|
||||
"""
|
||||
for index in range(len(self._value)):
|
||||
yield self.__getitem__(index)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._value)
|
||||
|
||||
def __contains__(self, value):
|
||||
return self._value.__contains__(value)
|
||||
|
||||
async def add_follower(self,
|
||||
config,
|
||||
name: str,
|
||||
path: str,
|
||||
):
|
||||
"""Add a new follower
|
||||
"""
|
||||
self._follower[name] = []
|
||||
for index in range(len(self._value)):
|
||||
try:
|
||||
value = await config.option(path, index).value.get()
|
||||
except PropertiesOptionError as err:
|
||||
value = err
|
||||
self._follower[name].append(value)
|
||||
|
||||
|
||||
class RougailExtra:
|
||||
"""Object that implement access to extra variable
|
||||
For example %%extra1.family.variable
|
||||
"""
|
||||
def __init__(self,
|
||||
suboption: Dict) -> None:
|
||||
self.suboption = suboption
|
||||
|
||||
def __getattr__(self,
|
||||
key: str,
|
||||
) -> Any:
|
||||
return self.suboption[key]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.suboption.values())
|
||||
|
||||
|
||||
class RougailTemplate:
|
||||
"""Engine to process Creole cheetah template
|
||||
"""
|
||||
def __init__(self, # pylint: disable=R0913
|
||||
config: Config,
|
||||
) -> None:
|
||||
self.config = config
|
||||
self.destinations_dir = abspath(RougailConfig['destinations_dir'])
|
||||
self.tmp_dir = abspath(RougailConfig['tmp_dir'])
|
||||
self.templates_dir = abspath(RougailConfig['templates_dir'])
|
||||
self.patches_dir = abspath(RougailConfig['patches_dir'])
|
||||
eos = {}
|
||||
functions_file = RougailConfig['functions_file']
|
||||
if isfile(functions_file):
|
||||
eosfunc = load_modules(functions_file)
|
||||
for func in dir(eosfunc):
|
||||
if not func.startswith('_'):
|
||||
eos[func] = getattr(eosfunc, func)
|
||||
self.eosfunc = eos
|
||||
self.rougail_variables_dict = {}
|
||||
|
||||
def patch_template(self,
|
||||
filename: str,
|
||||
) -> None:
|
||||
"""Apply patch to a template
|
||||
"""
|
||||
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1', '-f']
|
||||
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
|
||||
|
||||
patch_file = join(self.patches_dir, f'{filename}.patch')
|
||||
if isfile(patch_file):
|
||||
log.info(_("Patching template '{filename}' with '{patch_file}'"))
|
||||
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file])
|
||||
if ret: # pragma: no cover
|
||||
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file])
|
||||
msg = _(f"Error applying patch: '{patch_file}'\n"
|
||||
f"To reproduce and fix this error {patch_cmd_err}")
|
||||
log.error(_(msg))
|
||||
copy(join(self.templates_dir, filename), self.tmp_dir)
|
||||
|
||||
def prepare_template(self,
|
||||
filename: str,
|
||||
) -> None:
|
||||
"""Prepare template source file
|
||||
"""
|
||||
log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'"))
|
||||
if not isdir(self.tmp_dir):
|
||||
raise TemplateError(_(f'cannot find tmp_dir {self.tmp_dir}'))
|
||||
copy(filename, self.tmp_dir)
|
||||
self.patch_template(filename)
|
||||
|
||||
def process(self,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
variable: Any,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
log.info(_(f"Cheetah processing: '{destfilename}'"))
|
||||
try:
|
||||
extra_context = {'normalize_family': normalize_family,
|
||||
'rougail_filename': true_destfilename
|
||||
}
|
||||
if variable:
|
||||
extra_context['rougail_variable'] = variable
|
||||
cheetah_template = CheetahTemplate(source,
|
||||
self.rougail_variables_dict,
|
||||
self.eosfunc,
|
||||
extra_context,
|
||||
)
|
||||
data = str(cheetah_template)
|
||||
except CheetahNotFound as err: # pragma: no cover
|
||||
varname = err.args[0][13:-1]
|
||||
msg = f"Error: unknown variable used in template {source} to {destfilename}: {varname}"
|
||||
raise TemplateError(_(msg)) from err
|
||||
except Exception as err: # pragma: no cover
|
||||
msg = _(f"Error while instantiating template {source} to {destfilename}: {err}")
|
||||
raise TemplateError(msg) from err
|
||||
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
||||
|
||||
def instance_file(self,
|
||||
filevar: Dict,
|
||||
) -> None:
|
||||
"""Run templatisation on one file
|
||||
"""
|
||||
log.info(_("Instantiating file '{filename}'"))
|
||||
if 'variable' in filevar:
|
||||
variable = filevar['variable']
|
||||
else:
|
||||
variable = None
|
||||
filenames = filevar['name']
|
||||
if not isinstance(filenames, list):
|
||||
filenames = [filenames]
|
||||
if variable:
|
||||
variable = [variable]
|
||||
if not isdir(self.destinations_dir):
|
||||
raise TemplateError(_(f'cannot find destinations_dir {self.destinations_dir}'))
|
||||
for idx, filename in enumerate(filenames):
|
||||
destfilename = join(self.destinations_dir, filename[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
if variable:
|
||||
var = variable[idx]
|
||||
else:
|
||||
var = None
|
||||
source = join(self.tmp_dir, filevar['source'])
|
||||
if filevar['templating'] == 'creole':
|
||||
self.process(source,
|
||||
filename,
|
||||
destfilename,
|
||||
var,
|
||||
)
|
||||
else:
|
||||
copy(source, destfilename)
|
||||
|
||||
async def instance_files(self) -> None:
|
||||
"""Run templatisation on all files
|
||||
"""
|
||||
ori_dir = getcwd()
|
||||
chdir(self.templates_dir)
|
||||
for option in await self.config.option.list(type='all'):
|
||||
namespace = await option.option.name()
|
||||
is_variable_namespace = namespace == RougailConfig['variable_namespace']
|
||||
self.rougail_variables_dict[namespace] = await self.load_variables(option,
|
||||
is_variable_namespace,
|
||||
)
|
||||
for template in listdir('.'):
|
||||
self.prepare_template(template)
|
||||
for service_obj in await self.config.option('services').list('all'):
|
||||
for fills in await service_obj.list('all'):
|
||||
if await fills.option.name() in ['files', 'overrides']:
|
||||
for fill_obj in await fills.list('all'):
|
||||
fill = await fill_obj.value.dict()
|
||||
filename = fill['source']
|
||||
if not isfile(filename): # pragma: no cover
|
||||
raise FileNotFound(_(f"File {filename} does not exist."))
|
||||
if fill['activate']:
|
||||
self.instance_file(fill)
|
||||
else:
|
||||
log.debug(_("Instantiation of file '{filename}' disabled"))
|
||||
chdir(ori_dir)
|
||||
|
||||
async def load_variables(self,
|
||||
optiondescription,
|
||||
is_variable_namespace,
|
||||
) -> RougailExtra:
|
||||
"""Load all variables and set it in RougailExtra objects
|
||||
"""
|
||||
variables = {}
|
||||
for option in await optiondescription.list('all'):
|
||||
if await option.option.isoptiondescription():
|
||||
if await option.option.isleadership():
|
||||
for idx, suboption in enumerate(await option.list('all')):
|
||||
if idx == 0:
|
||||
leader = RougailLeader(await suboption.value.get())
|
||||
leader_name = await suboption.option.name()
|
||||
if is_variable_namespace:
|
||||
self.rougail_variables_dict[await suboption.option.name()] = leader
|
||||
else:
|
||||
await leader.add_follower(self.config,
|
||||
await suboption.option.name(),
|
||||
await suboption.option.path(),
|
||||
)
|
||||
variables[leader_name] = leader
|
||||
else:
|
||||
subfamilies = await self.load_variables(option,
|
||||
is_variable_namespace,
|
||||
)
|
||||
variables[await option.option.name()] = subfamilies
|
||||
else:
|
||||
if is_variable_namespace:
|
||||
self.rougail_variables_dict[await option.option.name()] = await option.value.get()
|
||||
variables[await option.option.name()] = await option.value.get()
|
||||
return RougailExtra(variables)
|
440
src/rougail/template/base.py
Normal file
440
src/rougail/template/base.py
Normal file
@ -0,0 +1,440 @@
|
||||
"""Template langage for Rougail
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2019-2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from shutil import copy
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
from subprocess import call
|
||||
from os import listdir, makedirs, getcwd, chdir
|
||||
from os.path import dirname, join, isfile, isdir, abspath
|
||||
|
||||
|
||||
try:
|
||||
from tiramisu3 import Config, undefined
|
||||
from tiramisu3.error import PropertiesOptionError # pragma: no cover
|
||||
except ModuleNotFoundError: # pragma: no cover
|
||||
from tiramisu import Config, undefined
|
||||
from tiramisu.error import PropertiesOptionError
|
||||
|
||||
from ..config import RougailConfig
|
||||
from ..error import FileNotFound, TemplateError
|
||||
from ..i18n import _
|
||||
from ..utils import load_modules
|
||||
|
||||
from . import engine as engines
|
||||
ENGINES = {}
|
||||
for engine in engines.__all__:
|
||||
ENGINES[engine] = getattr(engines, engine)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
INFORMATIONS = {'files': ['source', 'mode', 'owner', 'group', 'engine', 'included'],
|
||||
'overrides': ['name', 'source', 'engine'],
|
||||
}
|
||||
|
||||
|
||||
class RougailLeaderIndex:
|
||||
"""This object is create when access to a specified Index of the variable
|
||||
"""
|
||||
def __init__(self,
|
||||
value,
|
||||
follower,
|
||||
index,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._follower = follower
|
||||
self._index = index
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name not in self._follower:
|
||||
raise AttributeError(f'unable to find follower "{name}"')
|
||||
value = self._follower[name]
|
||||
if isinstance(value, PropertiesOptionError):
|
||||
raise AttributeError(f'unable to access to follower "{name}": {value}')
|
||||
return value
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __contains__(self, name):
|
||||
if self._follower.__contains__(name):
|
||||
value = self._follower[name]
|
||||
return not isinstance(value, PropertiesOptionError)
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __lt__(self, value):
|
||||
return self._value.__lt__(value)
|
||||
|
||||
def __le__(self, value):
|
||||
return self._value.__le__(value)
|
||||
|
||||
def __eq__(self, value):
|
||||
return self._value.__eq__(value)
|
||||
|
||||
def __ne__(self, value):
|
||||
return self._value.__ne__(value)
|
||||
|
||||
def __gt__(self, value):
|
||||
return self._value.__gt__(value)
|
||||
|
||||
def __ge__(self, value):
|
||||
return self._value >= value
|
||||
|
||||
def __add__(self, value):
|
||||
return self._value.__add__(value)
|
||||
|
||||
def __radd__(self, value):
|
||||
return value + self._value
|
||||
|
||||
|
||||
class RougailLeader:
|
||||
"""Implement access to leader and follower variable
|
||||
For examples: %%leader, %%leader[0].follower1
|
||||
"""
|
||||
def __init__(self,
|
||||
leader_name,
|
||||
value,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._follower = {leader_name: value}
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Get a leader.follower at requested index.
|
||||
"""
|
||||
followers = {key: values[index] for key, values in self._follower.items()}
|
||||
return RougailLeaderIndex(self._value[index],
|
||||
followers,
|
||||
index,
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over leader.follower.
|
||||
|
||||
Return synchronised value of leader.follower.
|
||||
"""
|
||||
for index in range(len(self._value)):
|
||||
yield self.__getitem__(index)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._value)
|
||||
|
||||
def __contains__(self, value):
|
||||
return self._value.__contains__(value)
|
||||
|
||||
async def _add_follower(self,
|
||||
config,
|
||||
name: str,
|
||||
path: str,
|
||||
):
|
||||
"""Add a new follower
|
||||
"""
|
||||
self._follower[name] = []
|
||||
for index in range(len(self._value)):
|
||||
try:
|
||||
value = await config.option(path, index).value.get()
|
||||
except PropertiesOptionError as err:
|
||||
value = err
|
||||
self._follower[name].append(value)
|
||||
|
||||
|
||||
class RougailExtra:
|
||||
"""Object that implement access to extra variable
|
||||
For example %%extra1.family.variable
|
||||
"""
|
||||
def __init__(self,
|
||||
suboption: Dict,
|
||||
) -> None:
|
||||
self._suboption = suboption
|
||||
|
||||
def __getattr__(self,
|
||||
key: str,
|
||||
) -> Any:
|
||||
try:
|
||||
return self._suboption[key]
|
||||
except KeyError:
|
||||
raise AttributeError(f'unable to find extra "{key}"')
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._suboption.values())
|
||||
|
||||
def items(self):
|
||||
return self._suboption.items()
|
||||
|
||||
def __str__(self):
|
||||
return f'<extra object with: {self._suboption}>'
|
||||
|
||||
|
||||
class RougailBaseTemplate:
|
||||
"""Engine to process Creole cheetah template
|
||||
"""
|
||||
def __init__(self, # pylint: disable=R0913
|
||||
config: Config,
|
||||
rougailconfig: RougailConfig=None,
|
||||
) -> None:
|
||||
if rougailconfig is None:
|
||||
rougailconfig = RougailConfig
|
||||
self.config = config
|
||||
self.destinations_dir = abspath(rougailconfig['destinations_dir'])
|
||||
self.tmp_dir = abspath(rougailconfig['tmp_dir'])
|
||||
self.templates_dir = abspath(rougailconfig['templates_dir'])
|
||||
self.patches_dir = abspath(rougailconfig['patches_dir'])
|
||||
eos = {}
|
||||
functions_file = rougailconfig['functions_file']
|
||||
if isfile(functions_file):
|
||||
eosfunc = load_modules(functions_file)
|
||||
for func in dir(eosfunc):
|
||||
if not func.startswith('_'):
|
||||
eos[func] = getattr(eosfunc, func)
|
||||
self.eosfunc = eos
|
||||
self.rougail_variables_dict = {}
|
||||
self.rougailconfig = rougailconfig
|
||||
self.log = log
|
||||
self.engines = ENGINES
|
||||
|
||||
def patch_template(self,
|
||||
filename: str,
|
||||
) -> None:
|
||||
"""Apply patch to a template
|
||||
"""
|
||||
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1', '-f']
|
||||
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
|
||||
|
||||
patch_file = join(self.patches_dir, f'{filename}.patch')
|
||||
if isfile(patch_file):
|
||||
self.log.info(_("Patching template '{filename}' with '{patch_file}'"))
|
||||
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file])
|
||||
if ret: # pragma: no cover
|
||||
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file])
|
||||
msg = _(f"Error applying patch: '{patch_file}'\n"
|
||||
f"To reproduce and fix this error {patch_cmd_err}")
|
||||
self.log.error(_(msg))
|
||||
copy(join(self.templates_dir, filename), self.tmp_dir)
|
||||
|
||||
def prepare_template(self,
|
||||
filename: str,
|
||||
) -> None:
|
||||
"""Prepare template source file
|
||||
"""
|
||||
self.log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'"))
|
||||
if not isdir(self.tmp_dir):
|
||||
raise TemplateError(_(f'cannot find tmp_dir {self.tmp_dir}'))
|
||||
copy(filename, self.tmp_dir)
|
||||
self.patch_template(filename)
|
||||
|
||||
def instance_file(self,
|
||||
filevar: Dict,
|
||||
type_: str,
|
||||
service_name: str,
|
||||
) -> None:
|
||||
"""Run templatisation on one file
|
||||
"""
|
||||
self.log.info(_("Instantiating file '{filename}'"))
|
||||
if 'variable' in filevar:
|
||||
variable = filevar['variable']
|
||||
else:
|
||||
variable = None
|
||||
filenames = filevar.get('name')
|
||||
if not isinstance(filenames, list):
|
||||
filenames = [filenames]
|
||||
if variable and not isinstance(variable, list):
|
||||
variable = [variable]
|
||||
if not isdir(self.destinations_dir):
|
||||
raise TemplateError(_(f'cannot find destinations_dir {self.destinations_dir}'))
|
||||
for idx, filename, in enumerate(filenames):
|
||||
if variable:
|
||||
var = variable[idx]
|
||||
else:
|
||||
var = None
|
||||
func = f'_instance_{type_}'
|
||||
data = getattr(self, func)(filevar,
|
||||
filename,
|
||||
service_name,
|
||||
variable,
|
||||
idx,
|
||||
)
|
||||
if data is None:
|
||||
continue
|
||||
filename, source, destfile, var = data
|
||||
destfilename = join(self.destinations_dir, destfile[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
self.log.info(_(f"{filevar['engine']} processing: '{destfilename}'"))
|
||||
self.engines[filevar['engine']].process(filename=filename,
|
||||
source=source,
|
||||
true_destfilename=destfile,
|
||||
destfilename=destfilename,
|
||||
destdir=self.destinations_dir,
|
||||
variable=var,
|
||||
rougail_variables_dict=self.rougail_variables_dict,
|
||||
eosfunc=self.eosfunc,
|
||||
)
|
||||
|
||||
async def instance_files(self) -> None:
|
||||
"""Run templatisation on all files
|
||||
"""
|
||||
ori_dir = getcwd()
|
||||
chdir(self.templates_dir)
|
||||
for option in await self.config.option.list(type='all'):
|
||||
namespace = await option.option.name()
|
||||
is_variable_namespace = namespace == self.rougailconfig['variable_namespace']
|
||||
if namespace == 'services':
|
||||
is_service_namespace = 'root'
|
||||
else:
|
||||
is_service_namespace = False
|
||||
self.rougail_variables_dict[namespace] = await self.load_variables(option,
|
||||
is_variable_namespace,
|
||||
is_service_namespace,
|
||||
)
|
||||
for template in listdir('.'):
|
||||
self.prepare_template(template)
|
||||
for included in (True, False):
|
||||
for service_obj in await self.config.option('services').list('all'):
|
||||
service_name = await service_obj.option.name()
|
||||
if await service_obj.option('activate').value.get() is False:
|
||||
if included is False:
|
||||
self.desactive_service(service_name)
|
||||
continue
|
||||
for fills in await service_obj.list('optiondescription'):
|
||||
type_ = await fills.option.name()
|
||||
for fill_obj in await fills.list('all'):
|
||||
fill = await fill_obj.value.dict()
|
||||
await self.get_informations(type_, fill, fill_obj)
|
||||
if 'included' in fill:
|
||||
if (fill['included'] == 'no' and included is True) or \
|
||||
(fill['included'] != 'no' and included is False):
|
||||
continue
|
||||
elif included is True:
|
||||
continue
|
||||
if fill['activate']:
|
||||
self.instance_file(fill, type_, service_name)
|
||||
else:
|
||||
self.log.debug(_("Instantiation of file '{filename}' disabled"))
|
||||
self.post_instance_service(service_name)
|
||||
self.post_instance()
|
||||
chdir(ori_dir)
|
||||
|
||||
async def get_informations(self,
|
||||
type_: str,
|
||||
dico: dict,
|
||||
obj: 'Option',
|
||||
) -> None:
|
||||
for key in INFORMATIONS.get(type_, []):
|
||||
default_key = f'default_{type_}_{key}'
|
||||
if default_key in RougailConfig:
|
||||
default_value = RougailConfig[default_key]
|
||||
else:
|
||||
default_value = undefined
|
||||
dico[key] = await obj.information.get(key, default_value)
|
||||
|
||||
def desactive_service(self,
|
||||
service_name: str,
|
||||
):
|
||||
raise NotImplementedError(_('cannot desactivate a service'))
|
||||
|
||||
def post_instance_service(self, service_name): # pragma: no cover
|
||||
pass
|
||||
|
||||
def post_instance(self): # pragma: no cover
|
||||
pass
|
||||
|
||||
def _instance_ip(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
raise NotImplementedError(_('cannot instanciate this service type ip'))
|
||||
|
||||
def _instance_files(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
raise NotImplementedError(_('cannot instanciate this service type file'))
|
||||
|
||||
def _instance_overrides(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
raise NotImplementedError(_('cannot instanciate this service type override'))
|
||||
|
||||
async def load_variables(self,
|
||||
optiondescription,
|
||||
is_variable_namespace: str,
|
||||
is_service_namespace: str,
|
||||
) -> RougailExtra:
|
||||
"""Load all variables and set it in RougailExtra objects
|
||||
"""
|
||||
variables = {}
|
||||
for option in await optiondescription.list('all'):
|
||||
if await option.option.isoptiondescription():
|
||||
if await option.option.isleadership():
|
||||
for idx, suboption in enumerate(await option.list('all')):
|
||||
if idx == 0:
|
||||
leader_name = await suboption.option.name()
|
||||
leader = RougailLeader(leader_name, await suboption.value.get())
|
||||
leadership_name = await option.option.name()
|
||||
if is_variable_namespace:
|
||||
self.rougail_variables_dict[await suboption.option.name()] = leader
|
||||
else:
|
||||
await leader._add_follower(self.config,
|
||||
await suboption.option.name(),
|
||||
await suboption.option.path(),
|
||||
)
|
||||
variables[leadership_name] = leader
|
||||
else:
|
||||
if is_service_namespace == 'root':
|
||||
new_is_service_namespace = 'service_name'
|
||||
elif is_service_namespace == 'service_name':
|
||||
new_is_service_namespace = await option.option.name()
|
||||
elif is_service_namespace in INFORMATIONS:
|
||||
# remove 's'
|
||||
new_is_service_namespace = is_service_namespace[:-1]
|
||||
else:
|
||||
new_is_service_namespace = is_service_namespace
|
||||
subfamilies = await self.load_variables(option,
|
||||
is_variable_namespace,
|
||||
new_is_service_namespace,
|
||||
)
|
||||
variables[await option.option.name()] = subfamilies
|
||||
else:
|
||||
if is_variable_namespace:
|
||||
value = await option.value.get()
|
||||
self.rougail_variables_dict[await option.option.name()] = value
|
||||
if await option.option.issymlinkoption() and await option.option.isfollower():
|
||||
value = []
|
||||
path = await option.option.path()
|
||||
for index in range(await option.value.len()):
|
||||
value.append(await self.config.option(path, index).value.get())
|
||||
else:
|
||||
value = await option.value.get()
|
||||
variables[await option.option.name()] = value
|
||||
if isinstance(is_service_namespace, str) and is_service_namespace + 's' in INFORMATIONS:
|
||||
await self.get_informations(is_service_namespace + 's',
|
||||
variables,
|
||||
optiondescription,
|
||||
)
|
||||
return RougailExtra(variables)
|
4
src/rougail/template/engine/__init__.py
Normal file
4
src/rougail/template/engine/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from . import none, creole, jinja2, creole_legacy
|
||||
|
||||
|
||||
__all__ = ('none', 'creole', 'jinja2', 'creole_legacy')
|
135
src/rougail/template/engine/creole.py
Normal file
135
src/rougail/template/engine/creole.py
Normal file
@ -0,0 +1,135 @@
|
||||
"""Creole engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from os.path import abspath, normpath
|
||||
from Cheetah.Template import Template
|
||||
from Cheetah.NameMapper import NotFound
|
||||
from typing import Dict, Any
|
||||
|
||||
from ...i18n import _
|
||||
from ...utils import normalize_family
|
||||
from ...error import TemplateError
|
||||
|
||||
|
||||
@classmethod
|
||||
def cl_compile(kls, *args, **kwargs):
|
||||
"""Rewrite compile methode to force some settings
|
||||
"""
|
||||
kwargs['compilerSettings'] = {'directiveStartToken' : '%',
|
||||
'cheetahVarStartToken' : '%%',
|
||||
'EOLSlurpToken' : '%',
|
||||
'commentStartToken' : '#',
|
||||
'multiLineCommentStartToken' : '#*',
|
||||
'multiLineCommentEndToken' : '*#',
|
||||
}
|
||||
return kls.old_compile(*args, **kwargs) # pylint: disable=E1101
|
||||
Template.old_compile = Template.compile
|
||||
Template.compile = cl_compile
|
||||
|
||||
|
||||
class CheetahTemplate(Template): # pylint: disable=W0223
|
||||
"""Construct a cheetah templating object
|
||||
"""
|
||||
def __init__(self,
|
||||
filename: str,
|
||||
source: str,
|
||||
context,
|
||||
eosfunc: Dict,
|
||||
extra_context: Dict,
|
||||
):
|
||||
"""Initialize Creole CheetahTemplate
|
||||
"""
|
||||
if filename is not None:
|
||||
super().__init__(file=filename,
|
||||
searchList=[context, eosfunc, extra_context],
|
||||
)
|
||||
else:
|
||||
super().__init__(source=source,
|
||||
searchList=[context, eosfunc, extra_context],
|
||||
)
|
||||
|
||||
# FORK of Cheetah function, do not replace '\\' by '/'
|
||||
def serverSidePath(self,
|
||||
path=None,
|
||||
normpath=normpath,
|
||||
abspath=abspath
|
||||
): # pylint: disable=W0621
|
||||
|
||||
# strange...
|
||||
if path is None and isinstance(self, str):
|
||||
path = self
|
||||
if path: # pylint: disable=R1705
|
||||
return normpath(abspath(path))
|
||||
# original code return normpath(abspath(path.replace("\\", '/')))
|
||||
elif hasattr(self, '_filePath') and self._filePath: # pragma: no cover
|
||||
return normpath(abspath(self._filePath))
|
||||
else: # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
# Sync to creole_legacy.py
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
destdir: str,
|
||||
variable: Any,
|
||||
rougail_variables_dict: Dict,
|
||||
eosfunc: Dict,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
try:
|
||||
extra_context = {'normalize_family': normalize_family,
|
||||
'rougail_filename': true_destfilename,
|
||||
'rougail_destination_dir': destdir,
|
||||
}
|
||||
if variable is not None:
|
||||
extra_context['rougail_variable'] = variable
|
||||
cheetah_template = CheetahTemplate(filename,
|
||||
source,
|
||||
rougail_variables_dict,
|
||||
eosfunc,
|
||||
extra_context,
|
||||
)
|
||||
data = str(cheetah_template)
|
||||
except NotFound as err: # pragma: no cover
|
||||
varname = err.args[0][13:-1]
|
||||
if filename:
|
||||
msg = f"Error: unknown variable used in template {filename} to {destfilename}: {varname}"
|
||||
else:
|
||||
msg = f"Error: unknown variable used in file {destfilename}: {varname}"
|
||||
raise TemplateError(_(msg)) from err
|
||||
except Exception as err: # pragma: no cover
|
||||
if filename:
|
||||
msg = _(f"Error while instantiating template {filename} to {destfilename}: {err}")
|
||||
else:
|
||||
msg = _(f"Error while instantiating filename {destfilename}: {err}")
|
||||
raise TemplateError(msg) from err
|
||||
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
157
src/rougail/template/engine/creole_legacy.py
Normal file
157
src/rougail/template/engine/creole_legacy.py
Normal file
@ -0,0 +1,157 @@
|
||||
"""Legacy Creole engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
|
||||
from typing import Dict, Any
|
||||
from Cheetah.NameMapper import NotFound
|
||||
|
||||
from .creole import CheetahTemplate as oriCheetahTemplate
|
||||
from ...i18n import _
|
||||
from ...utils import normalize_family
|
||||
from ...error import TemplateError
|
||||
|
||||
|
||||
@classmethod
|
||||
def cl_compile(kls, *args, **kwargs):
|
||||
"""Rewrite compile methode to force some settings
|
||||
"""
|
||||
kwargs['compilerSettings'] = {'directiveStartToken' : u'%',
|
||||
'cheetahVarStartToken' : u'%%',
|
||||
'EOLSlurpToken' : u'%',
|
||||
'PSPStartToken' : u'µ' * 10,
|
||||
'PSPEndToken' : u'µ' * 10,
|
||||
'commentStartToken' : u'µ' * 10,
|
||||
'commentEndToken' : u'µ' * 10,
|
||||
'multiLineCommentStartToken' : u'µ' * 10,
|
||||
'multiLineCommentEndToken' : u'µ' * 10}
|
||||
return kls.old_compile(*args, **kwargs) # pylint: disable=E1101
|
||||
oriCheetahTemplate.compile = cl_compile
|
||||
|
||||
|
||||
class IsDefined:
|
||||
"""
|
||||
filtre permettant de ne pas lever d'exception au cas où
|
||||
la variable Creole n'est pas définie
|
||||
"""
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def __call__(self, varname):
|
||||
if '.' in varname:
|
||||
splitted_var = varname.split('.')
|
||||
if len(splitted_var) != 2:
|
||||
msg = u"Group variables must be of type master.slave"
|
||||
raise KeyError(msg)
|
||||
master, slave = splitted_var
|
||||
if master in self.context:
|
||||
return slave in self.context[master].slave.keys()
|
||||
return False
|
||||
else:
|
||||
return varname in self.context
|
||||
|
||||
|
||||
class CreoleClient():
|
||||
def get(self, path):
|
||||
path = path.replace('/', '.')
|
||||
if path.startswith('.'):
|
||||
path = path[1:]
|
||||
if '.' not in path:
|
||||
return self.context[path]
|
||||
else:
|
||||
root, path = path.split('.', 1)
|
||||
obj = self.context[root]
|
||||
for var in path.split('.'):
|
||||
obj = getattr(obj, var)
|
||||
return obj
|
||||
|
||||
|
||||
def is_empty(data):
|
||||
if str(data) in ['', '""', "''", "[]", "['']", '[""]', "None"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class CheetahTemplate(oriCheetahTemplate):
|
||||
def __init__(self,
|
||||
filename: str,
|
||||
source: str,
|
||||
context,
|
||||
eosfunc: Dict,
|
||||
extra_context: Dict,
|
||||
):
|
||||
creole_client = CreoleClient()
|
||||
creole_client.context=context
|
||||
extra_context['is_defined'] = IsDefined(context)
|
||||
extra_context['creole_client'] = creole_client
|
||||
extra_context['is_empty'] = is_empty
|
||||
extra_context['_creole_filename'] = extra_context['rougail_filename']
|
||||
super().__init__(filename, source, context, eosfunc, extra_context)
|
||||
|
||||
|
||||
# Sync to creole.py
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
destdir: str,
|
||||
variable: Any,
|
||||
rougail_variables_dict: Dict,
|
||||
eosfunc: Dict,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
try:
|
||||
extra_context = {'normalize_family': normalize_family,
|
||||
'rougail_filename': true_destfilename,
|
||||
'rougail_destination_dir': destdir,
|
||||
}
|
||||
if variable is not None:
|
||||
extra_context['rougail_variable'] = variable
|
||||
cheetah_template = CheetahTemplate(filename,
|
||||
source,
|
||||
rougail_variables_dict,
|
||||
eosfunc,
|
||||
extra_context,
|
||||
)
|
||||
data = str(cheetah_template)
|
||||
except NotFound as err: # pragma: no cover
|
||||
varname = err.args[0][13:-1]
|
||||
if filename:
|
||||
msg = f"Error: unknown variable used in template {filename} to {destfilename}: {varname}"
|
||||
else:
|
||||
msg = f"Error: unknown variable used in file {destfilename}: {varname}"
|
||||
raise TemplateError(_(msg)) from err
|
||||
except Exception as err: # pragma: no cover
|
||||
if filename:
|
||||
msg = _(f"Error while instantiating template {filename} to {destfilename}: {err}")
|
||||
else:
|
||||
msg = _(f"Error while instantiating filename {destfilename}: {err}")
|
||||
raise TemplateError(msg) from err
|
||||
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
74
src/rougail/template/engine/jinja2.py
Normal file
74
src/rougail/template/engine/jinja2.py
Normal file
@ -0,0 +1,74 @@
|
||||
"""Jinja2 engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import Any, Dict
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from jinja2.exceptions import UndefinedError
|
||||
|
||||
from ...i18n import _
|
||||
from ...utils import normalize_family
|
||||
from ...error import TemplateError
|
||||
|
||||
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
destdir: str,
|
||||
variable: Any,
|
||||
rougail_variables_dict: Dict,
|
||||
eosfunc: Dict,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
dir_name, template_name = filename.rsplit('/', 1)
|
||||
if source is not None: # pragma: no cover
|
||||
raise TemplateError(_('source is not supported for jinja2'))
|
||||
rougail_variables_dict['rougail_variable'] = variable
|
||||
if variable is not None:
|
||||
var = {'rougail_variable': variable}
|
||||
else:
|
||||
var = {}
|
||||
try:
|
||||
# extra_context = {'normalize_family': normalize_family,
|
||||
# eosfunc
|
||||
env = Environment(loader=FileSystemLoader([dir_name, destdir]))
|
||||
template = env.get_template(template_name)
|
||||
data = template.render(**rougail_variables_dict,
|
||||
rougail_filename=true_destfilename,
|
||||
rougail_destination_dir=destdir,
|
||||
**var,
|
||||
)
|
||||
except UndefinedError as err: # pragma: no cover
|
||||
varname = err
|
||||
msg = f"Error: unknown variable used in template {filename} to {destfilename}: {varname}"
|
||||
raise TemplateError(_(msg)) from err
|
||||
|
||||
if not data.endswith('\n'):
|
||||
data = data + '\n'
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
35
src/rougail/template/engine/none.py
Normal file
35
src/rougail/template/engine/none.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""None engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import Any
|
||||
from shutil import copy
|
||||
|
||||
|
||||
def process(filename: str,
|
||||
destfilename: str,
|
||||
**kwargs
|
||||
):
|
||||
copy(filename, destfilename)
|
163
src/rougail/template/systemd.py
Normal file
163
src/rougail/template/systemd.py
Normal file
@ -0,0 +1,163 @@
|
||||
"""Template langage for Rougail to create file and systemd file
|
||||
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
from os import makedirs, symlink
|
||||
from os.path import dirname, isfile, join
|
||||
from ipaddress import ip_network
|
||||
|
||||
from .base import RougailBaseTemplate
|
||||
from ..i18n import _
|
||||
from ..error import FileNotFound
|
||||
|
||||
|
||||
ROUGAIL_IP_TEMPLATE = """[Service]
|
||||
%for %%ip in %%rougail_variable
|
||||
IPAddressAllow=%%ip
|
||||
%end for
|
||||
IPAddressDeny=any
|
||||
"""
|
||||
|
||||
|
||||
ROUGAIL_TMPL_TEMPLATE = """%def display(%%file, %%filename)
|
||||
%if %%filename.startswith('/etc/') or %%filename.startswith('/var/') or %%filename.startswith('/srv/')
|
||||
C %%filename %%file.mode %%file.owner %%file.group - /usr/local/lib%%filename
|
||||
z %%filename - - - - -
|
||||
%end if
|
||||
%end def
|
||||
%for %%service in %%services
|
||||
%if %%service.activate is True and %%hasattr(%%service, 'files')
|
||||
%for %%file in %%service.files
|
||||
%if %%file.activate is True and %%file.included != 'content'
|
||||
%if %%isinstance(%%file.name, list)
|
||||
%for %%filename in %%file.name
|
||||
%%display(%%file, %%filename)%slurp
|
||||
%end for
|
||||
%else
|
||||
%%display(%%file, %%file.name)%slurp
|
||||
%end if
|
||||
%end if
|
||||
%end for
|
||||
%end if
|
||||
%end for
|
||||
"""
|
||||
|
||||
|
||||
class RougailSystemdTemplate(RougailBaseTemplate):
|
||||
def __init__(self, # pylint: disable=R0913
|
||||
config: 'Config',
|
||||
rougailconfig: 'RougailConfig'=None,
|
||||
) -> None:
|
||||
self.ip_per_service = None
|
||||
super().__init__(config, rougailconfig)
|
||||
|
||||
def _instance_files(self,
|
||||
filevar: Dict,
|
||||
destfile: str,
|
||||
service_name: str,
|
||||
variable,
|
||||
idx: int,
|
||||
) -> tuple:
|
||||
source = filevar['source']
|
||||
if not isfile(source): # pragma: no cover
|
||||
raise FileNotFound(_(f"File {source} does not exist."))
|
||||
tmp_file = join(self.tmp_dir, source)
|
||||
#self.instance_file(fill, 'files')
|
||||
if variable:
|
||||
var = variable[idx]
|
||||
else:
|
||||
var = None
|
||||
return tmp_file, None, destfile, var
|
||||
|
||||
def _instance_overrides(self,
|
||||
filevar: Dict,
|
||||
destfile,
|
||||
service_name: str,
|
||||
*args,
|
||||
) -> tuple:
|
||||
source = filevar['source']
|
||||
if not isfile(source): # pragma: no cover
|
||||
raise FileNotFound(_(f"File {source} does not exist."))
|
||||
tmp_file = join(self.tmp_dir, source)
|
||||
service_name = filevar['name']
|
||||
return tmp_file, None, f'/systemd/system/{service_name}.service.d/rougail.conf', None
|
||||
|
||||
def _instance_ip(self,
|
||||
filevar: Dict,
|
||||
ip,
|
||||
service_name: str,
|
||||
var: Any,
|
||||
idx: int,
|
||||
*args,
|
||||
) -> tuple:
|
||||
if self.ip_per_service is None:
|
||||
self.ip_per_service = []
|
||||
if 'netmask' in filevar:
|
||||
if isinstance(filevar["netmask"], list):
|
||||
netmask = filevar['netmask'][idx]
|
||||
else:
|
||||
netmask = filevar['netmask']
|
||||
self.ip_per_service.append(str(ip_network(f'{ip}/{netmask}')))
|
||||
elif ip:
|
||||
self.ip_per_service.append(ip)
|
||||
|
||||
def desactive_service(self,
|
||||
service_name: str,
|
||||
):
|
||||
filename = f'{self.destinations_dir}/systemd/system/{service_name}.service'
|
||||
makedirs(dirname(filename), exist_ok=True)
|
||||
symlink('/dev/null', filename)
|
||||
|
||||
def post_instance_service(self,
|
||||
service_name: str,
|
||||
) -> None: # pragma: no cover
|
||||
if self.ip_per_service is None:
|
||||
return
|
||||
destfile = f'/systemd/system/{service_name}.service.d/rougail_ip.conf'
|
||||
destfilename = join(self.destinations_dir, destfile[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
self.log.info(_(f"creole processing: '{destfilename}'"))
|
||||
self.engines['creole'].process(filename=None,
|
||||
source=ROUGAIL_IP_TEMPLATE,
|
||||
true_destfilename=destfile,
|
||||
destfilename=destfilename,
|
||||
destdir=self.destinations_dir,
|
||||
variable=self.ip_per_service,
|
||||
rougail_variables_dict=self.rougail_variables_dict,
|
||||
eosfunc=self.eosfunc,
|
||||
)
|
||||
self.ip_per_service = None
|
||||
|
||||
def post_instance(self):
|
||||
destfile = '/tmpfiles.d/rougail.conf'
|
||||
destfilename = join(self.destinations_dir, destfile[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
self.log.info(_(f"creole processing: '{destfilename}'"))
|
||||
self.engines['creole'].process(filename=None,
|
||||
source=ROUGAIL_TMPL_TEMPLATE,
|
||||
true_destfilename=destfile,
|
||||
destfilename=destfilename,
|
||||
destdir=self.destinations_dir,
|
||||
variable=None,
|
||||
rougail_variables_dict=self.rougail_variables_dict,
|
||||
eosfunc=self.eosfunc,
|
||||
)
|
@ -28,7 +28,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
from json import dumps
|
||||
from os.path import isfile
|
||||
|
||||
from .config import RougailConfig
|
||||
from .annotator import CONVERT_OPTION
|
||||
from .objspace import RootRougailObject
|
||||
|
||||
@ -56,7 +55,7 @@ class TiramisuReflector:
|
||||
):
|
||||
self.index = 0
|
||||
self.text = []
|
||||
if isfile(funcs_path):
|
||||
if funcs_path and isfile(funcs_path):
|
||||
self.text.extend(["from importlib.machinery import SourceFileLoader",
|
||||
"from importlib.util import spec_from_loader, module_from_spec",
|
||||
f"loader = SourceFileLoader('func', '{funcs_path}')",
|
||||
@ -73,7 +72,7 @@ class TiramisuReflector:
|
||||
])
|
||||
self.objectspace = objectspace
|
||||
self.make_tiramisu_objects()
|
||||
if self.objectspace.has_dyn_option == True:
|
||||
if self.objectspace.has_dyn_option is True:
|
||||
self.text.append("from rougail.tiramisu import ConvertDynOptionDescription")
|
||||
|
||||
def make_tiramisu_objects(self) -> None:
|
||||
@ -96,10 +95,11 @@ class TiramisuReflector:
|
||||
because `extra` family could use `variable_namespace` variables.
|
||||
"""
|
||||
if hasattr(self.objectspace.space, 'variables'):
|
||||
if RougailConfig['variable_namespace'] in self.objectspace.space.variables:
|
||||
yield self.objectspace.space.variables[RougailConfig['variable_namespace']]
|
||||
variable_namespace = self.objectspace.rougailconfig['variable_namespace']
|
||||
if variable_namespace in self.objectspace.space.variables:
|
||||
yield self.objectspace.space.variables[variable_namespace]
|
||||
for elt, value in self.objectspace.space.variables.items():
|
||||
if elt != RougailConfig['variable_namespace']:
|
||||
if elt != self.objectspace.rougailconfig['variable_namespace']:
|
||||
yield value
|
||||
if hasattr(self.objectspace.space, 'services'):
|
||||
yield self.objectspace.space.services
|
||||
@ -219,7 +219,7 @@ class Common:
|
||||
"""Populate properties
|
||||
"""
|
||||
option_name = child.source.reflector_object.get()
|
||||
kwargs = (f"'condition': ParamOption({option_name}, todict=True), "
|
||||
kwargs = (f"'condition': ParamOption({option_name}, todict=True, notraisepropertyerror=True), "
|
||||
f"'expected': {self.populate_param(child.expected)}")
|
||||
if child.inverse:
|
||||
kwargs += ", 'reverse_condition': ParamValue(True)"
|
||||
@ -243,7 +243,7 @@ class Common:
|
||||
):
|
||||
"""Populate variable parameters
|
||||
"""
|
||||
if param.type in ['number', 'boolean', 'nil', 'string']:
|
||||
if param.type in ['number', 'boolean', 'nil', 'string', 'port']:
|
||||
value = param.text
|
||||
if param.type == 'string' and value is not None:
|
||||
value = self.convert_str(value)
|
||||
@ -251,9 +251,17 @@ class Common:
|
||||
if param.type == 'variable':
|
||||
return self.build_option_param(param)
|
||||
if param.type == 'information':
|
||||
return f'ParamInformation("{param.text}", None)'
|
||||
if hasattr(self.elt, 'multi') and self.elt.multi:
|
||||
default = []
|
||||
else:
|
||||
default = None
|
||||
return f'ParamInformation("{param.text}", {default})'
|
||||
if param.type == 'target_information':
|
||||
return f'ParamSelfInformation("{param.text}", None)'
|
||||
if param.type == 'suffix':
|
||||
return 'ParamSuffix()'
|
||||
if param.type == 'index':
|
||||
return 'ParamIndex()'
|
||||
raise Exception(f'unknown type {param.type}') # pragma: no cover
|
||||
|
||||
@staticmethod
|
||||
@ -265,7 +273,8 @@ class Common:
|
||||
params = [f'{option_name}']
|
||||
if hasattr(param, 'suffix'):
|
||||
param_type = 'ParamDynOption'
|
||||
params.extend([f"'{param.suffix}'", f'{param.family.reflector_name}'])
|
||||
family = param.family.reflector_object.get()
|
||||
params.extend([f"'{param.suffix}'", f'{family}'])
|
||||
else:
|
||||
param_type = 'ParamOption'
|
||||
if not param.propertyerror:
|
||||
@ -291,9 +300,11 @@ class Variable(Common):
|
||||
keys['opt'] = self.elt.opt.reflector_object.get()
|
||||
if hasattr(self.elt, 'values'):
|
||||
values = self.elt.values
|
||||
if values[0].type == 'calculation':
|
||||
if values[0].type == 'variable':
|
||||
value = values[0].name.reflector_object.get()
|
||||
keys['values'] = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
|
||||
elif values[0].type == 'function':
|
||||
keys['values'] = self.calculation_value(self.elt.values[0], [])
|
||||
else:
|
||||
keys['values'] = str(tuple([val.name for val in values]))
|
||||
if hasattr(self.elt, 'multi') and self.elt.multi:
|
||||
@ -304,11 +315,11 @@ class Variable(Common):
|
||||
if isinstance(value, str):
|
||||
value = self.convert_str(value)
|
||||
elif isinstance(value, self.objectspace.value):
|
||||
value = self.calculation_value(value, [])
|
||||
value = self.calculation_value(value, [], calc_multi=value.calc_multi)
|
||||
keys[key] = value
|
||||
if hasattr(self.elt, 'validators'):
|
||||
keys['validators'] = '[' + ', '.join([self.calculation_value(val,
|
||||
['ParamSelfOption()']) for val in self.elt.validators]) + ']'
|
||||
['ParamSelfOption(whole=False)']) for val in self.elt.validators]) + ']'
|
||||
for key in ['min_number', 'max_number']:
|
||||
if hasattr(self.elt, key):
|
||||
keys[key] = getattr(self.elt, key)
|
||||
@ -320,26 +331,36 @@ class Variable(Common):
|
||||
def calculation_value(self,
|
||||
child,
|
||||
args,
|
||||
calc_multi=False,
|
||||
) -> str:
|
||||
"""Generate calculated value
|
||||
"""
|
||||
kwargs = []
|
||||
# has parameters
|
||||
function = child.name
|
||||
new_args = []
|
||||
if hasattr(child, 'param'):
|
||||
for param in child.param:
|
||||
value = self.populate_param(param)
|
||||
if not hasattr(param, 'name'):
|
||||
args.append(str(value))
|
||||
new_args.append(str(value))
|
||||
else:
|
||||
kwargs.append(f"'{param.name}': " + value)
|
||||
ret = f'Calculation(func.{function}, Params((' + ', '.join(args) + ')'
|
||||
if function == 'valid_network_netmask':
|
||||
new_args.extend(args)
|
||||
else:
|
||||
args.extend(new_args)
|
||||
new_args = args
|
||||
ret = f'Calculation(func.{function}, Params((' + ', '.join(new_args) + ')'
|
||||
if kwargs:
|
||||
ret += ', kwargs={' + ', '.join(kwargs) + '}'
|
||||
ret += ')'
|
||||
if hasattr(child, 'warnings_only'):
|
||||
ret += f', warnings_only={child.warnings_only}'
|
||||
return ret + ')'
|
||||
ret = ret + ')'
|
||||
if calc_multi:
|
||||
ret = '[' + ret + ']'
|
||||
return ret
|
||||
|
||||
|
||||
class Family(Common):
|
||||
@ -354,7 +375,7 @@ class Family(Common):
|
||||
if hasattr(self.elt, 'suffixes'):
|
||||
self.objectspace.has_dyn_option = True
|
||||
self.object_type = 'ConvertDynOptionDescription'
|
||||
elif isinstance(self.elt, self.objectspace.leadership):
|
||||
elif hasattr(self.elt, 'leadership') and self.elt.leadership:
|
||||
self.object_type = 'Leadership'
|
||||
else:
|
||||
self.object_type = 'OptionDescription'
|
||||
@ -370,5 +391,5 @@ class Family(Common):
|
||||
) -> None:
|
||||
if hasattr(self.elt, 'suffixes'):
|
||||
dyn = self.elt.suffixes.reflector_object.get()
|
||||
keys['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}))))"
|
||||
keys['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}, notraisepropertyerror=True))))"
|
||||
keys['children'] = '[' + ', '.join([child.get() for child in self.children]) + ']'
|
||||
|
669
src/rougail/update.py
Normal file
669
src/rougail/update.py
Normal file
@ -0,0 +1,669 @@
|
||||
"""Update Rougail XML file to new version
|
||||
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from os.path import join, isfile, basename
|
||||
from os import listdir
|
||||
from lxml.etree import DTD, parse, XMLParser, XMLSyntaxError # pylint: disable=E0611
|
||||
from lxml.etree import Element, SubElement, tostring
|
||||
from ast import parse as ast_parse
|
||||
|
||||
from .i18n import _
|
||||
from .error import UpgradeError
|
||||
|
||||
from .utils import normalize_family
|
||||
from .config import RougailConfig
|
||||
|
||||
|
||||
VERSIONS = {'creole': ['1'],
|
||||
'rougail': ['0.9', '0.10'],
|
||||
}
|
||||
|
||||
|
||||
def get_function_name(root, version):
|
||||
version = version.replace('.', '_')
|
||||
return f'update_{root}_{version}'
|
||||
|
||||
|
||||
FUNCTION_VERSIONS = [(root, version, get_function_name(root, version)) for root, versions in VERSIONS.items() for version in versions]
|
||||
|
||||
|
||||
class RougailUpgrade:
|
||||
def __init__(self,
|
||||
test=False,
|
||||
upgrade_help=None,
|
||||
rougailconfig: RougailConfig=None,
|
||||
) -> None:
|
||||
self.test = test
|
||||
if upgrade_help is None:
|
||||
upgrade_help = {}
|
||||
self.upgrade_help = upgrade_help
|
||||
if rougailconfig is None:
|
||||
rougailconfig = RougailConfig
|
||||
self.rougailconfig = rougailconfig
|
||||
|
||||
def load_xml_from_folders(self,
|
||||
srcfolder: str,
|
||||
dstfolder: str,
|
||||
namespace: str,
|
||||
display: bool=True,
|
||||
):
|
||||
"""Loads all the XML files located in the xmlfolders' list
|
||||
|
||||
:param xmlfolders: list of full folder's name
|
||||
"""
|
||||
filenames = [filename for filename in listdir(srcfolder) if filename.endswith('.xml')]
|
||||
filenames.sort()
|
||||
for filename in filenames:
|
||||
xmlsrc = join(srcfolder, filename)
|
||||
xmldst = join(dstfolder, filename)
|
||||
if isfile(xmldst):
|
||||
raise Exception(f'cannot update "{xmlsrc}" destination file "{xmldst}" already exists')
|
||||
try:
|
||||
parser = XMLParser(remove_blank_text=True)
|
||||
document = parse(xmlsrc, parser)
|
||||
except XMLSyntaxError as err:
|
||||
raise Exception(_(f'not a XML file: {err}')) from err
|
||||
root = document.getroot()
|
||||
search_function_name = get_function_name(root.tag, root.attrib.get('version', '1'))
|
||||
function_found = False
|
||||
for root_name, version, function_version in FUNCTION_VERSIONS:
|
||||
if function_found and hasattr(self, function_version):
|
||||
if display:
|
||||
print(f' - convert {filename} to version {version}')
|
||||
upgrade_help = self.upgrade_help.get(function_version, {}).get(filename, {})
|
||||
if upgrade_help.get('remove') is True:
|
||||
continue
|
||||
root = getattr(self, function_version)(root, upgrade_help, namespace)
|
||||
if function_version == search_function_name:
|
||||
function_found = True
|
||||
root.attrib['version'] = version
|
||||
with open(xmldst, 'wb') as xmlfh:
|
||||
xmlfh.write(tostring(root, pretty_print=True, encoding="UTF-8", xml_declaration=True))
|
||||
# if
|
||||
# if not self.dtd.validate(document):
|
||||
# dtd_error = self.dtd.error_log.filter_from_errors()[0]
|
||||
# msg = _(f'not a valid XML file: {dtd_error}')
|
||||
# raise DictConsistencyError(msg, 43, [xmlfile])
|
||||
# yield xmlfile, document.getroot()
|
||||
|
||||
|
||||
def update_rougail_0_10(self,
|
||||
root: 'Element',
|
||||
upgrade_help: dict,
|
||||
namespace: str,
|
||||
) -> 'Element':
|
||||
variables = root.find('variables')
|
||||
if variables is None:
|
||||
return root
|
||||
constraints = root.find('constraints')
|
||||
if constraints is None:
|
||||
return root
|
||||
groups = []
|
||||
for constraint in constraints:
|
||||
if constraint.tag == 'group':
|
||||
constraints.remove(constraint)
|
||||
groups.append(constraint)
|
||||
if not groups:
|
||||
return root
|
||||
paths = self._get_path_variables(variables,
|
||||
namespace == self.rougailconfig['variable_namespace'],
|
||||
namespace,
|
||||
)
|
||||
for group in groups:
|
||||
if group.attrib['leader'] in paths:
|
||||
leader_obj = paths[group.attrib['leader']]
|
||||
#FIXME name peut avoir "." il faut le virer
|
||||
#FIXME si extra c'est un follower !
|
||||
if 'name' in group.attrib:
|
||||
name = group.attrib['name']
|
||||
if 'description' in group.attrib:
|
||||
description = group.attrib['description']
|
||||
else:
|
||||
description = name
|
||||
else:
|
||||
name = leader_obj['variable'].attrib['name']
|
||||
if '.' in name:
|
||||
name = name.rsplit('.', 1)[-1]
|
||||
if 'description' in group.attrib:
|
||||
description = group.attrib['description']
|
||||
elif 'description' in leader_obj['variable'].attrib:
|
||||
description = leader_obj['variable'].attrib['description']
|
||||
else:
|
||||
description = name
|
||||
family = SubElement(leader_obj['parent'], 'family', name=name, description=description, leadership="True")
|
||||
leader_obj['parent'].remove(leader_obj['variable'])
|
||||
family.append(leader_obj['variable'])
|
||||
else:
|
||||
# append in group
|
||||
follower = next(iter(group))
|
||||
leader_name = group.attrib['leader']
|
||||
if '.' in leader_name:
|
||||
leader_path = leader_name.rsplit('.', 1)[0]
|
||||
follower_path = leader_path + '.' + follower.text
|
||||
else:
|
||||
follower_path = follower.text
|
||||
obj = paths[follower_path]
|
||||
family = SubElement(obj['parent'], 'family', name=leader_name, leadership="True")
|
||||
for follower in group:
|
||||
leader_name = group.attrib['leader']
|
||||
if '.' in leader_name:
|
||||
leader_path = leader_name.rsplit('.', 1)[0]
|
||||
follower_path = leader_path + '.' + follower.text
|
||||
else:
|
||||
follower_path = follower.text
|
||||
follower_obj = paths[follower_path]
|
||||
follower_obj['parent'].remove(follower_obj['variable'])
|
||||
family.append(follower_obj['variable'])
|
||||
return root
|
||||
|
||||
def _get_path_variables(self, variables, is_variable_namespace, path, dico=None):
|
||||
if dico is None:
|
||||
dico = {}
|
||||
for variable in variables:
|
||||
if not is_variable_namespace and path:
|
||||
subpath = path + '.'
|
||||
else:
|
||||
subpath = ''
|
||||
subpath += variable.attrib['name']
|
||||
if variable.tag == 'family':
|
||||
self._get_path_variables(variable, is_variable_namespace, subpath, dico)
|
||||
elif variable.tag == 'variable':
|
||||
dico[subpath] = {'variable': variable, 'parent': variables}
|
||||
return dico
|
||||
|
||||
def update_rougail_0_9(self,
|
||||
root: 'Element',
|
||||
upgrade_help: dict,
|
||||
namespace: str,
|
||||
) -> 'Element':
|
||||
# rename root
|
||||
root.tag = 'rougail'
|
||||
variables_auto_valid_enum = {}
|
||||
variables_help = {}
|
||||
families_help = {}
|
||||
variables = {}
|
||||
families = {}
|
||||
valid_enums = {}
|
||||
current_service = upgrade_help.get('services', {}).get('default', 'unknown')
|
||||
files = {current_service: {}}
|
||||
ips = {current_service: []}
|
||||
servicelists = {}
|
||||
test_unknowns_variables = set()
|
||||
autos = []
|
||||
constraints_obj = None
|
||||
for subelement in root:
|
||||
if not isinstance(subelement.tag, str):
|
||||
# XML comment
|
||||
continue
|
||||
if subelement.tag == 'family_action':
|
||||
root.remove(subelement)
|
||||
continue
|
||||
for subsubelement in subelement:
|
||||
if not isinstance(subsubelement.tag, str):
|
||||
# XML comment
|
||||
continue
|
||||
for subsubsubelement in subsubelement:
|
||||
if not isinstance(subsubsubelement.tag, str):
|
||||
# XML comment
|
||||
continue
|
||||
if subsubsubelement.tag == 'variable':
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subsubelement.remove(subsubsubelement)
|
||||
continue
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('type', {}):
|
||||
subsubsubelement.attrib['type'] = upgrade_help['variables']['type'][subsubsubelement.attrib['name']]
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('hidden', {}).get('add', []):
|
||||
subsubsubelement.attrib['hidden'] = 'True'
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('hidden', {}).get('remove', []):
|
||||
self.remove(subsubsubelement, 'hidden', optional=True)
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('mandatory', {}).get('remove', []):
|
||||
self.remove(subsubsubelement, 'mandatory')
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('mandatory', {}).get('add', []):
|
||||
subsubsubelement.attrib['mandatory'] = 'True'
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('type', {}):
|
||||
subsubsubelement.attrib['type'] = upgrade_help.get('variables', {}).get('type', {})[subsubsubelement.attrib['name']]
|
||||
if namespace == self.rougailconfig['variable_namespace']:
|
||||
path = subsubsubelement.attrib['name']
|
||||
npath = normalize_family(subsubsubelement.attrib['name'])
|
||||
else:
|
||||
path = namespace + '.' + subsubelement.attrib['name'] + '.' + subsubsubelement.attrib['name']
|
||||
npath = normalize_family(namespace) + '.' + normalize_family(subsubelement.attrib['name']) + '.' + normalize_family(subsubsubelement.attrib['name'])
|
||||
variables[path] = subsubsubelement
|
||||
variables[npath] = subsubsubelement
|
||||
type = subsubsubelement.attrib.get('type')
|
||||
if type in ['oui/non', 'yes/no', 'on/off']:
|
||||
variables_auto_valid_enum.setdefault(subsubsubelement.attrib['type'], []).append(path)
|
||||
del subsubsubelement.attrib['type']
|
||||
elif type == 'hostname_strict':
|
||||
subsubsubelement.attrib['type'] = 'hostname'
|
||||
elif type in ['domain', 'domain_strict']:
|
||||
subsubsubelement.attrib['type'] = 'domainname'
|
||||
elif type == 'string':
|
||||
del subsubsubelement.attrib['type']
|
||||
if self.test and subsubsubelement.attrib.get('auto_freeze') == 'True':
|
||||
del subsubsubelement.attrib['auto_freeze']
|
||||
#if self.test and subsubsubelement.attrib.get('redefine') == 'True':
|
||||
# subsubsubelement.attrib['exists'] = 'False'
|
||||
# del subsubsubelement.attrib['redefine']
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('redefine', {}).get('remove', {}):
|
||||
del subsubsubelement.attrib['redefine']
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('remove_check', []):
|
||||
subsubsubelement.attrib['remove_check'] = 'True'
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('mode', {}).get('modify', {}):
|
||||
subsubsubelement.attrib['mode'] = upgrade_help.get('variables', {}).get('mode', {}).get('modify', {})[subsubsubelement.attrib['name']]
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('type', {}).get('modify', {}):
|
||||
subsubsubelement.attrib['type'] = upgrade_help.get('variables', {}).get('type', {}).get('modify', {})[subsubsubelement.attrib['name']]
|
||||
type = subsubsubelement.attrib['type']
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('test', {}):
|
||||
subsubsubelement.attrib['test'] = upgrade_help.get('variables', {}).get('test', {})[subsubsubelement.attrib['name']]
|
||||
if subsubsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('remove_value', []):
|
||||
for value in subsubsubelement:
|
||||
subsubsubelement.remove(value)
|
||||
if subsubsubelement.attrib['name'] != normalize_family(subsubsubelement.attrib['name']):
|
||||
if "description" not in subsubsubelement.attrib:
|
||||
subsubsubelement.attrib['description'] = subsubsubelement.attrib['name']
|
||||
subsubsubelement.attrib['name'] = normalize_family(subsubsubelement.attrib['name'])
|
||||
elif subsubsubelement.tag == 'param':
|
||||
if subsubelement.tag == 'check' and subsubelement.attrib['target'] in upgrade_help.get('check', {}).get('remove', []):
|
||||
continue
|
||||
type = subsubsubelement.attrib.get('type')
|
||||
if type == 'eole':
|
||||
subsubsubelement.attrib['type'] = 'variable'
|
||||
type = 'variable'
|
||||
if type == 'python':
|
||||
subsubsubelement.attrib['type'] = 'function'
|
||||
if subsubsubelement.text.startswith('range('):
|
||||
func_ast = ast_parse(subsubsubelement.text)
|
||||
subsubsubelement.text = 'range'
|
||||
for arg in func_ast.body[0].value.args:
|
||||
SubElement(subsubelement, 'param', type='number').text = str(arg.value)
|
||||
else:
|
||||
raise Exception(f'{subsubsubelement.text} is not a supported function')
|
||||
type = 'function'
|
||||
elif type in ('container', 'context'):
|
||||
raise UpgradeError(_(f'cannot convert param with type "{type}"'))
|
||||
if subsubelement.attrib['name'] == 'valid_entier' and not 'type' in subsubsubelement.attrib:
|
||||
subsubsubelement.attrib['type'] = 'number'
|
||||
if (not type or type == 'variable') and subsubsubelement.text in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subsubelement.remove(subsubsubelement)
|
||||
continue
|
||||
if subsubelement.attrib['name'] == 'valid_enum' and not type:
|
||||
if subsubsubelement.attrib.get('name') == 'checkval':
|
||||
if subsubelement.attrib['target'] in upgrade_help.get('check', {}).get('valid_enums', {}).get('checkval', {}).get('remove', []):
|
||||
subsubelement.remove(subsubsubelement)
|
||||
continue
|
||||
raise UpgradeError(_('checkval in valid_enum is no more supported'))
|
||||
if subsubsubelement.text.startswith('['):
|
||||
for val in eval(subsubsubelement.text):
|
||||
SubElement(subsubelement, 'param').text = str(val)
|
||||
subsubelement.remove(subsubsubelement)
|
||||
self.move(subsubsubelement, 'hidden', 'propertyerror', optional=True)
|
||||
if type == 'variable' and subsubsubelement.text in upgrade_help.get('variables', {}).get('rename', []):
|
||||
subsubsubelement.text = upgrade_help.get('variables', {}).get('rename', [])[subsubsubelement.text]
|
||||
up = upgrade_help.get('params', {}).get(subsubsubelement.text)
|
||||
if up:
|
||||
if 'text' in up:
|
||||
subsubsubelement.text = up['text']
|
||||
if 'type' in up:
|
||||
subsubsubelement.attrib['type'] = up['type']
|
||||
elif subsubsubelement.tag == 'target':
|
||||
type = subsubsubelement.attrib.get('type')
|
||||
if type in ['service_accesslist', 'service_restrictionlist', 'interfacelist', 'hostlist']:
|
||||
subsubelement.remove(subsubsubelement)
|
||||
elif (not type or type == 'variable') and subsubsubelement.text in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subsubelement.remove(subsubsubelement)
|
||||
elif type == 'family' and subsubsubelement.text in upgrade_help.get('families', {}).get('remove', []):
|
||||
subsubelement.remove(subsubsubelement)
|
||||
elif type == 'actionlist':
|
||||
# for family in root.find('variables'):
|
||||
# family_list = SubElement(subsubelement, 'target')
|
||||
# family_list.attrib['type'] = 'family'
|
||||
# family_list.text = namespace + '.' + family.attrib['name']
|
||||
subsubelement.remove(subsubsubelement)
|
||||
if upgrade_help.get('targets', {}).get(subsubsubelement.text, {}).get('optional'):
|
||||
subsubsubelement.attrib['optional'] = upgrade_help.get('targets', {}).get(subsubsubelement.text, {}).get('optional')
|
||||
has_target = False
|
||||
for target in subsubelement:
|
||||
if target.tag == 'target':
|
||||
has_target = True
|
||||
break
|
||||
if not has_target:
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
elif subsubsubelement.tag == 'slave':
|
||||
if subsubsubelement.text in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subsubelement.remove(subsubsubelement)
|
||||
continue
|
||||
subsubsubelement.tag = 'follower'
|
||||
subsubsubelement.text = normalize_family(subsubsubelement.text)
|
||||
if subelement.tag == 'containers':
|
||||
current_service = self.upgrade_container(subsubsubelement, current_service, files, ips, servicelists, upgrade_help)
|
||||
subsubelement.remove(subsubsubelement)
|
||||
if subelement.tag == 'help':
|
||||
if subsubelement.tag == 'variable':
|
||||
if not subsubelement.attrib['name'] in upgrade_help.get('variables', {}).get('remove', []):
|
||||
variables_help[subsubelement.attrib['name']] = subsubelement.text
|
||||
elif subsubelement.tag == 'family':
|
||||
if subsubelement.attrib['name'] not in upgrade_help.get('families', {}).get('remove', []):
|
||||
families_help[subsubelement.attrib['name']] = subsubelement.text
|
||||
else:
|
||||
raise Exception(f'unknown help tag {subsubelement.tag}')
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
if subsubelement.tag == 'auto':
|
||||
if subsubelement.attrib['target'] in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
autos.append(subsubelement.attrib['target'])
|
||||
subsubelement.tag = 'fill'
|
||||
if subsubelement.tag in ['fill', 'check']:
|
||||
if subsubelement.tag == 'check' and subsubelement.attrib['name'] == 'valid_enum':
|
||||
if subsubelement.attrib['target'] in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
params = []
|
||||
for param in subsubelement:
|
||||
if param.tag == 'param':
|
||||
params.append(param.text)
|
||||
key = '~'.join(params)
|
||||
if key in valid_enums:
|
||||
SubElement(valid_enums[key], 'target').text = subsubelement.attrib['target']
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
valid_enums['~'.join(params)] = subsubelement
|
||||
if subsubelement.tag == 'fill' and subsubelement.attrib['target'] in upgrade_help.get('fills', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
if subsubelement.tag == 'check' and subsubelement.attrib['target'] in upgrade_help.get('check', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
if subsubelement.attrib['target'] in upgrade_help.get('variables', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
if subsubelement.attrib['name'] == 'valid_networknetmask':
|
||||
subsubelement.attrib['name'] = 'valid_network_netmask'
|
||||
target = SubElement(subsubelement, 'target')
|
||||
target.text = subsubelement.attrib['target']
|
||||
if self.test:
|
||||
target.attrib['optional'] = 'True'
|
||||
del subsubelement.attrib['target']
|
||||
elif subsubelement.tag == 'separators':
|
||||
subelement.remove(subsubelement)
|
||||
elif subsubelement.tag == 'condition':
|
||||
if subsubelement.attrib['source'] in upgrade_help.get('variables', {}).get('remove', []):
|
||||
try:
|
||||
subelement.remove(subsubelement)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
if subsubelement.attrib['name'] == 'hidden_if_not_in':
|
||||
subsubelement.attrib['name'] = 'disabled_if_not_in'
|
||||
if subsubelement.attrib['name'] == 'hidden_if_in':
|
||||
subsubelement.attrib['name'] = 'disabled_if_in'
|
||||
if subsubelement.attrib['name'] == 'frozen_if_in':
|
||||
subsubelement.attrib['name'] = 'hidden_if_in'
|
||||
self.move(subsubelement, 'fallback', 'optional', optional='True')
|
||||
if subsubelement.attrib['source'] in upgrade_help.get('variables', {}).get('rename', []):
|
||||
subsubelement.attrib['source'] = upgrade_help.get('variables', {}).get('rename', [])[subsubelement.attrib['source']]
|
||||
elif subsubelement.tag == 'group':
|
||||
if subsubelement.attrib['master'] in upgrade_help.get('groups', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
else:
|
||||
self.move(subsubelement, 'master', 'leader')
|
||||
for follower in subsubelement:
|
||||
if '.' in subsubelement.attrib['leader']:
|
||||
path = subsubelement.attrib['leader'].rsplit('.', 1)[0] + '.' + follower.text
|
||||
else:
|
||||
path = follower.text
|
||||
self.remove(variables[path], 'multi', optional=True)
|
||||
if subsubelement.attrib['leader'] in upgrade_help.get('groups', {}).get('reorder', {}):
|
||||
for follower in subsubelement:
|
||||
subsubelement.remove(follower)
|
||||
for follower in upgrade_help.get('groups', {}).get('reorder', {})[subsubelement.attrib['leader']]:
|
||||
SubElement(subsubelement, 'follower').text = follower
|
||||
if subelement.tag == 'files':
|
||||
current_service = self.upgrade_container(subsubelement, current_service, files, ips, servicelists, upgrade_help)
|
||||
subelement.remove(subsubelement)
|
||||
if subsubelement.tag == 'family':
|
||||
self.remove(subsubelement, 'icon', optional=True)
|
||||
if subsubelement.attrib['name'] in upgrade_help.get('families', {}).get('mode', {}).get('remove', []) and 'mode' in subsubelement.attrib:
|
||||
del subsubelement.attrib['mode']
|
||||
if subsubelement.attrib['name'] in upgrade_help.get('families', {}).get('rename', {}):
|
||||
subsubelement.attrib['name'] = upgrade_help.get('families', {}).get('rename', {})[subsubelement.attrib['name']]
|
||||
if subsubelement.attrib['name'] in upgrade_help.get('families', {}).get('remove', []):
|
||||
subelement.remove(subsubelement)
|
||||
continue
|
||||
if subsubelement.attrib['name'] in upgrade_help.get('families', {}).get('hidden', {}).get('add', []):
|
||||
subsubelement.attrib['hidden'] = 'True'
|
||||
if subsubelement.attrib['name'] != normalize_family(subsubelement.attrib['name']):
|
||||
if "description" not in subsubelement.attrib:
|
||||
subsubelement.attrib['description'] = subsubelement.attrib['name']
|
||||
subsubelement.attrib['name'] = normalize_family(subsubelement.attrib['name'])
|
||||
families[subsubelement.attrib['name']] = subsubelement
|
||||
# if empty, remove
|
||||
if is_empty(subelement) or subelement.tag in ['containers', 'files', 'help']:
|
||||
root.remove(subelement)
|
||||
continue
|
||||
# copy subelement
|
||||
if subelement.tag == 'constraints':
|
||||
constraints_obj = subelement
|
||||
services = None
|
||||
service_elt = {}
|
||||
for variable, obj in upgrade_help.get('variables', {}).get('add', {}).items():
|
||||
family = next(iter(families.values()))
|
||||
variables[variable] = SubElement(family, 'variable', name=variable)
|
||||
if 'value' in obj:
|
||||
SubElement(variables[variable], 'value').text = obj['value']
|
||||
for name in ['hidden', 'exists']:
|
||||
if name in obj:
|
||||
variables[variable].attrib[name] = obj[name]
|
||||
files_is_empty = True
|
||||
for lst in files.values():
|
||||
if len(lst):
|
||||
files_is_empty = False
|
||||
break
|
||||
if not files_is_empty:
|
||||
services = Element('services')
|
||||
root.insert(0, services)
|
||||
for service_name, lst in files.items():
|
||||
service = self.create_service(services, service_name, service_elt, servicelists, upgrade_help)
|
||||
for file_ in lst.values():
|
||||
self.move(file_, 'name_type', 'file_type', optional=True)
|
||||
if 'file_type' in file_.attrib and file_.attrib['file_type'] == 'SymLinkOption':
|
||||
file_.attrib['file_type'] = 'variable'
|
||||
if variables[file_.text].attrib['type'] == 'string':
|
||||
variables[file_.text].attrib['type'] = 'filename'
|
||||
self.remove(file_, 'rm', optional=True)
|
||||
self.remove(file_, 'mkdir', optional=True)
|
||||
service.append(file_)
|
||||
ips_is_empty = True
|
||||
for lst in ips.values():
|
||||
if len(lst):
|
||||
ips_is_empty = False
|
||||
break
|
||||
if not ips_is_empty:
|
||||
if services is None:
|
||||
services = Element('services')
|
||||
root.insert(0, services)
|
||||
for service_name, lst in ips.items():
|
||||
service = self.create_service(services, service_name, service_elt, servicelists, upgrade_help)
|
||||
for ip in lst:
|
||||
if ip.text in upgrade_help.get('ips', {}).get('remove', []):
|
||||
continue
|
||||
for type in ['ip_type', 'netmask_type']:
|
||||
if type in ip.attrib and ip.attrib[type] == 'SymLinkOption':
|
||||
ip.attrib[type] = 'variable'
|
||||
if 'netmask' in ip.attrib:
|
||||
if ip.attrib['netmask'] == '255.255.255.255':
|
||||
del ip.attrib['netmask']
|
||||
if ip.text in variables and 'type' not in variables[ip.text].attrib:
|
||||
variables[ip.text].attrib['type'] = 'ip'
|
||||
else:
|
||||
if ip.text in variables and 'type' not in variables[ip.text].attrib:
|
||||
variables[ip.text].attrib['type'] = 'network'
|
||||
if ip.attrib['netmask'] in variables and 'type' not in variables[ip.attrib['netmask']].attrib:
|
||||
variables[ip.attrib['netmask']].attrib['type'] = 'netmask'
|
||||
elif ip.text in variables and 'type' not in variables[ip.text].attrib:
|
||||
variables[ip.text].attrib['type'] = 'ip'
|
||||
self.remove(ip, 'interface')
|
||||
self.remove(ip, 'interface_type', optional=True)
|
||||
self.remove(ip, 'service_restrictionlist', optional=True)
|
||||
service.append(ip)
|
||||
if ips_is_empty and files_is_empty:
|
||||
for service_name in files:
|
||||
if services is None:
|
||||
services = Element('services')
|
||||
root.insert(0, services)
|
||||
if service_name != 'unknown':
|
||||
self.create_service(services, service_name, service_elt, servicelists, upgrade_help)
|
||||
if constraints_obj is None:
|
||||
constraints_obj = SubElement(root, 'constraints')
|
||||
if variables_auto_valid_enum:
|
||||
for type, variables_valid_enum in variables_auto_valid_enum.items():
|
||||
valid_enum = SubElement(constraints_obj, 'check')
|
||||
valid_enum.attrib['name'] = 'valid_enum'
|
||||
for val in type.split('/'):
|
||||
param = SubElement(valid_enum, 'param')
|
||||
param.text = val
|
||||
for variable in variables_valid_enum:
|
||||
target = SubElement(valid_enum, 'target')
|
||||
target.text = variable
|
||||
for name, text in variables_help.items():
|
||||
variables[name].attrib['help'] = text
|
||||
for name, text in families_help.items():
|
||||
if name in upgrade_help.get('families', {}).get('rename', {}):
|
||||
name = upgrade_help.get('families', {}).get('rename', {})[name]
|
||||
families[normalize_family(name)].attrib['help'] = text
|
||||
for auto in autos:
|
||||
if auto in variables:
|
||||
variables[auto].attrib['hidden'] = 'True'
|
||||
else:
|
||||
# redefine value in first family
|
||||
family = next(iter(families.values()))
|
||||
variable = SubElement(family, 'variable', name=auto, redefine="True", hidden="True")
|
||||
if self.test:
|
||||
del variable.attrib['redefine']
|
||||
return root
|
||||
|
||||
@staticmethod
|
||||
def move(elt, src, dst, optional=False):
|
||||
if src == 'text':
|
||||
value = elt.text
|
||||
elt.text = None
|
||||
else:
|
||||
if optional and src not in elt.attrib:
|
||||
return
|
||||
value = elt.attrib[src]
|
||||
del elt.attrib[src]
|
||||
#
|
||||
if dst == 'text':
|
||||
elt.text = value
|
||||
else:
|
||||
elt.attrib[dst] = value
|
||||
|
||||
@staticmethod
|
||||
def remove(elt, src, optional=False):
|
||||
if optional and src not in elt.attrib:
|
||||
return
|
||||
del elt.attrib[src]
|
||||
|
||||
@staticmethod
|
||||
def create_service(services, service_name, service_elt, servicelists, upgrade_help):
|
||||
if service_name in service_elt:
|
||||
return service_elt[service_name]
|
||||
service = SubElement(services, 'service')
|
||||
service.attrib['name'] = service_name
|
||||
if service_name == 'unknown':
|
||||
service.attrib['manage'] = 'False'
|
||||
if service_name in upgrade_help.get('services', {}).get('unmanage', []):
|
||||
service.attrib['manage'] = 'False'
|
||||
service_elt[service_name] = service
|
||||
if upgrade_help.get('servicelists', {}).get(service_name):
|
||||
service.attrib['servicelist'] = upgrade_help.get('servicelists', {}).get(service_name)
|
||||
elif service_name in servicelists:
|
||||
service.attrib['servicelist'] = servicelists[service_name]
|
||||
return service
|
||||
|
||||
def upgrade_container(self, elt, current_service, files, ips, servicelists, upgrade_help):
|
||||
if elt.tag == 'file':
|
||||
self.move(elt, 'name', 'text')
|
||||
self.remove(elt, 'del_comment', optional=True)
|
||||
elt.attrib['engine'] = 'creole_legacy'
|
||||
if (not 'instance_mode' in elt.attrib or elt.attrib['instance_mode'] != 'when_container') and \
|
||||
elt.text not in upgrade_help.get('files', {}).get('remove', {}):
|
||||
if elt.attrib.get('filelist') in upgrade_help.get('services', {}).get('filelist_service', {}):
|
||||
elt_service = upgrade_help.get('services', {}).get('filelist_service', {})[elt.attrib['filelist']]
|
||||
if elt_service in files:
|
||||
service = elt_service
|
||||
else:
|
||||
service = current_service
|
||||
else:
|
||||
service = current_service
|
||||
files[service][elt.text] = elt
|
||||
elif elt.tag in ['host', 'disknod', 'fstab', 'interface', 'package', 'service_access']:
|
||||
pass
|
||||
elif elt.tag == 'service_restriction':
|
||||
for restriction in elt:
|
||||
if restriction.tag == 'ip' and restriction.text != '0.0.0.0':
|
||||
self.remove(restriction, 'ip_type', optional=True)
|
||||
self.remove(restriction, 'netmask_type', optional=True)
|
||||
if elt.attrib['service'] in upgrade_help.get('services', {}).get('rename', {}):
|
||||
elt_service = upgrade_help.get('services', {}).get('rename', {})[elt.attrib['service']]
|
||||
else:
|
||||
elt_service = elt.attrib['service']
|
||||
if elt_service in ips:
|
||||
service = elt_service
|
||||
else:
|
||||
service = current_service
|
||||
ips[service].append(restriction)
|
||||
elif elt.tag == 'service':
|
||||
new_name = elt.text
|
||||
if current_service == 'unknown':
|
||||
if new_name in files:
|
||||
raise Exception('hu?')
|
||||
files[new_name] = files[current_service]
|
||||
del files[current_service]
|
||||
ips[new_name] = ips[current_service]
|
||||
del ips[current_service]
|
||||
elif new_name not in files:
|
||||
files[new_name] = {}
|
||||
ips[new_name] = []
|
||||
current_service = new_name
|
||||
if 'servicelist' in elt.attrib:
|
||||
servicelists[current_service] = elt.attrib['servicelist']
|
||||
else:
|
||||
raise Exception(f'unknown containers tag {elt.tag}')
|
||||
return current_service
|
||||
|
||||
|
||||
def is_empty(elt, not_check_attrib=False):
|
||||
if not not_check_attrib and elt.attrib:
|
||||
return False
|
||||
empty = True
|
||||
for file_ in elt:
|
||||
empty = False
|
||||
return empty
|
||||
|
||||
|
||||
def update_creole_1():
|
||||
print('pfff')
|
@ -26,10 +26,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import List
|
||||
from unicodedata import normalize, combining
|
||||
import re
|
||||
|
||||
from importlib.machinery import SourceFileLoader
|
||||
from importlib.util import spec_from_loader, module_from_spec
|
||||
|
||||
from .i18n import _
|
||||
from .error import DictConsistencyError
|
||||
|
||||
NAME_REGEXP = re.compile(r"^[a-z0-9_]*$")
|
||||
|
||||
|
||||
def valid_variable_family_name(name: str,
|
||||
xmlfiles: List[str],
|
||||
) -> None:
|
||||
match = NAME_REGEXP.search(name)
|
||||
if not match:
|
||||
msg = _(f'invalid variable or family name "{name}" must only contains '
|
||||
'lowercase ascii character, number or _')
|
||||
raise DictConsistencyError(msg, 76, xmlfiles)
|
||||
|
||||
|
||||
def normalize_family(family_name: str) -> str:
|
||||
"""replace space, accent, uppercase, ... by valid character
|
||||
|
@ -32,7 +32,6 @@ from lxml.etree import DTD, parse, XMLSyntaxError # pylint: disable=E0611
|
||||
|
||||
from .i18n import _
|
||||
from .error import DictConsistencyError
|
||||
from .config import RougailConfig
|
||||
|
||||
|
||||
class XMLReflector:
|
||||
@ -40,14 +39,16 @@ class XMLReflector:
|
||||
parsing it, validating against the Creole DTD,
|
||||
writing the xml result on the disk
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self,
|
||||
rougailconfig: 'RougailConfig',
|
||||
) -> None:
|
||||
"""Loads the Creole DTD
|
||||
|
||||
:raises IOError: if the DTD is not found
|
||||
|
||||
:param dtdfilename: the full filename of the Creole DTD
|
||||
"""
|
||||
dtdfilename = RougailConfig['dtdfilename']
|
||||
dtdfilename = rougailconfig['dtdfilename']
|
||||
if not isfile(dtdfilename):
|
||||
raise IOError(_("no such DTD file: {}").format(dtdfilename))
|
||||
with open(dtdfilename, 'r') as dtdfd:
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<services>
|
||||
<service name="tata">
|
||||
<rougail version="0.10">
|
||||
<services>
|
||||
<service name="tata">
|
||||
</service>
|
||||
</services>
|
||||
</services>
|
||||
</rougail>
|
||||
|
10
tests/dictionaries/00empty/makedict/after.json
Normal file
10
tests/dictionaries/00empty/makedict/after.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"services.tata.activate": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"services.tata.manage": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
}
|
||||
}
|
4
tests/dictionaries/00empty/makedict/base.json
Normal file
4
tests/dictionaries/00empty/makedict/base.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"services.tata.activate": true,
|
||||
"services.tata.manage": true
|
||||
}
|
10
tests/dictionaries/00empty/makedict/before.json
Normal file
10
tests/dictionaries/00empty/makedict/before.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"services.tata.activate": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"services.tata.manage": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ try:
|
||||
from tiramisu3 import *
|
||||
except:
|
||||
from tiramisu import *
|
||||
option_2 = OptionDescription(name="tata", doc="tata", children=[])
|
||||
option_3 = BoolOption(name="activate", doc="activate", default=True)
|
||||
option_4 = BoolOption(name="manage", doc="manage", default=True)
|
||||
option_2 = OptionDescription(name="tata", doc="tata", children=[option_3, option_4])
|
||||
option_1 = OptionDescription(name="services", doc="services", children=[option_2], properties=frozenset({"hidden"}))
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
||||
|
@ -1,13 +1,11 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<variables>
|
||||
<variable name="myvar" auto_freeze="True">
|
||||
<value>no</value>
|
||||
</variable>
|
||||
<variable name="instanciated_module" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
</variables>
|
||||
<rougail version="0.10">
|
||||
<variables>
|
||||
<variable name="myvar" auto_freeze="True">
|
||||
<value>no</value>
|
||||
</variable>
|
||||
<variable name="server_deployed" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
</variables>
|
||||
</rougail>
|
||||
<!-- vim: ts=4 sw=4 expandtab
|
||||
-->
|
||||
|
10
tests/dictionaries/00load_autofreeze/makedict/after.json
Normal file
10
tests/dictionaries/00load_autofreeze/makedict/after.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.myvar": {
|
||||
"owner": "forced",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
@ -1 +1,4 @@
|
||||
{"rougail.myvar": "no", "rougail.instanciated_module": false}
|
||||
{
|
||||
"rougail.myvar": "no",
|
||||
"rougail.server_deployed": false
|
||||
}
|
||||
|
10
tests/dictionaries/00load_autofreeze/makedict/before.json
Normal file
10
tests/dictionaries/00load_autofreeze/makedict/before.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.myvar": {
|
||||
"owner": "default",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ try:
|
||||
from tiramisu3 import *
|
||||
except:
|
||||
from tiramisu import *
|
||||
option_3 = BoolOption(name="instanciated_module", doc="instanciated_module", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_2 = StrOption(name="myvar", doc="myvar", default="no", properties=frozenset({"auto_freeze", "force_store_value", "mandatory", "normal", Calculation(func.calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_3, todict=True), 'expected': ParamValue(True), 'reverse_condition': ParamValue(True)}))}))
|
||||
option_3 = BoolOption(name="server_deployed", doc="server_deployed", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_2 = StrOption(name="myvar", doc="myvar", default="no", properties=frozenset({"basic", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(option_3, todict=True, notraisepropertyerror=True), 'expected': ParamValue(True)}))}))
|
||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2, option_3])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
||||
|
@ -1,13 +1,11 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<variables>
|
||||
<variable name="my_var" auto_freeze="True" mode="expert">
|
||||
<value>no</value>
|
||||
</variable>
|
||||
<variable name="instanciated_module" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
</variables>
|
||||
<rougail version="0.10">
|
||||
<variables>
|
||||
<variable name="my_var" auto_freeze="True" mode="expert">
|
||||
<value>no</value>
|
||||
</variable>
|
||||
<variable name="server_deployed" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
</variables>
|
||||
</rougail>
|
||||
<!-- vim: ts=4 sw=4 expandtab
|
||||
-->
|
||||
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.my_var": {
|
||||
"owner": "forced",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
@ -1 +1,4 @@
|
||||
{"rougail.my_var": "no", "rougail.instanciated_module": false}
|
||||
{
|
||||
"rougail.my_var": "no",
|
||||
"rougail.server_deployed": false
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.my_var": {
|
||||
"owner": "default",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ try:
|
||||
from tiramisu3 import *
|
||||
except:
|
||||
from tiramisu import *
|
||||
option_3 = BoolOption(name="instanciated_module", doc="instanciated_module", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_2 = StrOption(name="my_var", doc="my_var", default="no", properties=frozenset({"auto_freeze", "expert", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_3, todict=True), 'expected': ParamValue(True), 'reverse_condition': ParamValue(True)}))}))
|
||||
option_3 = BoolOption(name="server_deployed", doc="server_deployed", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_2 = StrOption(name="my_var", doc="my_var", default="no", properties=frozenset({"expert", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(option_3, todict=True, notraisepropertyerror=True), 'expected': ParamValue(True)}))}))
|
||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2, option_3])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
||||
|
@ -1,12 +1,11 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<variables>
|
||||
<family name="général">
|
||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_save="True">
|
||||
<value>non</value>
|
||||
</variable>
|
||||
</family>
|
||||
</variables>
|
||||
<rougail version="0.10">
|
||||
<variables>
|
||||
<variable name="server_deployed" type="boolean"/>
|
||||
<family name="general" description="général">
|
||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_save="True">
|
||||
<value>non</value>
|
||||
</variable>
|
||||
</family>
|
||||
</variables>
|
||||
</rougail>
|
||||
<!-- vim: ts=4 sw=4 expandtab
|
||||
-->
|
||||
|
10
tests/dictionaries/00load_autosave/makedict/after.json
Normal file
10
tests/dictionaries/00load_autosave/makedict/after.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"rougail.general.mode_conteneur_actif": {
|
||||
"owner": "forced",
|
||||
"value": "non"
|
||||
}
|
||||
}
|
@ -1 +1,4 @@
|
||||
{"rougail.general.mode_conteneur_actif": "non"}
|
||||
{
|
||||
"rougail.server_deployed": true,
|
||||
"rougail.general.mode_conteneur_actif": "non"
|
||||
}
|
||||
|
10
tests/dictionaries/00load_autosave/makedict/before.json
Normal file
10
tests/dictionaries/00load_autosave/makedict/before.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"rougail.general.mode_conteneur_actif": {
|
||||
"owner": "default",
|
||||
"value": "non"
|
||||
}
|
||||
}
|
@ -11,7 +11,8 @@ try:
|
||||
from tiramisu3 import *
|
||||
except:
|
||||
from tiramisu import *
|
||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"basic", "force_store_value", "mandatory"}))
|
||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"basic"}))
|
||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
||||
option_2 = BoolOption(name="server_deployed", doc="server_deployed", default=True, properties=frozenset({"mandatory", "normal"}))
|
||||
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"basic", "force_store_value", "mandatory"}))
|
||||
option_3 = OptionDescription(name="general", doc="général", children=[option_4], properties=frozenset({"basic"}))
|
||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2, option_3])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
||||
|
@ -1,12 +1,11 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<variables>
|
||||
<family name="général">
|
||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_save="True" mode="expert">
|
||||
<value>non</value>
|
||||
</variable>
|
||||
</family>
|
||||
</variables>
|
||||
<rougail version="0.10">
|
||||
<variables>
|
||||
<variable name="server_deployed" type="boolean"/>
|
||||
<family name="general" description="général">
|
||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_save="True" mode="expert">
|
||||
<value>non</value>
|
||||
</variable>
|
||||
</family>
|
||||
</variables>
|
||||
</rougail>
|
||||
<!-- vim: ts=4 sw=4 expandtab
|
||||
-->
|
||||
|
10
tests/dictionaries/00load_autosaveexpert/makedict/after.json
Normal file
10
tests/dictionaries/00load_autosaveexpert/makedict/after.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"rougail.general.mode_conteneur_actif": {
|
||||
"owner": "forced",
|
||||
"value": "non"
|
||||
}
|
||||
}
|
@ -1 +1,4 @@
|
||||
{"rougail.general.mode_conteneur_actif": "non"}
|
||||
{
|
||||
"rougail.server_deployed": true,
|
||||
"rougail.general.mode_conteneur_actif": "non"
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"rougail.general.mode_conteneur_actif": {
|
||||
"owner": "default",
|
||||
"value": "non"
|
||||
}
|
||||
}
|
@ -11,7 +11,8 @@ try:
|
||||
from tiramisu3 import *
|
||||
except:
|
||||
from tiramisu import *
|
||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"expert", "force_store_value", "mandatory"}))
|
||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"expert"}))
|
||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
||||
option_2 = BoolOption(name="server_deployed", doc="server_deployed", default=True, properties=frozenset({"mandatory", "normal"}))
|
||||
option_4 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"expert", "force_store_value", "mandatory"}))
|
||||
option_3 = OptionDescription(name="general", doc="général", children=[option_4], properties=frozenset({"expert"}))
|
||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2, option_3])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
||||
|
@ -1,13 +1,11 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<variables>
|
||||
<family name="général">
|
||||
<!-- this is a comment -->
|
||||
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
|
||||
<value>non</value>
|
||||
</variable>
|
||||
</family>
|
||||
</variables>
|
||||
<rougail version="0.10">
|
||||
<variables>
|
||||
<family name="general" description="général">
|
||||
<!-- this is a comment -->
|
||||
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
|
||||
<value>non</value>
|
||||
</variable>
|
||||
</family>
|
||||
</variables>
|
||||
</rougail>
|
||||
<!-- vim: ts=4 sw=4 expandtab
|
||||
-->
|
||||
|
6
tests/dictionaries/00load_comment/makedict/after.json
Normal file
6
tests/dictionaries/00load_comment/makedict/after.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"rougail.general.mode_conteneur_actif": {
|
||||
"owner": "default",
|
||||
"value": "non"
|
||||
}
|
||||
}
|
@ -1 +1,3 @@
|
||||
{"rougail.general.mode_conteneur_actif": "non"}
|
||||
{
|
||||
"rougail.general.mode_conteneur_actif": "non"
|
||||
}
|
||||
|
6
tests/dictionaries/00load_comment/makedict/before.json
Normal file
6
tests/dictionaries/00load_comment/makedict/before.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"rougail.general.mode_conteneur_actif": {
|
||||
"owner": "default",
|
||||
"value": "non"
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user