class PaloAlto::XML::ConfigClass
Attributes
props[RW]
api_attributes[R]
force_relative[RW]
parent_instance[RW]
subclasses[R]
Public Class Methods
new(parent_instance:, create_children: false)
click to toggle source
# File lib/palo_alto/config.rb, line 385 def initialize(parent_instance:, create_children: false) @parent_instance = parent_instance @subclasses = {} @values = {} @create_children = create_children @external_values = {} # data we received and don't need to set again @api_attributes={} @expression = :child unless self.is_a?(ArrayConfigClass) # for ArrayConfigClass, it will be set externally after the constructor xpath_argument = @parent_instance @arguments = [ xpath_argument, [_section] ] end end
Public Instance Methods
array_class_setter(*args, klass:, section:, &block)
click to toggle source
# File lib/palo_alto/config.rb, line 588 def array_class_setter(*args, klass:, section:, &block) # either we have a selector or a block raise(ArgumentError, "wrong number of arguments (expected one argument or block)") unless (args.length==1 && !block) || (args.empty? && block) entry = klass.new(parent_instance: self, create_children: @create_children) if block obj = self.child(section.to_sym).where(PaloAlto.instance_eval(&block)) entry.expression = obj.expression entry.arguments = obj.arguments entry else selector=args[0] @subclasses[section]||= {} entry.instance_variable_get('@external_values').merge!({"@#{selector.keys.first}" => selector.values.first}) entry.selector = selector entry.set_xpath_from_selector if @create_children @subclasses[section][selector] ||= entry else entry end end end
clear!()
click to toggle source
# File lib/palo_alto/config.rb, line 433 def clear! @subclasses = {} @values = {} self end
create!()
click to toggle source
# File lib/palo_alto/config.rb, line 404 def create! @create_children = true self end
delete!()
click to toggle source
# File lib/palo_alto/config.rb, line 716 def delete! payload = { type: 'config', action: 'delete', xpath: self.to_xpath } XML.execute(payload) end
delete_child(name)
click to toggle source
# File lib/palo_alto/config.rb, line 712 def delete_child(name) @subclasses.delete(name) && true || false end
enforce_type(prop_arr, value, value_type: prop_arr['type'])
click to toggle source
# File lib/palo_alto/config.rb, line 524 def enforce_type(prop_arr, value, value_type: prop_arr['type']) case value_type when 'bool' return true if ['yes', true].include?(value) return false if ['no', false].include?(value) raise ArgumentError, 'Not bool: ' + value.inspect when 'string', 'ipdiscontmask', 'iprangespec', 'ipspec', 'rangelistspec' raise(ArgumentError, 'Not string') unless value.is_a?(String) if prop_arr['regex'] raise ArgumentError, "Not matching regex: #{value.inspect} (#{prop_arr["regex"].inspect})" unless value.match(prop_arr["regex"]) end if prop_arr['maxlen'] raise ArgumentError, 'Too long' if value.length > prop_arr['maxlen'].to_i end return value when 'enum' 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' return value if accepted_values.include?(value) raise ArgumentError, "not allowed: #{value.inspect} (not within #{accepted_values.inspect})" when 'float' return Float(value) when 'rangedint' number = Integer(value) return number if number >= prop_arr['min'].to_i && number <= prop_arr['max'].to_i raise ArgumentError, "not in range #{prop_arr['min']}..#{prop_arr['max']}: #{number}" when 'multiple' prop_arr['multi-types'].keys.each{|key| return enforce_type(prop_arr['multi-types'][key], value, value_type: key) rescue false } raise(ArgumentError, "Nothing matching found for #{value.inspect} (#{prop_arr.inspect})") end end
external_set(data)
click to toggle source
# File lib/palo_alto/config.rb, line 493 def external_set(data) data.element_children.map{|child| child.name.match(/\A[a-zA-Z0-9_-]*\z/) or raise 'invalid character' if prop = self.class.props[child.name] if has_multiple_values? @external_values[child.name]||=[] @external_values[child.name] << enforce_type(prop, child.text) else @external_values[child.name] = enforce_type(prop, child.text) end elsif new_class=eval('self.class::' + child.name.capitalize.gsub(/-(.)/) {|e| $1.upcase}) rescue false # check for class name in camelcase format if new_class.superclass == ConfigClass subclass = self.send(child.name.gsub('-','_')) elsif new_class.superclass == ArrayConfigClass primary_key = get_primary_key(child.attribute_nodes, new_class.props) subclass = self.send(child.name, primary_key) # create subclass subclass.set_array_class_attributes(child, primary_key) # primary key, api_attributes else raise end subclass.external_set(child) else raise "unknown key: #{child.name}" end subclass } end
get(ignore_empty_result: false, xpath: self.to_xpath)
click to toggle source
# File lib/palo_alto/config.rb, line 439 def get(ignore_empty_result: false, xpath: self.to_xpath) if self.class.superclass == ArrayConfigClass && !@selector raise(InvalidCommandException, "Please use 'get_all' here") end payload = { type: 'config', action: 'get', xpath: xpath } data = XML.execute(payload) start_time=Time.now if data.xpath('//response/result/*').length != 1 if ignore_empty_result==false raise(ObjectNotPresentException, "empty result: #{payload.inspect}") end else #self.parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first).first @create_children=true n = data.xpath('//response/result/*') clear! external_set(n.first) if is_a?(ArrayConfigClass) primary_key = get_primary_key(n.first.attribute_nodes, self.class.props) set_array_class_attributes(n.first, primary_key) # primary key, api_attributes end end if XML.debug.include?(:statistics) puts "Elapsed for parsing: #{Time.now-start_time} seconds" end self end
get_all()
click to toggle source
# File lib/palo_alto/config.rb, line 416 def get_all raise(InvalidCommandException, "please use 'get' here") if self.class.superclass != ArrayConfigClass payload = { type: "config", action: "get", xpath: self.to_xpath } data = XML.execute(payload) start_time=Time.now result = self.parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first) if XML.debug.include?(:statistics) puts "Elapsed for parsing #{result.length} results: #{Time.now-start_time} seconds" end result end
get_primary_key(attribute_nodes, props)
click to toggle source
# File lib/palo_alto/config.rb, line 476 def get_primary_key(attribute_nodes, props) primary_key_attr = attribute_nodes.find{|attr| props.keys.find{|k| k=="@#{attr.name}"} } Hash[primary_key_attr.name.to_sym, primary_key_attr.value] end
inspect()
click to toggle source
# File lib/palo_alto/config.rb, line 412 def inspect self.to_s[0..-1] + ' ' + self.values(full_tree: false).map{|k,v| "#{k}: #{v.inspect}"}.join(", ") + ">" end
multimove!(dst:, members:, all_errors: false)
click to toggle source
# File lib/palo_alto/config.rb, line 725 def multimove!(dst:, members:, all_errors: false) source = self.to_xpath builder = Nokogiri::XML::Builder.new{|xml| xml.root { xml.send('selected-list') { xml.source(xpath: source) { members.each{|member| xml.member member} } } xml.send('all-errors', all_errors ? 'yes' : 'no') } } element = builder.doc.root.children.map(&:to_xml).join("\n") payload = { type: 'config', action: 'multi-move', xpath: dst, element: element } XML.execute(payload) end
prop_get(prop, include_defaults: true)
click to toggle source
# File lib/palo_alto/config.rb, line 656 def prop_get(prop, include_defaults: true) my_prop = self.class.props[prop] if @values.has_key?(prop) return @values[prop] elsif @external_values.has_key?(prop) && @external_values[prop].is_a?(Array) return @values[prop] = @external_values[prop].dup elsif @external_values.has_key?(prop) return @external_values[prop] elsif my_prop.has_key?("default") && include_defaults return enforce_type(my_prop, my_prop['default']) else return nil end end
prop_set(prop, value)
click to toggle source
# File lib/palo_alto/config.rb, line 671 def prop_set(prop, value) my_prop = self.class.props[prop] or raise(InternalErrorException, "Unknown attribute for #{self.class}: #{prop}") if has_multiple_values? && value.is_a?(String) value = value.split(/\s+/) end if value.is_a?(Array) @values[prop] = value.map{|v| enforce_type(my_prop, v)} elsif value.nil? @values[prop] = nil else @values[prop] = enforce_type(my_prop, value) end end
push!()
click to toggle source
# File lib/palo_alto/config.rb, line 700 def push! xml_str = self.to_xml(changed_only: false, full_tree: true, include_root: true) payload = { type: 'config', action: 'edit', xpath: self.to_xpath, element: xml_str } XML.execute(payload) end
set_array_class_attributes(child, primary_key)
click to toggle source
# File lib/palo_alto/config.rb, line 483 def set_array_class_attributes(child, primary_key) @external_values.merge!({ '@' + primary_key.keys.first.to_s => primary_key.values.first}) # save also the other attributes like loc and uuid, if set child.attribute_nodes.each{|attr| next if attr.name.to_sym == primary_key.keys.first api_attributes[attr.name] = attr.value } end
set_values(h, external: false)
click to toggle source
# File lib/palo_alto/config.rb, line 635 def set_values(h, external: false) if h.is_a?(PaloAlto::XML::ConfigClass) h=h.values(include_defaults: false) end raise(ArgumentError, 'needs to be a Hash') unless h.is_a?(Hash) clear! create! h.each{|k,v| if v.is_a?(Hash) self.send(k.to_s.gsub('-','_')).set_values(v, external: external) else if external @external_values[k]=v else self.prop_set(k,v) end end } self end
to_xml(changed_only:, full_tree:, include_root: )
click to toggle source
# File lib/palo_alto/config.rb, line 687 def to_xml(changed_only:, full_tree:, include_root: ) builder = Nokogiri::XML::Builder.new{|xml| xml.send(self._section, (self.selector rescue nil)) { self.xml_builder(xml, changed_only: changed_only, full_tree: full_tree) } } if include_root builder.doc.root.to_xml else builder.doc.root.children.map(&:to_xml).join("\n") end end
values(full_tree: true, include_defaults: true)
click to toggle source
# File lib/palo_alto/config.rb, line 614 def values(full_tree: true, include_defaults: true) h={} self.class.props.keys.map{|k| prop = prop_get(k, include_defaults: include_defaults) h[k] = prop if prop } if full_tree @subclasses.each{|k,subclass| if subclass.is_a?(Hash) h[k]||={} subclass.each{|k2, subclass2| h[k][k2]=subclass2.values(full_tree: true, include_defaults: include_defaults) } else h[k]=subclass.values(full_tree: true, include_defaults: include_defaults) end } end return h end
xml_builder(xml, full_tree: false, changed_only: true)
click to toggle source
# File lib/palo_alto/config.rb, line 557 def xml_builder(xml, full_tree: false, changed_only: true) keys = changed_only ? @values.keys : self.class.props.keys keys.map{|k| next if k.start_with?('@') v=prop_get(k, include_defaults: false) next unless v Array(v).each{|val| val='yes' if val==true val='no' if val==false xml.method_missing(k, val) # somehow .send does not work with k==:system } } if full_tree @subclasses.each{|k,subclass| if subclass.is_a?(Hash) subclass.each{|k2, subclass2| xml.send(k, k2){|xml| subclass2.xml_builder(xml, full_tree: full_tree, changed_only: changed_only) } } else xml.method_missing(k){|xml| # somehow .send does not work with k==:system subclass.xml_builder(xml, full_tree: full_tree, changed_only: changed_only) } end } end return xml end