Class: Cisco::Yang

Inherits:
Object
  • Object
show all
Defined in:
lib/cisco_node_utils/yang.rb

Overview

class Cisco::Yang Utility class mainly containing methods to compare YANG configurations in order to determine if the running config is in-sync with the desired config.

Class Method Summary collapse

Class Method Details

.array_equiv?(op, should, is) ⇒ Boolean

Returns:

  • (Boolean)

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/cisco_node_utils/yang.rb', line 112

def self.array_equiv?(op, should, is)
  n = should.length
  loop = lambda do|i|
    if i == n
      if op == :replace
        is.length == should.length
      else
        true
      end
    else
      should_elt = should[i]
      is_elt = is.find do |elt|
        sub_elt(op, should_elt, elt)
      end
      if is_elt.nil? && !nil_array(should_elt)
        should_elt.nil?
      else
        loop.call(i + 1)
      end
    end
  end
  loop.call(0)
end

.empty?(yang) ⇒ Boolean

Is the specified yang string empty?

Returns:

  • (Boolean)

26
27
28
# File 'lib/cisco_node_utils/yang.rb', line 26

def self.empty?(yang)
  !yang || yang.empty?
end

.hash_equiv?(op, should, is) ⇒ Boolean

Returns:

  • (Boolean)

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/cisco_node_utils/yang.rb', line 136

def self.hash_equiv?(op, should, is)
  keys = should.keys
  n = keys.length
  loop = lambda do|i|
    if i == n
      if op == :replace
        is.keys.length == should.keys.length
      else
        true
      end
    else
      k = keys[i]
      is_v = is[k]
      should_v = should[k]
      if is_v.nil? && !nil_array(should_v)
        false
      else
        sub_elt(op, should_v, is_v) && loop.call(i + 1)
      end
    end
  end
  loop.call(0)
end

.insync_for_merge?(should, is) ⇒ Boolean

Given target and current YANG configurations, returns true if the configurations are in-sync, relative to a “merge_config” action

Parameters:

  • should (String)

    the should configuration in YANG JASON format

  • is (String)

    the is configuration in YANG JASON format

Returns:

  • (Boolean)

34
35
36
37
38
39
# File 'lib/cisco_node_utils/yang.rb', line 34

def self.insync_for_merge?(should, is)
  should_hash = self.empty?(should) ? {} : JSON.parse(should)
  is_hash = self.empty?(is) ? {} : JSON.parse(is)

  !needs_something?(:merge, should_hash, is_hash)
end

.insync_for_replace?(should, is) ⇒ Boolean

Given a is and should YANG configuration, returns true if the configuration are in-sync, relative to a “replace_config” action

Parameters:

  • should (String)

    the should configuration in YANG JASON format

  • is (String)

    the is configuration in YANG JASON format

Returns:

  • (Boolean)

45
46
47
48
49
50
# File 'lib/cisco_node_utils/yang.rb', line 45

def self.insync_for_replace?(should, is)
  should_hash = self.empty?(should) ? {} : JSON.parse(should)
  is_hash = self.empty?(is) ? {} : JSON.parse(is)

  !needs_something?(:replace, should_hash, is_hash)
end

.needs_something?(op, should, is) ⇒ Boolean

usage:

needs_something?(op, should, run)

op     - symbol - If value is not :replace, it's assumed to be :merge.
                  Indicates to the function whether to check for a
                  possible merge vs. replace

should - JSON   - JSON tree representing target configuration

is     - JSON   - JSON tree representing current configuration

Needs merge will determine if should and is differ sufficiently to necessitate running the merge command.

The logic here amounts to determining if should is a subtree of is, with a tiny bit of domain trickiness surrounding elements that are arrays that contain a single nil element that is required for “creating” certain configuration elements.

There are ultimately 3 different types of elements in a json tree. Hashes, Arrays, and leaves. While hashes and array values are organized with an order, the logic here ignores the order. In fact, it specifically attempts to match assuming order doesn't matter. This is largely to allow users some freedom in specifying the target configuration. The gRPC interface doesn't seem to care about order. If that changes, then so should this code.

Arrays and Hashes are compared by iterating over every element in should, and ensuring it is within is.

Leaves are directly compared for equality, excepting the condition that the should leaf is in fact an array with one element that is nil.

Needs replace will determine if should and is differ sufficiently to necessitate running the replace command.

The logic is the same as merge, except when comparing hashes, if the is hash table has elements that are not in should, we ultimately indicate that replace is needed

Returns:

  • (Boolean)

94
95
96
# File 'lib/cisco_node_utils/yang.rb', line 94

def self.needs_something?(op, should, is)
  !hash_equiv?(op, should, is)
end

.nil_array(elt) ⇒ Object


98
99
100
# File 'lib/cisco_node_utils/yang.rb', line 98

def self.nil_array(elt)
  elt.nil? || (elt.is_a?(Array) && elt.length == 1 && elt[0].nil?)
end

.sub_elt(op, should, is) ⇒ Object


102
103
104
105
106
107
108
109
110
# File 'lib/cisco_node_utils/yang.rb', line 102

def self.sub_elt(op, should, is)
  if should.is_a?(Hash) && is.is_a?(Hash)
    return self.hash_equiv?(op, should, is)
  elsif should.is_a?(Array) && is.is_a?(Array)
    return self.array_equiv?(op, should, is)
  else
    return !(should != is && !nil_array(should))
  end
end