palo_alto 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/lib/palo_alto/config.rb +701 -675
- data/lib/palo_alto/op.rb +71 -40
- data/lib/palo_alto/version.rb +1 -1
- data/lib/palo_alto.rb +127 -53
- metadata +3 -2
data/lib/palo_alto/config.rb
CHANGED
@@ -1,812 +1,838 @@
|
|
1
|
-
# generated: 2021-
|
1
|
+
# generated: 2021-11-21 15:25:41 +0100
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'openssl'
|
3
5
|
require 'nokogiri'
|
4
6
|
|
5
7
|
module PaloAlto
|
8
|
+
# https://github.com/teamcapybara/xpath - MIT license
|
9
|
+
module DSL
|
10
|
+
def relative(*expressions)
|
11
|
+
Expression.new(:relative, current, expressions)
|
12
|
+
end
|
6
13
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
Expression.new(:relative, current, expressions)
|
11
|
-
end
|
14
|
+
def root(*expressions)
|
15
|
+
Expression.new(:root, current, expressions)
|
16
|
+
end
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
def current
|
19
|
+
Expression.new(:this_node)
|
20
|
+
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
def descendant(*expressions)
|
23
|
+
Expression.new(:descendant, current, expressions)
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
26
|
+
def child(*expressions)
|
27
|
+
Expression.new(:child, current, expressions)
|
28
|
+
end
|
24
29
|
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
def axis(name, *element_names)
|
31
|
+
Expression.new(:axis, current, name, element_names)
|
32
|
+
end
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
34
|
+
def anywhere(*expressions)
|
35
|
+
Expression.new(:anywhere, expressions)
|
36
|
+
end
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
|
38
|
+
def xpath_attr(expression)
|
39
|
+
Expression.new(:attribute, current, expression)
|
40
|
+
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
def text
|
43
|
+
Expression.new(:text, current)
|
44
|
+
end
|
40
45
|
|
41
|
-
|
42
|
-
|
43
|
-
|
46
|
+
def css(selector)
|
47
|
+
Expression.new(:css, current, Literal.new(selector))
|
48
|
+
end
|
44
49
|
|
45
|
-
|
46
|
-
|
47
|
-
|
50
|
+
def function(name, *arguments)
|
51
|
+
Expression.new(:function, name, *arguments)
|
52
|
+
end
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
|
54
|
+
def method(name, *arguments)
|
55
|
+
if name != :not
|
56
|
+
Expression.new(:function, name, current, *arguments)
|
57
|
+
else
|
58
|
+
Expression.new(:function, name, *arguments)
|
59
|
+
end
|
60
|
+
end
|
52
61
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
def where(expression)
|
63
|
+
if expression
|
64
|
+
Expression.new(:where, current, expression)
|
65
|
+
else
|
66
|
+
current
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# alias_method :[], :where
|
60
70
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
else
|
65
|
-
current
|
66
|
-
end
|
67
|
-
end
|
68
|
-
#alias_method :[], :where
|
71
|
+
def is(expression)
|
72
|
+
Expression.new(:is, current, expression)
|
73
|
+
end
|
69
74
|
|
70
|
-
|
71
|
-
|
72
|
-
|
75
|
+
def binary_operator(name, rhs)
|
76
|
+
Expression.new(:binary_operator, name, current, rhs)
|
77
|
+
end
|
73
78
|
|
74
|
-
|
75
|
-
|
76
|
-
|
79
|
+
def union(*expressions)
|
80
|
+
Union.new(*[self, expressions].flatten)
|
81
|
+
end
|
82
|
+
alias + union
|
77
83
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
alias_method :+, :union
|
84
|
+
def last
|
85
|
+
function(:last)
|
86
|
+
end
|
82
87
|
|
83
|
-
|
84
|
-
|
85
|
-
|
88
|
+
def position
|
89
|
+
function(:position)
|
90
|
+
end
|
86
91
|
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
METHODS = [
|
93
|
+
# node set
|
94
|
+
:count, :id, :local_name, :namespace_uri,
|
95
|
+
# string
|
96
|
+
:string, :concat, :starts_with, :contains, :substring_before,
|
97
|
+
:substring_after, :substring, :string_length, :normalize_space,
|
98
|
+
:translate,
|
99
|
+
# boolean
|
100
|
+
:boolean, :not, :true, :false, :lang,
|
101
|
+
# number
|
102
|
+
:number, :sum, :floor, :ceiling, :round
|
103
|
+
].freeze
|
90
104
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
:translate,
|
98
|
-
# boolean
|
99
|
-
:boolean, :not, :true, :false, :lang,
|
100
|
-
# number
|
101
|
-
:number, :sum, :floor, :ceiling, :round
|
102
|
-
].freeze
|
105
|
+
METHODS.each do |key|
|
106
|
+
name = key.to_s.tr('_', '-').to_sym
|
107
|
+
define_method key do |*args|
|
108
|
+
method(name, *args)
|
109
|
+
end
|
110
|
+
end
|
103
111
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
method(name, *args)
|
108
|
-
end
|
109
|
-
end
|
112
|
+
def qname
|
113
|
+
method(:name)
|
114
|
+
end
|
110
115
|
|
111
|
-
|
112
|
-
|
113
|
-
|
116
|
+
alias inverse not
|
117
|
+
alias ~ not
|
118
|
+
alias ! not
|
119
|
+
alias normalize normalize_space
|
120
|
+
alias n normalize_space
|
114
121
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
122
|
+
OPERATORS = [
|
123
|
+
%i[equals = ==],
|
124
|
+
%i[or or |],
|
125
|
+
%i[and and &],
|
126
|
+
%i[not_equals != !=],
|
127
|
+
%i[lte <= <=],
|
128
|
+
%i[lt < <],
|
129
|
+
%i[gte >= >=],
|
130
|
+
%i[gt > >],
|
131
|
+
%i[plus +],
|
132
|
+
%i[minus -],
|
133
|
+
%i[multiply * *],
|
134
|
+
%i[divide div /],
|
135
|
+
%i[mod mod %]
|
136
|
+
].freeze
|
120
137
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
%i[lt < <],
|
128
|
-
%i[gte >= >=],
|
129
|
-
%i[gt > >],
|
130
|
-
%i[plus +],
|
131
|
-
%i[minus -],
|
132
|
-
%i[multiply * *],
|
133
|
-
%i[divide div /],
|
134
|
-
%i[mod mod %]
|
135
|
-
].freeze
|
138
|
+
OPERATORS.each do |(name, operator, alias_name)|
|
139
|
+
define_method name do |rhs|
|
140
|
+
binary_operator(operator, rhs)
|
141
|
+
end
|
142
|
+
alias_method alias_name, name if alias_name
|
143
|
+
end
|
136
144
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
end
|
145
|
+
AXES = %i[
|
146
|
+
ancestor ancestor_or_self attribute descendant_or_self
|
147
|
+
following following_sibling namespace parent preceding
|
148
|
+
preceding_sibling self
|
149
|
+
].freeze
|
143
150
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
151
|
+
AXES.each do |key|
|
152
|
+
name = key.to_s.tr('_', '-').to_sym
|
153
|
+
define_method key do |*element_names|
|
154
|
+
axis(name, *element_names)
|
155
|
+
end
|
156
|
+
end
|
149
157
|
|
150
|
-
|
151
|
-
name = key.to_s.tr('_', '-').to_sym
|
152
|
-
define_method key do |*element_names|
|
153
|
-
axis(name, *element_names)
|
154
|
-
end
|
155
|
-
end
|
158
|
+
alias self_axis self
|
156
159
|
|
157
|
-
|
160
|
+
def ends_with(suffix)
|
161
|
+
function(:substring, current,
|
162
|
+
function(:'string-length', current).minus(function(:'string-length', suffix)).plus(1)) == suffix
|
163
|
+
end
|
158
164
|
|
159
|
-
|
160
|
-
|
161
|
-
|
165
|
+
def contains_word(word)
|
166
|
+
function(:concat, ' ', current.normalize_space, ' ').contains(" #{word} ")
|
167
|
+
end
|
162
168
|
|
163
|
-
|
164
|
-
|
165
|
-
end
|
169
|
+
UPPERCASE_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'
|
170
|
+
LOWERCASE_LETTERS = 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'
|
166
171
|
|
167
|
-
|
168
|
-
|
172
|
+
def lowercase
|
173
|
+
method(:translate, UPPERCASE_LETTERS, LOWERCASE_LETTERS)
|
174
|
+
end
|
169
175
|
|
170
|
-
|
171
|
-
|
172
|
-
|
176
|
+
def parenthesis(arg)
|
177
|
+
Expression.new(:parenthesis, arg)
|
178
|
+
end
|
173
179
|
|
174
|
-
|
175
|
-
|
176
|
-
|
180
|
+
def uppercase
|
181
|
+
method(:translate, LOWERCASE_LETTERS, UPPERCASE_LETTERS)
|
182
|
+
end
|
177
183
|
|
178
|
-
|
179
|
-
|
180
|
-
|
184
|
+
def one_of(*expressions)
|
185
|
+
expressions.map { |e| current.equals(e) }.reduce(:or)
|
186
|
+
end
|
181
187
|
|
182
|
-
|
183
|
-
|
184
|
-
|
188
|
+
def next_sibling(*expressions)
|
189
|
+
axis(:'following-sibling')[1].axis(:self, *expressions)
|
190
|
+
end
|
185
191
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
192
|
+
def previous_sibling(*expressions)
|
193
|
+
axis(:'preceding-sibling')[1].axis(:self, *expressions)
|
194
|
+
end
|
195
|
+
end
|
190
196
|
|
191
|
-
|
192
|
-
|
197
|
+
extend PaloAlto::DSL
|
198
|
+
include PaloAlto::DSL
|
193
199
|
|
194
|
-
|
195
|
-
|
196
|
-
|
200
|
+
def self.generate
|
201
|
+
yield(self)
|
202
|
+
end
|
197
203
|
|
198
|
-
|
199
|
-
|
204
|
+
class Union
|
205
|
+
include Enumerable
|
200
206
|
|
201
|
-
|
202
|
-
|
207
|
+
attr_reader :expressions
|
208
|
+
alias arguments expressions
|
203
209
|
|
204
|
-
|
205
|
-
|
206
|
-
|
210
|
+
def initialize(*expressions)
|
211
|
+
@expressions = expressions
|
212
|
+
end
|
207
213
|
|
208
|
-
|
209
|
-
|
210
|
-
|
214
|
+
def expression
|
215
|
+
:union
|
216
|
+
end
|
211
217
|
|
212
|
-
|
213
|
-
|
214
|
-
|
218
|
+
def each(&block)
|
219
|
+
arguments.each(&block)
|
220
|
+
end
|
215
221
|
|
216
|
-
|
217
|
-
|
218
|
-
|
222
|
+
def method_missing(*args) # rubocop:disable Style/MissingRespondToMissing
|
223
|
+
PaloAlto::Union.new(*arguments.map { |e| e.send(*args) })
|
224
|
+
end
|
219
225
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
226
|
+
def to_xpath(type = nil)
|
227
|
+
Renderer.render(self, type)
|
228
|
+
end
|
229
|
+
end
|
224
230
|
|
225
|
-
|
226
|
-
|
227
|
-
def initialize(value)
|
228
|
-
@value = value
|
229
|
-
end
|
230
|
-
end
|
231
|
-
class Renderer
|
232
|
-
def self.render(node, type)
|
233
|
-
new(type).render(node)
|
234
|
-
end
|
231
|
+
class Literal
|
232
|
+
attr_reader :value
|
235
233
|
|
236
|
-
|
237
|
-
|
238
|
-
|
234
|
+
def initialize(value)
|
235
|
+
@value = value
|
236
|
+
end
|
237
|
+
end
|
239
238
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
239
|
+
class Renderer
|
240
|
+
def self.render(node, type)
|
241
|
+
new(type).render(node)
|
242
|
+
end
|
244
243
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
when Array then argument.map { |element| convert_argument(element) }
|
249
|
-
when String then string_literal(argument)
|
250
|
-
when Literal then argument.value
|
251
|
-
else argument.to_s
|
252
|
-
end
|
253
|
-
end
|
244
|
+
def initialize(type)
|
245
|
+
@type = type
|
246
|
+
end
|
254
247
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
end.join(%q(,"'",))
|
260
|
-
"concat(#{string})"
|
261
|
-
else
|
262
|
-
"'#{string}'"
|
263
|
-
end
|
264
|
-
end
|
248
|
+
def render(node)
|
249
|
+
arguments = node.arguments.map { |argument| convert_argument(argument) }
|
250
|
+
send(node.expression, *arguments)
|
251
|
+
end
|
265
252
|
|
266
|
-
|
267
|
-
|
268
|
-
|
253
|
+
def convert_argument(argument)
|
254
|
+
case argument
|
255
|
+
when Expression, Union then render(argument)
|
256
|
+
when Array then argument.map { |element| convert_argument(element) }
|
257
|
+
when String then string_literal(argument)
|
258
|
+
when Literal then argument.value
|
259
|
+
else argument.to_s
|
260
|
+
end
|
261
|
+
end
|
269
262
|
|
270
|
-
|
271
|
-
|
272
|
-
|
263
|
+
def string_literal(string)
|
264
|
+
if string.include?("'")
|
265
|
+
string = string.split("'", -1).map do |substr|
|
266
|
+
"'#{substr}'"
|
267
|
+
end.join(%q(,"'",))
|
268
|
+
"concat(#{string})"
|
269
|
+
else
|
270
|
+
"'#{string}'"
|
271
|
+
end
|
272
|
+
end
|
273
273
|
|
274
|
-
|
275
|
-
|
276
|
-
|
274
|
+
def this_node
|
275
|
+
'.'
|
276
|
+
end
|
277
277
|
|
278
|
-
|
279
|
-
|
280
|
-
|
278
|
+
def binary_operator(name, left, right)
|
279
|
+
"#{left}#{name}#{right}".gsub('./@', '@')
|
280
|
+
end
|
281
281
|
|
282
|
-
|
283
|
-
|
284
|
-
|
282
|
+
def parenthesis(arg)
|
283
|
+
"(#{arg})"
|
284
|
+
end
|
285
285
|
|
286
|
-
|
287
|
-
|
288
|
-
|
286
|
+
def root(_current, element_names)
|
287
|
+
element_names.any? ? "/#{element_names.join('/')}" : ''
|
288
|
+
end
|
289
289
|
|
290
|
-
|
291
|
-
|
292
|
-
|
290
|
+
def relative(_current, _element_names)
|
291
|
+
'.'
|
292
|
+
end
|
293
293
|
|
294
|
-
|
295
|
-
|
296
|
-
|
294
|
+
def descendant(current, element_names)
|
295
|
+
with_element_conditions("#{current}//", element_names)
|
296
|
+
end
|
297
297
|
|
298
|
-
|
299
|
-
|
300
|
-
|
298
|
+
def child(current, element_names)
|
299
|
+
with_element_conditions("#{current}/", element_names)
|
300
|
+
end
|
301
301
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
else
|
306
|
-
"#{current}/attribute::*[local-name(.) = #{string_literal(name)}]"
|
307
|
-
end
|
308
|
-
end
|
302
|
+
def axis(current, name, element_names)
|
303
|
+
with_element_conditions("#{current}/#{name}::", element_names)
|
304
|
+
end
|
309
305
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
else
|
314
|
-
function(:contains, one, two)
|
315
|
-
end
|
316
|
-
end
|
306
|
+
def anywhere(element_names)
|
307
|
+
with_element_conditions('//', element_names)
|
308
|
+
end
|
317
309
|
|
318
|
-
|
319
|
-
|
320
|
-
|
310
|
+
def where(on, condition)
|
311
|
+
"#{on}[#{condition}]"
|
312
|
+
end
|
321
313
|
|
322
|
-
|
323
|
-
|
324
|
-
|
314
|
+
def attribute(current, name)
|
315
|
+
if valid_xml_name?(name)
|
316
|
+
"#{current}/@#{name}"
|
317
|
+
else
|
318
|
+
"#{current}/attribute::*[local-name(.) = #{string_literal(name)}]"
|
319
|
+
end
|
320
|
+
end
|
325
321
|
|
326
|
-
|
327
|
-
|
328
|
-
|
322
|
+
def is(one, two)
|
323
|
+
if @type == :exact
|
324
|
+
binary_operator('=', one, two)
|
325
|
+
else
|
326
|
+
function(:contains, one, two)
|
327
|
+
end
|
328
|
+
end
|
329
329
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
end
|
334
|
-
union(paths)
|
335
|
-
end
|
330
|
+
def variable(name)
|
331
|
+
"%{#{name}}"
|
332
|
+
end
|
336
333
|
|
337
|
-
|
338
|
-
|
339
|
-
|
334
|
+
def text(current)
|
335
|
+
"#{current}/text()"
|
336
|
+
end
|
340
337
|
|
341
|
-
|
342
|
-
|
343
|
-
|
338
|
+
def literal(node)
|
339
|
+
node
|
340
|
+
end
|
344
341
|
|
345
|
-
|
342
|
+
def css(current, selector)
|
343
|
+
paths = Nokogiri::CSS.xpath_for(selector).map do |xpath_selector|
|
344
|
+
"#{current}#{xpath_selector}"
|
345
|
+
end
|
346
|
+
union(paths)
|
347
|
+
end
|
346
348
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
elsif element_names.length > 1
|
351
|
-
"#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(' | ')}]"
|
352
|
-
else
|
353
|
-
"#{expression}*"
|
354
|
-
end
|
355
|
-
end
|
349
|
+
def union(*expressions)
|
350
|
+
expressions.join(' | ')
|
351
|
+
end
|
356
352
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
end
|
361
|
-
class Expression
|
362
|
-
include PaloAlto::DSL
|
353
|
+
def function(name, *arguments)
|
354
|
+
"#{name}(#{arguments.join(', ')})"
|
355
|
+
end
|
363
356
|
|
364
|
-
|
365
|
-
def initialize(expression, *arguments)
|
366
|
-
@expression = expression
|
367
|
-
@arguments = arguments
|
368
|
-
end
|
357
|
+
private
|
369
358
|
|
370
|
-
|
371
|
-
|
372
|
-
|
359
|
+
def with_element_conditions(expression, element_names)
|
360
|
+
if element_names.length == 1
|
361
|
+
"#{expression}#{element_names.first}"
|
362
|
+
elsif element_names.length > 1
|
363
|
+
"#{expression}*[#{element_names.map { |e| "self::#{e}" }.join(' | ')}]"
|
364
|
+
else
|
365
|
+
"#{expression}*"
|
366
|
+
end
|
367
|
+
end
|
373
368
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
369
|
+
def valid_xml_name?(name)
|
370
|
+
name =~ /^[a-zA-Z_:][a-zA-Z0-9_:.\-]*$/
|
371
|
+
end
|
372
|
+
end
|
378
373
|
|
374
|
+
class Expression
|
375
|
+
include PaloAlto::DSL
|
379
376
|
|
380
|
-
|
381
|
-
class ConfigClass < Expression
|
382
|
-
attr_reader :api_attributes, :subclasses
|
383
|
-
attr_accessor :parent_instance
|
377
|
+
attr_accessor :expression, :arguments
|
384
378
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
@create_children = create_children
|
390
|
-
@external_values = {} # data we received and don't need to set again
|
391
|
-
@api_attributes={}
|
379
|
+
def initialize(expression, *arguments)
|
380
|
+
@expression = expression
|
381
|
+
@arguments = arguments
|
382
|
+
end
|
392
383
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
@arguments = [ xpath_argument, [_section] ]
|
397
|
-
end
|
398
|
-
end
|
384
|
+
def current
|
385
|
+
self
|
386
|
+
end
|
399
387
|
|
400
|
-
|
401
|
-
|
402
|
-
|
388
|
+
def to_xpath(type = nil)
|
389
|
+
Renderer.render(self, type)
|
390
|
+
end
|
391
|
+
end
|
403
392
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
393
|
+
class XML
|
394
|
+
class ConfigClass < Expression
|
395
|
+
attr_reader :api_attributes, :subclasses, :parent_instance
|
396
|
+
attr_accessor :parent_instance, :force_relative
|
408
397
|
|
409
|
-
|
410
|
-
|
398
|
+
def initialize(parent_instance:, create_children: false)
|
399
|
+
@parent_instance = parent_instance
|
400
|
+
@subclasses = {}
|
401
|
+
@values = {}
|
402
|
+
@create_children = create_children
|
403
|
+
@external_values = {} # data we received and don't need to set again
|
404
|
+
@api_attributes = {}
|
411
405
|
|
412
|
-
|
413
|
-
|
414
|
-
|
406
|
+
@expression = :child
|
407
|
+
unless is_a?(ArrayConfigClass) # for ArrayConfigClass, it will be set externally after the constructor
|
408
|
+
xpath_argument = @parent_instance
|
409
|
+
@arguments = [xpath_argument, [_section]]
|
410
|
+
end
|
411
|
+
end
|
415
412
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
type: "config",
|
420
|
-
action: "get",
|
421
|
-
xpath: self.to_xpath
|
422
|
-
}
|
413
|
+
class << self
|
414
|
+
attr_accessor :props
|
415
|
+
end
|
423
416
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
warn "Elapsed for parsing #{result.length} results: #{Time.now-start_time} seconds"
|
429
|
-
end
|
430
|
-
result
|
431
|
-
end
|
417
|
+
def create!
|
418
|
+
@create_children = true
|
419
|
+
self
|
420
|
+
end
|
432
421
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
self
|
437
|
-
end
|
422
|
+
def inspect
|
423
|
+
to_s[0..-1] + ' ' + values(full_tree: false).map { |k, v| "#{k}: #{v.inspect}" }.join(', ') + '>'
|
424
|
+
end
|
438
425
|
|
439
|
-
|
440
|
-
|
441
|
-
raise(InvalidCommandException, "Please use 'get_all' here")
|
442
|
-
end
|
426
|
+
def get_all
|
427
|
+
raise(InvalidCommandException, "please use 'get' here") if self.class.superclass != ArrayConfigClass
|
443
428
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
429
|
+
payload = {
|
430
|
+
type: 'config',
|
431
|
+
action: 'get',
|
432
|
+
xpath: to_xpath
|
433
|
+
}
|
449
434
|
|
450
|
-
|
451
|
-
|
435
|
+
data = XML.execute(payload)
|
436
|
+
start_time = Time.now
|
437
|
+
result = parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first)
|
438
|
+
if XML.debug.include?(:statistics)
|
439
|
+
warn "Elapsed for parsing #{result.length} results: #{Time.now - start_time} seconds"
|
440
|
+
end
|
441
|
+
result
|
442
|
+
end
|
452
443
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
444
|
+
def clear!
|
445
|
+
@subclasses = {}
|
446
|
+
@values = {}
|
447
|
+
self
|
448
|
+
end
|
458
449
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
n = data.xpath('//response/result/*')
|
464
|
-
if n.any?
|
465
|
-
clear!
|
466
|
-
external_set(n.first)
|
450
|
+
def get(ignore_empty_result: false, xpath: to_xpath, return_only: false)
|
451
|
+
if self.class.superclass == ArrayConfigClass && !@selector
|
452
|
+
raise(InvalidCommandException, "Please use 'get_all' here")
|
453
|
+
end
|
467
454
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
self
|
474
|
-
end.tap do
|
475
|
-
if XML.debug.include?(:statistics)
|
476
|
-
warn "Elapsed for parsing: #{Time.now-start_time} seconds"
|
477
|
-
end
|
478
|
-
end
|
479
|
-
end
|
455
|
+
payload = {
|
456
|
+
type: 'config',
|
457
|
+
action: 'get',
|
458
|
+
xpath: xpath
|
459
|
+
}
|
480
460
|
|
481
|
-
|
482
|
-
|
483
|
-
props.keys.find{|k| k=="@#{attr.name}"}
|
484
|
-
}
|
485
|
-
Hash[primary_key_attr.name.to_sym, primary_key_attr.value]
|
486
|
-
end
|
461
|
+
data = XML.execute(payload)
|
462
|
+
start_time = Time.now
|
487
463
|
|
488
|
-
|
489
|
-
|
464
|
+
if data.xpath('//response/result/*').length != 1 && (ignore_empty_result == false)
|
465
|
+
raise(ObjectNotPresentException, "empty result: #{payload.inspect}")
|
466
|
+
end
|
490
467
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
468
|
+
if return_only
|
469
|
+
data.xpath('//response/result/*')
|
470
|
+
else
|
471
|
+
@create_children = true
|
472
|
+
n = data.xpath('//response/result/*')
|
473
|
+
if n.any?
|
474
|
+
clear!
|
475
|
+
external_set(n.first)
|
497
476
|
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
477
|
+
if is_a?(ArrayConfigClass)
|
478
|
+
primary_key = get_primary_key(n.first.attribute_nodes, self.class.props)
|
479
|
+
set_array_class_attributes(n.first, primary_key) # primary key, api_attributes
|
480
|
+
end
|
481
|
+
end
|
482
|
+
self
|
483
|
+
end.tap do
|
484
|
+
warn "Elapsed for parsing: #{Time.now - start_time} seconds" if XML.debug.include?(:statistics)
|
485
|
+
end
|
486
|
+
end
|
508
487
|
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
488
|
+
def get_primary_key(attribute_nodes, props)
|
489
|
+
primary_key_attr = attribute_nodes.find do |attr|
|
490
|
+
props.keys.find { |k| k == "@#{attr.name}" }
|
491
|
+
end
|
492
|
+
Hash[primary_key_attr.name.to_sym, primary_key_attr.value]
|
493
|
+
end
|
514
494
|
|
515
|
-
|
495
|
+
def set_array_class_attributes(child, primary_key)
|
496
|
+
@external_values.merge!({ "@#{primary_key.keys.first}" => primary_key.values.first })
|
516
497
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
end
|
521
|
-
subclass.external_set(child)
|
522
|
-
else
|
523
|
-
raise "unknown key: #{child.name}"
|
524
|
-
end
|
525
|
-
subclass
|
526
|
-
}
|
527
|
-
end
|
498
|
+
# save also the other attributes like loc and uuid, if set
|
499
|
+
child.attribute_nodes.each do |attr|
|
500
|
+
next if attr.name.to_sym == primary_key.keys.first
|
528
501
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
return true if ['yes', true].include?(value)
|
533
|
-
return false if ['no', false].include?(value)
|
534
|
-
raise ArgumentError, "Not bool: #{value.inspect}"
|
535
|
-
when 'string', 'ipdiscontmask', 'iprangespec', 'ipspec', 'rangelistspec'
|
536
|
-
raise(ArgumentError, "Not string: #{value.inspect}") unless value.is_a?(String)
|
537
|
-
if prop_arr['regex']
|
538
|
-
raise ArgumentError, "Not matching regex: #{value.inspect} (#{prop_arr["regex"].inspect})" unless value.match(prop_arr["regex"])
|
539
|
-
end
|
540
|
-
if prop_arr['maxlen']
|
541
|
-
raise ArgumentError, 'Too long' if value.length > prop_arr['maxlen'].to_i
|
542
|
-
end
|
543
|
-
return value
|
544
|
-
when 'enum'
|
545
|
-
accepted_values = prop_arr.is_a?(Hash) ? prop_arr['enum'].map{|x| x['value']} : prop_arr.map{|x| x['value']} # is an array if part of value_type 'multiple'
|
546
|
-
return value if accepted_values.include?(value)
|
547
|
-
raise ArgumentError, "not allowed: #{value.inspect} (not within #{accepted_values.inspect})"
|
548
|
-
when 'float'
|
549
|
-
return Float(value)
|
550
|
-
when 'rangedint'
|
551
|
-
number = Integer(value)
|
552
|
-
return number if number >= prop_arr['min'].to_i && number <= prop_arr['max'].to_i
|
553
|
-
raise ArgumentError, "not in range #{prop_arr['min']}..#{prop_arr['max']}: #{number}"
|
554
|
-
when 'multiple'
|
555
|
-
prop_arr['multi-types'].keys.each{|key|
|
556
|
-
return enforce_type(prop_arr['multi-types'][key], value, value_type: key) rescue false
|
557
|
-
}
|
558
|
-
raise(ArgumentError, "Nothing matching found for #{value.inspect} (#{prop_arr.inspect})")
|
559
|
-
end
|
560
|
-
end
|
502
|
+
api_attributes[attr.name] = attr.value
|
503
|
+
end
|
504
|
+
end
|
561
505
|
|
562
|
-
|
563
|
-
|
506
|
+
def get_class_from_child_str(child)
|
507
|
+
# check for class name in camelcase format
|
508
|
+
self.class.const_get(child.name.capitalize.gsub(/-(.)/) { Regexp.last_match(1).upcase })
|
509
|
+
rescue NameError
|
510
|
+
raise "Child not found: #{child.name}"
|
511
|
+
end
|
564
512
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
if full_tree
|
576
|
-
@subclasses.each{|k,subclass|
|
577
|
-
if subclass.is_a?(Hash)
|
578
|
-
subclass.each{|k2, subclass2|
|
579
|
-
xml.send(k, k2){|xml|
|
580
|
-
subclass2.xml_builder(xml, full_tree: full_tree, changed_only: changed_only)
|
581
|
-
}
|
582
|
-
}
|
583
|
-
else
|
584
|
-
xml.method_missing(k){|xml| # somehow .send does not work with k==:system
|
585
|
-
subclass.xml_builder(xml, full_tree: full_tree, changed_only: changed_only)
|
586
|
-
}
|
587
|
-
end
|
588
|
-
}
|
589
|
-
end
|
590
|
-
return xml
|
591
|
-
end
|
513
|
+
def external_set(data)
|
514
|
+
data.element_children.map do |child|
|
515
|
+
child.name.match(/\A[a-zA-Z0-9_-]*\z/) or raise 'invalid character'
|
516
|
+
if prop = self.class.props[child.name]
|
517
|
+
if has_multiple_values?
|
518
|
+
@external_values[child.name] ||= []
|
519
|
+
@external_values[child.name] << enforce_type(prop, child.text)
|
520
|
+
else
|
521
|
+
@external_values[child.name] = enforce_type(prop, child.text)
|
522
|
+
end
|
592
523
|
|
593
|
-
|
594
|
-
|
595
|
-
|
524
|
+
elsif (new_class = get_class_from_child_str(child))
|
525
|
+
if new_class.superclass == ConfigClass
|
526
|
+
subclass = send(child.name.gsub('-', '_'))
|
527
|
+
elsif new_class.superclass == ArrayConfigClass
|
528
|
+
primary_key = get_primary_key(child.attribute_nodes, new_class.props)
|
596
529
|
|
597
|
-
|
598
|
-
if block
|
599
|
-
obj = self.child(section.to_sym).where(PaloAlto.instance_eval(&block))
|
600
|
-
entry.expression = obj.expression
|
601
|
-
entry.arguments = obj.arguments
|
602
|
-
entry
|
603
|
-
else
|
604
|
-
selector=args[0]
|
605
|
-
@subclasses[section]||= {}
|
530
|
+
subclass = send(child.name, primary_key) # create subclass
|
606
531
|
|
607
|
-
|
608
|
-
|
609
|
-
|
532
|
+
subclass.set_array_class_attributes(child, primary_key) # primary key, api_attributes
|
533
|
+
else
|
534
|
+
raise
|
535
|
+
end
|
536
|
+
subclass.external_set(child)
|
537
|
+
else
|
538
|
+
raise "unknown key: #{child.name}"
|
539
|
+
end
|
540
|
+
subclass
|
541
|
+
end
|
542
|
+
end
|
610
543
|
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
end
|
617
|
-
end
|
544
|
+
def enforce_type(prop_arr, value, value_type: prop_arr['type'])
|
545
|
+
case value_type
|
546
|
+
when 'bool'
|
547
|
+
return true if ['yes', true].include?(value)
|
548
|
+
return false if ['no', false].include?(value)
|
618
549
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
prop = prop_get(k, include_defaults: include_defaults)
|
623
|
-
h[k] = prop if prop
|
624
|
-
}
|
625
|
-
if full_tree
|
626
|
-
@subclasses.each{|k,subclass|
|
627
|
-
if subclass.is_a?(Hash)
|
628
|
-
h[k]||={}
|
629
|
-
subclass.each{|k2, subclass2|
|
630
|
-
h[k][k2]=subclass2.values(full_tree: true, include_defaults: include_defaults)
|
631
|
-
}
|
632
|
-
else
|
633
|
-
h[k]=subclass.values(full_tree: true, include_defaults: include_defaults)
|
634
|
-
end
|
635
|
-
}
|
636
|
-
end
|
637
|
-
return h
|
638
|
-
end
|
550
|
+
raise ArgumentError, "Not bool: #{value.inspect}"
|
551
|
+
when 'string', 'ipdiscontmask', 'iprangespec', 'ipspec', 'rangelistspec'
|
552
|
+
raise(ArgumentError, "Not string: #{value.inspect}") unless value.is_a?(String)
|
639
553
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
clear!
|
646
|
-
create!
|
647
|
-
h.each{|k,v|
|
648
|
-
if v.is_a?(Hash)
|
649
|
-
self.send(k.to_s.gsub('-','_')).set_values(v, external: external)
|
650
|
-
else
|
651
|
-
if external
|
652
|
-
@external_values[k]=v
|
653
|
-
else
|
654
|
-
self.prop_set(k,v)
|
655
|
-
end
|
656
|
-
end
|
657
|
-
}
|
658
|
-
self
|
659
|
-
end
|
554
|
+
if prop_arr['regex'] && !value.match(prop_arr['regex']) && !value.match(prop_arr['regex'])
|
555
|
+
raise ArgumentError,
|
556
|
+
"Not matching regex: #{value.inspect} (#{prop_arr['regex'].inspect})"
|
557
|
+
end
|
558
|
+
raise ArgumentError, 'Too long' if prop_arr['maxlen'] && (value.length > prop_arr['maxlen'].to_i)
|
660
559
|
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
elsif my_prop.has_key?("default") && include_defaults
|
670
|
-
return enforce_types(my_prop, my_prop['default'])
|
671
|
-
else
|
672
|
-
return nil
|
673
|
-
end
|
674
|
-
end
|
560
|
+
value
|
561
|
+
when 'enum'
|
562
|
+
accepted_values = if prop_arr.is_a?(Hash)
|
563
|
+
prop_arr['enum'].map { |x| x['value'] }
|
564
|
+
else
|
565
|
+
prop_arr.map { |x| x['value'] } # is an array if part of value_type 'multiple'
|
566
|
+
end
|
567
|
+
return value if accepted_values.include?(value)
|
675
568
|
|
676
|
-
|
677
|
-
|
569
|
+
raise ArgumentError, "not allowed: #{value.inspect} (not within #{accepted_values.inspect})"
|
570
|
+
when 'float'
|
571
|
+
Float(value)
|
572
|
+
when 'rangedint'
|
573
|
+
number = Integer(value)
|
574
|
+
return number if number >= prop_arr['min'].to_i && number <= prop_arr['max'].to_i
|
678
575
|
|
679
|
-
|
680
|
-
|
681
|
-
|
576
|
+
raise ArgumentError, "not in range #{prop_arr['min']}..#{prop_arr['max']}: #{number}"
|
577
|
+
when 'multiple'
|
578
|
+
prop_arr['multi-types'].each_key do |key|
|
579
|
+
return enforce_type(prop_arr['multi-types'][key], value, value_type: key)
|
580
|
+
rescue StandardError
|
581
|
+
false
|
582
|
+
end
|
583
|
+
raise(ArgumentError, "Nothing matching found for #{value.inspect} (#{prop_arr.inspect})")
|
584
|
+
end
|
585
|
+
end
|
682
586
|
|
683
|
-
|
684
|
-
|
685
|
-
elsif !has_multiple_values?
|
686
|
-
enforce_type(prop_arr, values)
|
687
|
-
else
|
688
|
-
raise(ArgumentError, 'Needs to be Array but is not, or vice versa')
|
689
|
-
end
|
690
|
-
end
|
587
|
+
def xml_builder(xml, full_tree: false, changed_only: true)
|
588
|
+
keys = changed_only ? @values.keys : self.class.props.keys
|
691
589
|
|
692
|
-
|
693
|
-
|
590
|
+
keys.map do |k|
|
591
|
+
next if k.start_with?('@')
|
694
592
|
|
695
|
-
|
696
|
-
|
593
|
+
v = prop_get(k, include_defaults: false)
|
594
|
+
next unless v
|
697
595
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
596
|
+
Array(v).each do |val|
|
597
|
+
val = 'yes' if val == true
|
598
|
+
val = 'no' if val == false
|
599
|
+
xml.method_missing(k, val) # somehow .send does not work with k==:system
|
600
|
+
end
|
601
|
+
end
|
602
|
+
if full_tree
|
603
|
+
@subclasses.each do |k, subclass|
|
604
|
+
if subclass.is_a?(Hash)
|
605
|
+
subclass.each do |k2, subclass2|
|
606
|
+
xml.send(k, k2) do |xml2|
|
607
|
+
subclass2.xml_builder(xml2, full_tree: full_tree, changed_only: changed_only)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
else
|
611
|
+
xml.method_missing(k) do |xml2| # somehow .send does not work with k==:system
|
612
|
+
subclass.xml_builder(xml2, full_tree: full_tree, changed_only: changed_only)
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
xml
|
618
|
+
end
|
710
619
|
|
711
|
-
|
712
|
-
|
620
|
+
def array_class_setter(*args, klass:, section:, &block)
|
621
|
+
# either we have a selector or a block
|
622
|
+
unless (args.length == 1 && !block) || (args.empty? && block)
|
623
|
+
raise(ArgumentError,
|
624
|
+
'wrong number of arguments (expected one argument or block)')
|
625
|
+
end
|
713
626
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
627
|
+
entry = klass.new(parent_instance: self, create_children: @create_children)
|
628
|
+
if block
|
629
|
+
obj = child(section.to_sym).where(PaloAlto.instance_eval(&block))
|
630
|
+
entry.expression = obj.expression
|
631
|
+
entry.arguments = obj.arguments
|
632
|
+
entry
|
633
|
+
else
|
634
|
+
selector = args[0]
|
635
|
+
@subclasses[section] ||= {}
|
722
636
|
|
723
|
-
|
724
|
-
|
725
|
-
|
637
|
+
entry.instance_variable_get('@external_values').merge!({ "@#{selector.keys.first}" => selector.values.first })
|
638
|
+
entry.selector = selector
|
639
|
+
entry.set_xpath_from_selector
|
726
640
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
end
|
641
|
+
if @create_children
|
642
|
+
@subclasses[section][selector] ||= entry
|
643
|
+
else
|
644
|
+
entry
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
735
648
|
|
736
|
-
|
737
|
-
|
649
|
+
def values(full_tree: true, include_defaults: true)
|
650
|
+
h = {}
|
651
|
+
self.class.props.keys.map do |k|
|
652
|
+
prop = prop_get(k, include_defaults: include_defaults)
|
653
|
+
h[k] = prop if prop
|
654
|
+
end
|
655
|
+
if full_tree
|
656
|
+
@subclasses.each do |k, subclass|
|
657
|
+
if subclass.is_a?(Hash)
|
658
|
+
h[k] ||= {}
|
659
|
+
subclass.each do |k2, subclass2|
|
660
|
+
h[k][k2] = subclass2.values(full_tree: true, include_defaults: include_defaults)
|
661
|
+
end
|
662
|
+
else
|
663
|
+
h[k] = subclass.values(full_tree: true, include_defaults: include_defaults)
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
h
|
668
|
+
end
|
738
669
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
xml.source(xpath: source) {
|
743
|
-
members.each{|member| xml.member member}
|
744
|
-
}
|
745
|
-
}
|
746
|
-
xml.send('all-errors', all_errors ? 'yes' : 'no')
|
747
|
-
}
|
748
|
-
}
|
670
|
+
def set_values(h, external: false)
|
671
|
+
h = h.values(include_defaults: false) if h.is_a?(PaloAlto::XML::ConfigClass)
|
672
|
+
raise(ArgumentError, 'needs to be a Hash') unless h.is_a?(Hash)
|
749
673
|
|
750
|
-
|
674
|
+
clear!
|
675
|
+
create!
|
676
|
+
h.each do |k, v|
|
677
|
+
if v.is_a?(Hash)
|
678
|
+
send(k.to_s.gsub('-', '_')).set_values(v, external: external)
|
679
|
+
elsif external
|
680
|
+
@external_values[k] = v
|
681
|
+
else
|
682
|
+
prop_set(k, v)
|
683
|
+
end
|
684
|
+
end
|
685
|
+
self
|
686
|
+
end
|
751
687
|
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
688
|
+
def prop_get(prop, include_defaults: true)
|
689
|
+
my_prop = self.class.props[prop]
|
690
|
+
if @values.key?(prop)
|
691
|
+
@values[prop]
|
692
|
+
elsif @external_values.key?(prop) && @external_values[prop].is_a?(Array)
|
693
|
+
@values[prop] = @external_values[prop].dup
|
694
|
+
elsif @external_values.key?(prop)
|
695
|
+
@external_values[prop]
|
696
|
+
elsif my_prop.key?('default') && include_defaults
|
697
|
+
enforce_types(my_prop, my_prop['default'])
|
698
|
+
end
|
699
|
+
end
|
758
700
|
|
759
|
-
|
760
|
-
|
761
|
-
end
|
701
|
+
def enforce_types(prop_arr, values)
|
702
|
+
return if values.nil?
|
762
703
|
|
763
|
-
|
764
|
-
attr_accessor :selector
|
704
|
+
values = values.split(/\s+/) if has_multiple_values? && values.is_a?(String)
|
765
705
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
payload[:dst] = dst
|
775
|
-
end
|
706
|
+
if values.is_a?(Array) && has_multiple_values?
|
707
|
+
values.map { |v| enforce_type(prop_arr, v) }
|
708
|
+
elsif !has_multiple_values?
|
709
|
+
enforce_type(prop_arr, values)
|
710
|
+
else
|
711
|
+
raise(ArgumentError, 'Needs to be Array but is not, or vice versa')
|
712
|
+
end
|
713
|
+
end
|
776
714
|
|
777
|
-
|
778
|
-
|
715
|
+
def prop_set(prop, value)
|
716
|
+
my_prop = self.class.props[prop] or raise(InternalErrorException,
|
717
|
+
"Unknown attribute for #{self.class}: #{prop}")
|
779
718
|
|
780
|
-
|
781
|
-
|
782
|
-
k, v = selector.first
|
783
|
-
obj = xpath.where(PaloAlto.xpath_attr(k.to_sym) == v)
|
719
|
+
@values[prop] = enforce_types(my_prop, value)
|
720
|
+
end
|
784
721
|
|
785
|
-
|
786
|
-
|
787
|
-
|
722
|
+
def to_xml(changed_only:, full_tree:, include_root:)
|
723
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
724
|
+
xml.send(_section, begin
|
725
|
+
selector
|
726
|
+
rescue StandardError
|
727
|
+
nil
|
728
|
+
end) do
|
729
|
+
xml_builder(xml, changed_only: changed_only, full_tree: full_tree)
|
730
|
+
end
|
731
|
+
end
|
732
|
+
if include_root
|
733
|
+
builder.doc.root.to_xml
|
734
|
+
else
|
735
|
+
builder.doc.root.children.map(&:to_xml).join("\n")
|
736
|
+
end
|
737
|
+
end
|
788
738
|
|
789
|
-
|
790
|
-
|
791
|
-
unless internal_only
|
792
|
-
payload = {
|
793
|
-
type: 'config',
|
794
|
-
action: 'rename',
|
795
|
-
xpath: self.to_xpath,
|
796
|
-
newname: new_name
|
797
|
-
}
|
739
|
+
def push!
|
740
|
+
xml_str = to_xml(changed_only: false, full_tree: true, include_root: true)
|
798
741
|
|
799
|
-
|
800
|
-
|
742
|
+
payload = {
|
743
|
+
type: 'config',
|
744
|
+
action: 'edit',
|
745
|
+
xpath: to_xpath,
|
746
|
+
element: xml_str
|
747
|
+
}
|
748
|
+
XML.execute(payload)
|
749
|
+
end
|
801
750
|
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
751
|
+
def delete_child(name)
|
752
|
+
@subclasses.delete(name) && true || false
|
753
|
+
end
|
754
|
+
|
755
|
+
def delete!
|
756
|
+
payload = {
|
757
|
+
type: 'config',
|
758
|
+
action: 'delete',
|
759
|
+
xpath: to_xpath
|
760
|
+
}
|
761
|
+
XML.execute(payload)
|
762
|
+
end
|
763
|
+
|
764
|
+
def multimove!(dst:, members:, all_errors: false)
|
765
|
+
source = to_xpath
|
766
|
+
|
767
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
768
|
+
xml.root do
|
769
|
+
xml.send('selected-list') do
|
770
|
+
xml.source(xpath: source) do
|
771
|
+
members.each { |member| xml.member member }
|
772
|
+
end
|
773
|
+
end
|
774
|
+
xml.send('all-errors', all_errors ? 'yes' : 'no')
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
element = builder.doc.root.children.map(&:to_xml).join("\n")
|
779
|
+
|
780
|
+
payload = {
|
781
|
+
type: 'config',
|
782
|
+
action: 'multi-move',
|
783
|
+
xpath: dst,
|
784
|
+
element: element
|
785
|
+
}
|
786
|
+
|
787
|
+
XML.execute(payload)
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
class ArrayConfigClass < ConfigClass
|
792
|
+
attr_accessor :selector
|
793
|
+
|
794
|
+
def move!(where:, dst: nil)
|
795
|
+
payload = {
|
796
|
+
type: 'config',
|
797
|
+
action: 'move',
|
798
|
+
xpath: to_xpath,
|
799
|
+
where: where
|
800
|
+
}
|
801
|
+
payload[:dst] = dst if dst
|
802
|
+
|
803
|
+
XML.execute(payload)
|
804
|
+
end
|
805
|
+
|
806
|
+
def set_xpath_from_selector(selector: @selector)
|
807
|
+
xpath = parent_instance.child(_section)
|
808
|
+
k, v = selector.first
|
809
|
+
obj = xpath.where(PaloAlto.xpath_attr(k.to_sym) == v)
|
810
|
+
|
811
|
+
@expression = obj.expression
|
812
|
+
@arguments = obj.arguments
|
813
|
+
end
|
814
|
+
|
815
|
+
def rename!(new_name, internal_only: false)
|
816
|
+
# https://docs.paloaltonetworks.com/pan-os/10-1/pan-os-panorama-api/pan-os-xml-api-request-types/configuration-api/rename-configuration.html
|
817
|
+
unless internal_only
|
818
|
+
payload = {
|
819
|
+
type: 'config',
|
820
|
+
action: 'rename',
|
821
|
+
xpath: to_xpath,
|
822
|
+
newname: new_name
|
823
|
+
}
|
824
|
+
|
825
|
+
XML.execute(payload)
|
826
|
+
end
|
827
|
+
|
828
|
+
# now update also the internal value to the new name
|
829
|
+
selector.transform_values! { new_name }
|
830
|
+
@external_values["@#{selector.keys.first}"] = new_name
|
831
|
+
set_xpath_from_selector
|
832
|
+
end
|
833
|
+
end
|
808
834
|
|
809
|
-
|
835
|
+
# no end of class Xml here as generated source will be added here
|
810
836
|
class Config < ConfigClass
|
811
837
|
def has_multiple_values?
|
812
838
|
false
|