palo_alto 0.1.2 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8a6152aa6eb0aa5b5a8ba4e6a31149b45ed06646ac2cbd12907e390ecfc4812
4
- data.tar.gz: f1b8cc362bcad0902ddbdbc4eb38db276cabeee8b4cbc9155003f3e86a4b7ca1
3
+ metadata.gz: e727fd699e499c9c7cdf0f445f1f83dc6b39721595060eb40f8ca686742f76fe
4
+ data.tar.gz: 538f05942d1bebd82c67c79749cf4cca51c57d3f56d6acbe6c8765eccd0abaca
5
5
  SHA512:
6
- metadata.gz: 5813d1df939b84e47cb15138d82442f153e24165d0fc9efd50df726922b0f7bb9aa88d7266ec9523d84fe2bd9debae8a840967e4867c3039f04da57f91939037
7
- data.tar.gz: '09a3c66024510102688e1fa27dc3b4bebecdab9b3e7511c2fc5669eb6e50ecbad8e2fe82aed5e2b6c8e6da58b9a39c44dbbb93b5c36151927084a3db1d380cae'
6
+ metadata.gz: 6e02483ec9f3e9860d2a248b043e30f8da48a89c7385feee667fffdea51abfaced43db81a487ba2bdf79b0c396310baf65f0b30c2132a7e1486e9e5ba4fc09f8
7
+ data.tar.gz: 1be23db7b31ed050c9fea102f2ba3007b916572a0f1b6d9b25e94bc1ebb09b923a6f938bc31c9b165c057cc8af984aa245266de71afb5cb20ac2890b167443d2
data/README.md CHANGED
@@ -0,0 +1,3 @@
1
+ Works for me :)
2
+
3
+ You can find examples on how to use this module in the examples/ directory
@@ -0,0 +1,4 @@
1
+ require 'palo_alto'
2
+
3
+ xml = PaloAlto::XML.new(host: "panorama-test", port: "443", username: "admin", password: "Admin123!", debug: [:sent, :received, :statistics])
4
+
@@ -0,0 +1,50 @@
1
+ require 'palo_alto'
2
+ require "byebug"
3
+
4
+ xml = PaloAlto::XML.new(host: "panorama-test", port: "443", username: "admin", password: "Admin123!", debug: [:sent, :received, :statistics])
5
+
6
+ #rules=xml.config.devices.entry(name:'localhost.localdomain').device_group.entry(name: 'PLAYGROUND').pre_rulebase.security.rules.entry{(child(:source).child(:member).text=="VPN_Net_10.1.1.0-24").or(child(:destination).child(:member).text == 'VPN_Net_10.1.1.0-24')}.get_all
7
+
8
+ rules=xml.config.devices.entry(name:'localhost.localdomain').device_group.entry(name: 'PLAYGROUND').pre_rulebase.security.rules.entry{}.get_all
9
+
10
+ pp rules
11
+ pp rules.length
12
+
13
+
14
+
15
+
16
+ tag_name='vpn:test'
17
+
18
+ tag = xml.config.devices.entry(name:'localhost.localdomain').device_group.entry(name: dg).tag.entry(name:tag_name).create!
19
+ tag.color = "color23"
20
+ tag.push!
21
+
22
+
23
+
24
+ dg='PLAYGROUND'
25
+ rules=xml.config.devices.entry(name:'localhost.localdomain').device_group.entry(name: dg).pre_rulebase.security.rules.entry{}.get_all
26
+ rules.reject!{|rule| rule.api_attributes['loc'] != dg}
27
+
28
+ pp rules.first.api_attributes # attributes like uuid and loc
29
+ pp rules.first.values()
30
+
31
+ r = rules.first
32
+ r.tag.member = [tag.name]
33
+ r.group_tag = tag.name
34
+ r.description += "...."
35
+ r.push!
36
+
37
+ puts r.to_xpath
38
+ r.rename!("Test 1")
39
+ puts r.to_xpath
40
+ pp r.name
41
+
42
+ exit 0
43
+
44
+ # create a new template with persisted subclasses
45
+ new_template = xml.config.devices.entry(name:'localhost.localdomain').template.entry(name: 'testtemplate').create!
46
+ new_template.push!
47
+
48
+
49
+
50
+
@@ -0,0 +1,27 @@
1
+ require 'palo_alto'
2
+
3
+ xml = PaloAlto::XML.new(host: "panorama-test", port: "443", username: "admin", password: "Admin123!", debug: [:statistics, :warnings, :_sent, :_received])
4
+
5
+ query = "( full-path contains '/config/devices/entry[@name=\\'localhost.localdomain\\']/device-group/entry[@name=\\'gr\\']/address/entry[@name=\\'Blah_19\\']' )"
6
+ l=xml.log(query: query, log_type: 'config', nlogs: 50, show_detail: true, days: nil)
7
+
8
+ pp l.count
9
+ x = l.first
10
+ pp x
11
+
12
+
13
+ #################
14
+
15
+
16
+
17
+ ritm = 'RITM1234567'
18
+
19
+ def quote_string(v)
20
+ "'" + v.to_s.gsub(/'/, "\\\\'") + "'"
21
+ end
22
+
23
+ query = "( ( cmd eq edit ) or ( cmd eq audit-commit ) ) and ( comment contains #{quote_string(ritm)} )"
24
+
25
+ l=xml.log(query: query, log_type: 'config', nlogs: 50, show_detail: true, days: nil)
26
+
27
+ pp l.count
@@ -0,0 +1,127 @@
1
+ require 'palo_alto'
2
+
3
+ a= {commit: { partial:[
4
+ {'admin': ['admin']},
5
+ 'no-template',
6
+ 'no-template-stack',
7
+ 'no-log-collector',
8
+ 'no-log-collector-group',
9
+ 'no-wildfire-appliance',
10
+ 'no-wildfire-appliance-cluster',
11
+ {'device-and-network': 'excluded'},
12
+ {'shared-object': 'excluded'}
13
+ ]}}
14
+
15
+ b= { show: {devices: 'all' } }
16
+
17
+ c = {revert: { config: {
18
+ partial:[
19
+ {'admin': ['admin']},
20
+ 'no-template',
21
+ 'no-template-stack',
22
+ 'no-log-collector',
23
+ 'no-log-collector-group',
24
+ 'no-wildfire-appliance',
25
+ 'no-wildfire-appliance-cluster',
26
+ {'device-and-network': 'excluded'},
27
+ {'shared-object': 'excluded'}
28
+ ]}}}
29
+
30
+ d = {commit: nil}
31
+
32
+ e = 'commit'
33
+
34
+ f = {revert: 'config'}
35
+
36
+ g= {show: 'templates'}
37
+
38
+ h= {show: 'devicegroups'}
39
+
40
+ j={show: {jobs: {id: 12431}}}
41
+
42
+ k={check: 'full-commit-required'}
43
+
44
+ push_to_device={ 'commit-all': { 'shared-policy': { 'device-group': [{name:'TEST-DG'}]}}}
45
+
46
+ #validate:
47
+ p={ 'commit-all':
48
+ {
49
+ 'shared-policy': [
50
+ {'device-group': [{name:'PLAYGROUND'}]},
51
+ {'include-template':'yes'},
52
+ {'merge-with-candidate-cfg':'yes'},
53
+ {'force-template-values':'no'},
54
+ {'validate-only':'yes'}
55
+ ]
56
+ }
57
+ }
58
+
59
+ i = {show: {query: {result: {id: 10438 }}}}
60
+
61
+
62
+ # hit counts:
63
+ device_group = 'PLAYGROUND'
64
+
65
+ l = {
66
+ show: {
67
+ 'rule-hit-count': [{
68
+ 'device-group': [{
69
+ entry: [{
70
+ name: device_group
71
+ }, {
72
+ "pre-rulebase": [{
73
+ entry: [{
74
+ name: 'security'
75
+ }, {
76
+ 'rules': 'all'
77
+ }]
78
+ }]
79
+ }]
80
+ }]
81
+ }]
82
+ }
83
+ }
84
+
85
+ # hit count for one rule, with more details:
86
+ rule_name = "Rule 27"
87
+ l = {
88
+ show: {
89
+ 'rule-hit-count': [{
90
+ 'device-group': [{
91
+ entry: [{
92
+ name: device_group
93
+ }, {
94
+ "pre-rulebase": [{
95
+ entry: [{
96
+ name: 'security'
97
+ }, {
98
+ 'rules': {
99
+ "rule-name": [{
100
+ entry: [{
101
+ name: rule_name
102
+ }]
103
+ }]
104
+ }
105
+ }]
106
+ }]
107
+ }]
108
+ }]
109
+ }]
110
+ }
111
+ }
112
+
113
+
114
+ xml = PaloAlto::XML.new(host: "panorama-test", port: "443", username: "admin", password: "Admin123!", debug: [:sent, :received])
115
+
116
+ #pp xml.op.execute(a)
117
+ #pp xml.op.execute(b)
118
+ #pp xml.op.execute(c)
119
+ pp xml.op.execute(d)
120
+ puts "---------------------------"
121
+ pp xml.op.execute(e)
122
+ puts "---------------------------"
123
+
124
+ #pp xml.op.execute(f)
125
+
126
+ pp xml.op.execute(k)
127
+
@@ -1,4 +1,4 @@
1
- # generated: 2021-08-27 17:33:49 +0200
1
+ # generated: 2021-10-19 00:56:02 +0200
2
2
  require 'openssl'
3
3
  require 'nokogiri'
4
4
 
@@ -425,7 +425,7 @@ module PaloAlto
425
425
  start_time=Time.now
426
426
  result = self.parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first)
427
427
  if XML.debug.include?(:statistics)
428
- puts "Elapsed for parsing #{result.length} results: #{Time.now-start_time} seconds"
428
+ warn "Elapsed for parsing #{result.length} results: #{Time.now-start_time} seconds"
429
429
  end
430
430
  result
431
431
  end
@@ -436,7 +436,7 @@ module PaloAlto
436
436
  self
437
437
  end
438
438
 
439
- def get(ignore_empty_result: false, xpath: self.to_xpath)
439
+ def get(ignore_empty_result: false, xpath: self.to_xpath, return_only: false)
440
440
  if self.class.superclass == ArrayConfigClass && !@selector
441
441
  raise(InvalidCommandException, "Please use 'get_all' here")
442
442
  end
@@ -454,23 +454,28 @@ module PaloAlto
454
454
  if ignore_empty_result==false
455
455
  raise(ObjectNotPresentException, "empty result: #{payload.inspect}")
456
456
  end
457
+ end
458
+
459
+ if return_only
460
+ data.xpath('//response/result/*')
457
461
  else
458
- #self.parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first).first
459
462
  @create_children=true
460
463
  n = data.xpath('//response/result/*')
464
+ if n.any?
465
+ clear!
466
+ external_set(n.first)
461
467
 
462
- clear!
463
- external_set(n.first)
464
-
465
- if is_a?(ArrayConfigClass)
466
- primary_key = get_primary_key(n.first.attribute_nodes, self.class.props)
467
- set_array_class_attributes(n.first, primary_key) # primary key, api_attributes
468
+ if is_a?(ArrayConfigClass)
469
+ primary_key = get_primary_key(n.first.attribute_nodes, self.class.props)
470
+ set_array_class_attributes(n.first, primary_key) # primary key, api_attributes
471
+ end
472
+ end
473
+ self
474
+ end.tap do
475
+ if XML.debug.include?(:statistics)
476
+ warn "Elapsed for parsing: #{Time.now-start_time} seconds"
468
477
  end
469
478
  end
470
- if XML.debug.include?(:statistics)
471
- puts "Elapsed for parsing: #{Time.now-start_time} seconds"
472
- end
473
- self
474
479
  end
475
480
 
476
481
  def get_primary_key(attribute_nodes, props)
@@ -526,9 +531,9 @@ module PaloAlto
526
531
  when 'bool'
527
532
  return true if ['yes', true].include?(value)
528
533
  return false if ['no', false].include?(value)
529
- raise ArgumentError, 'Not bool: ' + value.inspect
534
+ raise ArgumentError, "Not bool: #{value.inspect}"
530
535
  when 'string', 'ipdiscontmask', 'iprangespec', 'ipspec', 'rangelistspec'
531
- raise(ArgumentError, 'Not string') unless value.is_a?(String)
536
+ raise(ArgumentError, "Not string: #{value.inspect}") unless value.is_a?(String)
532
537
  if prop_arr['regex']
533
538
  raise ArgumentError, "Not matching regex: #{value.inspect} (#{prop_arr["regex"].inspect})" unless value.match(prop_arr["regex"])
534
539
  end
@@ -662,28 +667,34 @@ module PaloAlto
662
667
  elsif @external_values.has_key?(prop)
663
668
  return @external_values[prop]
664
669
  elsif my_prop.has_key?("default") && include_defaults
665
- return enforce_type(my_prop, my_prop['default'])
670
+ return enforce_types(my_prop, my_prop['default'])
666
671
  else
667
672
  return nil
668
673
  end
669
674
  end
670
675
 
671
- def prop_set(prop, value)
672
- my_prop = self.class.props[prop] or raise(InternalErrorException, "Unknown attribute for #{self.class}: #{prop}")
676
+ def enforce_types(prop_arr, values)
677
+ return if values.nil?
673
678
 
674
- if has_multiple_values? && value.is_a?(String)
675
- value = value.split(/\s+/)
679
+ if has_multiple_values? && values.is_a?(String)
680
+ values = values.split(/\s+/)
676
681
  end
677
682
 
678
- if value.is_a?(Array)
679
- @values[prop] = value.map{|v| enforce_type(my_prop, v)}
680
- elsif value.nil?
681
- @values[prop] = nil
683
+ if values.is_a?(Array) && has_multiple_values?
684
+ values.map{|v| enforce_type(prop_arr, v)}
685
+ elsif !has_multiple_values?
686
+ enforce_type(prop_arr, values)
682
687
  else
683
- @values[prop] = enforce_type(my_prop, value)
688
+ raise(ArgumentError, 'Needs to be Array but is not, or vice versa')
684
689
  end
685
690
  end
686
691
 
692
+ def prop_set(prop, value)
693
+ my_prop = self.class.props[prop] or raise(InternalErrorException, "Unknown attribute for #{self.class}: #{prop}")
694
+
695
+ @values[prop] = enforce_types(my_prop, value)
696
+ end
697
+
687
698
  def to_xml(changed_only:, full_tree:, include_root: )
688
699
  builder = Nokogiri::XML::Builder.new{|xml|
689
700
  xml.send(self._section, (self.selector rescue nil)) {
@@ -768,23 +779,25 @@ module PaloAlto
768
779
 
769
780
  def set_xpath_from_selector(selector: @selector)
770
781
  xpath = self.parent_instance.child(_section)
771
- k,v=selector.first
782
+ k, v = selector.first
772
783
  obj = xpath.where(PaloAlto.xpath_attr(k.to_sym) == v)
773
784
 
774
785
  @expression = obj.expression
775
786
  @arguments = obj.arguments
776
787
  end
777
788
 
778
- def rename!(new_name)
789
+ def rename!(new_name, internal_only: false)
779
790
  # https://docs.paloaltonetworks.com/pan-os/10-1/pan-os-panorama-api/pan-os-xml-api-request-types/configuration-api/rename-configuration.html
780
- payload = {
781
- type: 'config',
782
- action: 'rename',
783
- xpath: self.to_xpath,
784
- newname: new_name
785
- }
791
+ unless internal_only
792
+ payload = {
793
+ type: 'config',
794
+ action: 'rename',
795
+ xpath: self.to_xpath,
796
+ newname: new_name
797
+ }
786
798
 
787
- result = XML.execute(payload)
799
+ result = XML.execute(payload)
800
+ end
788
801
 
789
802
  # now update also the internal value to the new name
790
803
  self.selector.transform_values!{new_name}
data/lib/palo_alto/log.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "pp"
2
-
3
1
  module PaloAlto
4
2
  class XML
5
3
 
@@ -23,7 +21,6 @@ module PaloAlto
23
21
  @count=nil
24
22
  @skip=0
25
23
  @first_result = fetch_result
26
- #pp @current_result
27
24
  super
28
25
  end
29
26
 
data/lib/palo_alto/op.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require "nokogiri"
2
- require "pp"
3
2
 
4
3
  module PaloAlto
5
4
  class XML
@@ -49,9 +48,7 @@ module PaloAlto
49
48
  section = obj.keys.first
50
49
  data = obj[section]
51
50
  else
52
- puts "----------"
53
- pp obj
54
- raise
51
+ raise obj.pretty_inspect
55
52
  end
56
53
 
57
54
  unless ops.has_key?(section.to_s)
@@ -60,9 +57,6 @@ module PaloAlto
60
57
  end
61
58
 
62
59
  ops_tree = ops[section.to_s]
63
- #pp [:ops, ops_tree]
64
- #pp [:obj, obj]
65
- #puts "****************** build #{section} (#{ops_tree[:obj]})"
66
60
 
67
61
  section = escape_xpath_tag(section)
68
62
 
@@ -104,8 +98,7 @@ module PaloAlto
104
98
  xml_builder(xml, ops_tree, v)
105
99
  }
106
100
  else
107
- pp ops_tree[:obj]
108
- raise
101
+ raise ops_tree[:obj].pretty_inspect
109
102
  end
110
103
  xml
111
104
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PaloAlto
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.6'
5
5
  end
data/lib/palo_alto.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'openssl'
4
4
  require 'nokogiri'
5
+ require 'net/http'
6
+ require 'pp'
5
7
 
6
8
  require_relative 'palo_alto/version'
7
9
 
@@ -10,7 +12,6 @@ require_relative 'palo_alto/log'
10
12
  require_relative 'palo_alto/op'
11
13
 
12
14
  module PaloAlto
13
-
14
15
  class PermanentException < StandardError
15
16
  end
16
17
 
@@ -212,12 +213,15 @@ module PaloAlto
212
213
  end
213
214
  end
214
215
 
215
- def commit!(all: false)
216
+ def commit!(all: false, device_groups: nil, wait_for_completion: true)
217
+ return nil if device_groups.is_a?(Array) && device_groups.empty?
218
+
216
219
  op = if all
217
220
  'commit'
218
221
  else
219
222
  { commit: { partial: [
220
223
  { 'admin': [XML.username] },
224
+ device_groups ? {'device-group': device_groups } : nil,
221
225
  'no-template',
222
226
  'no-template-stack',
223
227
  'no-log-collector',
@@ -226,9 +230,39 @@ module PaloAlto
226
230
  'no-wildfire-appliance-cluster',
227
231
  { 'device-and-network': 'excluded' },
228
232
  { 'shared-object': 'excluded' }
229
- ] } }
233
+ ].compact } }
230
234
  end
231
- Op.new.execute(op)
235
+ Op.new.execute(op).tap do |result|
236
+ if wait_for_completion
237
+ job_id = result.at_xpath('response/result/job')&.text
238
+ wait_for_job_completion(job_id) if job_id
239
+ end
240
+ end
241
+ end
242
+
243
+ def full_commit_required?
244
+ result = Op.new.execute({check: 'full-commit-required'})
245
+ return true unless result.at_xpath('response/result').text == 'no'
246
+
247
+ false
248
+ end
249
+
250
+ def check_for_changes(usernames: [XML.username])
251
+ result = Op.new.execute({show: {config: {list: {'change-summary': {partial: {admin: usernames}}}}}})
252
+ result.xpath('response/result/summary/device-group/member').map(&:inner_text)
253
+ end
254
+
255
+ def wait_for_job_completion(job_id, wait: 5, timeout: 300)
256
+ cmd = {show: {jobs: {id: job_id}}}
257
+ start = Time.now
258
+ begin
259
+ result = Op.new.execute(cmd)
260
+ unless result.at_xpath('response/result/job/status')&.text=='ACT'
261
+ return result
262
+ end
263
+ sleep wait
264
+ end while start+timeout > Time.now
265
+ return false
232
266
  end
233
267
 
234
268
  def revert!(all: false)
data/palo_alto.gemspec CHANGED
@@ -14,8 +14,8 @@ Gem::Specification.new do |spec|
14
14
  spec.required_ruby_version = '>= 2.7.0'
15
15
 
16
16
  spec.metadata['homepage_uri'] = spec.homepage
17
- spec.metadata["source_code_uri"] = 'https://github.com/Sebbb/palo_alto'
18
- spec.metadata["changelog_uri"] = 'https://github.com/Sebbb/palo_alto/blob/main/README.md'
17
+ spec.metadata["source_code_uri"] = 'https://github.com/Sebbb/palo_alto/'
18
+ spec.metadata["changelog_uri"] = 'https://github.com/Sebbb/palo_alto/blob/main/CHANGELOG.md'
19
19
 
20
20
  # Specify which files should be added to the gem when it is released.
21
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: palo_alto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Roesner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-08 00:00:00.000000000 Z
11
+ date: 2021-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -37,6 +37,10 @@ files:
37
37
  - LICENSE.txt
38
38
  - README.md
39
39
  - Rakefile
40
+ - examples/connecttest.rb
41
+ - examples/test_config.rb
42
+ - examples/test_log.rb
43
+ - examples/test_op.rb
40
44
  - lib/palo_alto.rb
41
45
  - lib/palo_alto/config.rb
42
46
  - lib/palo_alto/log.rb
@@ -48,8 +52,8 @@ licenses:
48
52
  - artistic-2.0
49
53
  metadata:
50
54
  homepage_uri: https://github.com/Sebbb/
51
- source_code_uri: https://github.com/Sebbb/palo_alto
52
- changelog_uri: https://github.com/Sebbb/palo_alto/blob/main/README.md
55
+ source_code_uri: https://github.com/Sebbb/palo_alto/
56
+ changelog_uri: https://github.com/Sebbb/palo_alto/blob/main/CHANGELOG.md
53
57
  post_install_message:
54
58
  rdoc_options: []
55
59
  require_paths: