From b2c60e54636338c931b061edffe4d28c2c7eea1e Mon Sep 17 00:00:00 2001
From: Alejandro Huertas <ahuertas@opennebula.systems>
Date: Tue, 23 Apr 2019 12:57:47 +0200
Subject: [PATCH] F #3263: Simplify and make the FSCK network code more clear

    * Divide the code into functions
    * Fix some bugs
    * Lint the code
---
 share/linters/.rubocop.yml |   1 -
 src/onedb/fsck/network.rb  | 810 ++++++++++++++++++++-----------------
 2 files changed, 432 insertions(+), 379 deletions(-)

diff --git a/share/linters/.rubocop.yml b/share/linters/.rubocop.yml
index d80f6d3dc81..3d23a268d37 100644
--- a/share/linters/.rubocop.yml
+++ b/share/linters/.rubocop.yml
@@ -502,7 +502,6 @@ AllCops:
     - src/onedb/fsck/vrouter.rb
     - src/onedb/fsck/pool_control.rb
     - src/onedb/fsck/marketplace.rb
-    - src/onedb/fsck/network.rb
     - src/onedb/fsck/template.rb
     - src/onedb/fsck/group.rb
     - src/onedb/fsck/cluster.rb
diff --git a/src/onedb/fsck/network.rb b/src/onedb/fsck/network.rb
index 346c1bf56ae..c98685074d1 100644
--- a/src/onedb/fsck/network.rb
+++ b/src/onedb/fsck/network.rb
@@ -1,43 +1,24 @@
-
+# Network module
 module OneDBFsck
 
-    # Init vnet counters
-    def init_network_counters
-        @db.fetch("SELECT oid,body FROM network_pool") do |row|
-            doc = nokogiri_doc(row[:body])
-
-            ar_leases = {}
-
-            doc.root.xpath("AR_POOL/AR/AR_ID").each do |ar_id|
-                ar_leases[ar_id.text.to_i] = {}
-            end
-
-            counters[:vnet][row[:oid]] = {
-                :ar_leases      => ar_leases,
-                :no_ar_leases   => {}
-            }
-        end
-    end
-
+    # Check that network cluster really exists
     def check_network_cluster
         cluster = @data_cluster
+
         @fixes_host_cluster = {}
 
-        @db.fetch("SELECT oid,body FROM network_pool") do |row|
+        @db.fetch('SELECT oid,body FROM network_pool') do |row|
             doc = nokogiri_doc(row[:body])
 
-            doc.root.xpath("CLUSTERS/ID").each do |e|
-                cluster_id = e.text.to_i
-
+            doc.root.xpath('CLUSTERS/ID').each do |e|
+                cluster_id    = e.text.to_i
                 cluster_entry = cluster[cluster_id]
 
                 if cluster_entry.nil?
-                    log_error("VNet #{row[:oid]} is in cluster " <<
+                    log_error("VNet #{row[:oid]} is in cluster " \
                               "#{cluster_id}, but it does not exist")
 
-                    e.remove
-
-                    @fixes_host_cluster[row[:oid]] = { body: doc.root.to_s }
+                    @fixes_host_cluster[row[:oid]] = { :body => doc.root.to_s }
                 else
                     cluster_entry[:vnets] << row[:oid]
                 end
@@ -45,440 +26,513 @@ def check_network_cluster
         end
     end
 
+    # Check network information
+    def check_network
+        @fixes_network = {}
+
+        @db[:network_pool].each do |row|
+            doc = nokogiri_doc(row[:body])
+            oid = row[:oid]
+
+            used_leases   = doc.root.at_xpath('USED_LEASES').text.to_i
+            counter_no_ar = counters[:vnet][row[:oid]][:no_ar_leases]
+            ar_leases     = counters[:vnet][row[:oid]][:ar_leases]
+
+            error, new_used_leases = check_ar_leases(oid,
+                                                     doc,
+                                                     ar_leases,
+                                                     counter_no_ar)
+
+            if new_used_leases != used_leases
+                log_error("VNet #{oid} has #{used_leases} used leases, " \
+                          "but it is actually #{new_used_leases}")
+
+                error = true
+
+                doc.root.at_xpath('USED_LEASES').content = new_used_leases.to_s
+            end
+
+            counter_no_ar.each do |_, counter_lease|
+                log_error("VM #{counter_lease[:vm]} has a lease from " \
+                          "VNet #{oid}, but it could not be matched " \
+                          'to any AR', false)
+            end
+
+            error = check_vn_mad(doc, oid, error)
+
+            @fixes_network[oid] = doc.root.to_s if error
+        end
+    end
+
+    # Fix network cluster
     def fix_network_cluster
         @db.transaction do
             @fixes_host_cluster.each do |id, entry|
-                @db[:host_pool].where(oid: id).update(body: entry[:body])
+                @db.fetch('SELECT cid FROM cluster_network_relation where ' \
+                          "oid=#{id}") do |row|
+                    doc = nokogiri_doc(entry[:body])
+
+                    doc.root.at_xpath('CLUSTERS/ID').content = row[:cid]
+
+                    @data_cluster[row[:cid]][:vnets] << id
+
+                    body = doc.root.to_s
+
+                    @db[:network_pool].where(:oid => id).update(:body => body)
+                end
             end
         end
     end
 
+    # Fix network errors
+    def fix_network
+        @db.transaction do
+            @fixes_network.each do |id, body|
+                @db[:network_pool].where(:oid => id).update(:body => body)
+            end
+        end
+    end
+
+    # Init vnet counters
+    def init_network_counters
+        @db.fetch('SELECT oid,body FROM network_pool') do |row|
+            doc = nokogiri_doc(row[:body])
+
+            ar_leases = {}
+
+            doc.root.xpath('AR_POOL/AR/AR_ID').each do |ar_id|
+                ar_leases[ar_id.text.to_i] = {}
+            end
+
+            counters[:vnet][row[:oid]] = {
+                :ar_leases      => ar_leases,
+                :no_ar_leases   => {}
+            }
+        end
+    end
+
+    # Init network leases counters
     def init_network_lease_counters
-        @db.fetch("SELECT oid,body,pid FROM network_pool WHERE pid<>-1") do |row|
+        query = 'SELECT oid,body,pid FROM network_pool WHERE pid<>-1'
+
+        @db.fetch(query) do |row|
             doc = nokogiri_doc(row[:body])
 
-            parent_vnet = doc.root.at_xpath("PARENT_NETWORK_ID").text.to_i
+            parent_vnet = doc.root.at_xpath('PARENT_NETWORK_ID').text.to_i
 
-            if (row[:pid] != parent_vnet)
+            if row[:pid] != parent_vnet
                 # TODO
             end
 
-            doc.root.xpath("AR_POOL/AR").each do |ar|
-                parent_ar_e = ar.at_xpath("PARENT_NETWORK_AR_ID")
-                if !(parent_ar_e.nil? || parent_ar_e.text == "")
+            doc.root.xpath('AR_POOL/AR').each do |ar|
+                parent_ar_e = ar.at_xpath('PARENT_NETWORK_AR_ID')
 
-                    parent_ar = parent_ar_e.text.to_i
+                next if parent_ar_e.nil? || parent_ar_e.text.empty?
 
-                    if counters[:vnet][parent_vnet][:ar_leases][parent_ar].nil?
-                        log_error(
-                            "VNet #{row[:oid]} is using parent "<<
-                            "VNet #{parent_vnet}, AR #{parent_ar}, "<<
-                            "but the AR does not exist", false)
-                    end
+                parent_ar = parent_ar_e.text.to_i
 
-                    # MAC
-                    first_mac = mac_s_to_i(ar.at_xpath("MAC").text)
+                if counters[:vnet][parent_vnet][:ar_leases][parent_ar].nil?
+                    log_error("VNet #{row[:oid]} is using parent " \
+                              "VNet #{parent_vnet}, AR #{parent_ar}, " \
+                              'but the AR does not exist', false)
+                end
 
-                    # IP
-                    first_ip = nil
-                    if (!ar.at_xpath("IP").nil?)
-                        first_ip = IPAddr.new(ar.at_xpath("IP").text.strip, Socket::AF_INET)
-                    end
+                # MAC
+                first_mac = mac_s_to_i(ar.at_xpath('MAC').text)
 
-                    # IP6
-                    global_prefix = nil
-                    if !ar.at_xpath("GLOBAL_PREFIX").nil?
-                        global_prefix = ip6_prefix_s_to_i(
-                                    ar.at_xpath("GLOBAL_PREFIX").text)
-                    end
+                # IP
+                unless ar.at_xpath('IP').nil?
+                    first_ip = IPAddr.new(ar.at_xpath('IP').text.strip,
+                                          Socket::AF_INET)
+                end
 
-                    ula_prefix = nil
-                    if !ar.at_xpath("ULA_PREFIX").nil?
-                        ula_prefix = ip6_prefix_s_to_i(
-                                    ar.at_xpath("ULA_PREFIX").text)
-                    end
+                # IP6
+                ipv6 = get_ipv6(ar)
 
-                    link_prefix = nil
-                    type = ar.at_xpath("TYPE").text
-                    if ( type == "IP6" || type == "IP4_6" )
-                        link_prefix = 0xfe80000000000000
-                    end
+                addrs = { :mac => first_mac, :ip => first_ip, :ipv6 => ipv6 }
 
-                    # Parent vnet has a lease for each address of this reservation
-                    ar.at_xpath("SIZE").text.to_i.times do |index|
-
-                        lease = {
-                            :ip         => nil,
-                            :ip6_global => nil,
-                            :ip6_link   => nil,
-                            :ip6_ula    => nil,
-                            :mac        => nil,
-                            :vm         => nil,
-                            :vnet       => row[:oid],
-                            :vrouter    => nil
-                        }
-
-                        #MAC
-                        mac = (first_mac & 0xFFFF00000000) +
-                              (((first_mac & 0xFFFFFFFF) + index) % 0x100000000)
-                        lease[:mac] = mac_i_to_s(mac)
-
-                        # IP
-                        if (!first_ip.nil?)
-                            lease[:ip] = IPAddr.new(first_ip.to_i + index,
-                                                    Socket::AF_INET).to_s
-                        end
-
-                        # IP6
-                        ip6_suffix = mac_to_ip6_suffix(mac)
-
-                        if (!global_prefix.nil?)
-                            lease[:ip6_global] = IPAddr.new(
-                                (global_prefix << 64) | ip6_suffix,
-                                Socket::AF_INET6 ).to_s
-                        end
-
-                        if (!ula_prefix.nil?)
-                            lease[:ip6_ula] = IPAddr.new(
-                                (ula_prefix << 64) | ip6_suffix,
-                                Socket::AF_INET6 ).to_s
-                        end
-
-                        if (!link_prefix.nil?)
-                            lease[:ip6_link] = IPAddr.new(
-                                (link_prefix << 64) | ip6_suffix,
-                                Socket::AF_INET6 ).to_s
-                        end
-
-                        counters[:vnet][parent_vnet][
-                            :ar_leases][parent_ar][mac] = lease
-                    end
-                end
+                # Parent vnet has a lease for each address of this reservation
+                calculate_leases(ar, row[:oid], addrs. parent_vnet, parent_ar)
             end
         end
     end
 
-    def check_network
-        @fixes_network = {}
+    # Get lease information
+    #
+    # @param oid   [Integer] VNet ID
+    # @param mac   [String]  VNet mac address
+    # @param ip    [String]  VNet IPv4 address
+    # @param ipv6  [Object]  VNet IPv6 object with all information about v6
+    # @param index [Integer] Index of the lease to calculate the mac and IP
+    #
+    # @return      [Object]  Object with lease information
+    def get_lease(oid, mac, ip, ipv6, index)
+        lease = {
+            :ip         => nil,
+            :ip6_global => nil,
+            :ip6_link   => nil,
+            :ip6_ula    => nil,
+            :mac        => nil,
+            :mac_index  => nil,
+            :vm         => nil,
+            :vnet       => oid,
+            :vrouter    => nil
+        }
+
+        # MAC
+        mac = (mac & 0xFFFF00000000) +
+              (((mac & 0xFFFFFFFF) + index) %
+              0x100000000)
+
+        lease[:mac]       = mac_i_to_s(mac)
+        lease[:mac_index] = mac
+
+        # IP
+        unless ip.nil?
+            lease[:ip] = IPAddr.new(ip.to_i + index, Socket::AF_INET).to_s
+        end
 
-        @db[:network_pool].each do |row|
-            doc = nokogiri_doc(row[:body])
-            oid = row[:oid]
+        # IP6
+        ip6_suffix = mac_to_ip6_suffix(mac)
 
-            used_leases = doc.root.at_xpath("USED_LEASES").text.to_i
-            new_used_leases = 0
+        g_pref = ipv6[:global_prefix]
+        u_pref = ipv6[:ula_prefix]
+        l_pref = ipv6[:linked_prefix]
 
-            counter_no_ar = counters[:vnet][row[:oid]][:no_ar_leases]
+        unless g_pref.nil?
+            pref = (g_pref << 64) | ip6_suffix
+            lease[:ip6_global] = IPAddr.new(pref, Socket::AF_INET6).to_s
+        end
 
-            error = false
-            counters[:vnet][row[:oid]][:ar_leases].each do |ar_id, counter_ar|
-                net_ar = doc.root.at_xpath("AR_POOL/AR[AR_ID=#{ar_id}]")
+        unless u_pref.nil?
+            pref = (u_pref << 64) | ip6_suffix
+            lease[:ip6_ula] = IPAddr.new(pref, Socket::AF_INET6).to_s
+        end
 
-                if (net_ar.nil?)
-                    # TODO shouldn't happen?
-                end
+        unless l_pref.nil?
+            pref = (l_pref << 64) | ip6_suffix
+            lease[:ip6_link] = IPAddr.new(pref, Socket::AF_INET6).to_s
+        end
 
-                # MAC
-                first_mac = mac_s_to_i(net_ar.at_xpath("MAC").text)
+        lease
+    end
 
-                # IP
-                first_ip = nil
-                if !net_ar.at_xpath("IP").nil?
-                    first_ip_st = net_ar.at_xpath("IP").text
+    # Calculate ar leases
+    #
+    # @param ar     [XML]      Address range information
+    # @param oid    [Interger] VNet ID
+    # @param addrs  [Object]   Object with mac, IP and IPv6 addresses
+    # @param p_vnet [String]   Parent VNet
+    # @param p_ar   [String]   Parent address range
+    def calculate_leases(address_range, oid, addrs, p_vnet, p_ar)
+        address_range.at_xpath('SIZE').text.to_i.times do |index|
+            lease = get_lease(oid, addrs[:mac], addrs[:ip], addrs[:ipv6], index)
+
+            counters[:vnet][p_vnet][:ar_leases][p_ar][mac] = lease
+        end
+    end
 
-                    if (first_ip_st != first_ip_st.strip)
-                        log_error("VNet #{oid} AR #{ar_id} "<<
-                            "IP \"#{first_ip_st}\" contains whitespaces")
-                        error = true
+    # Get IPv6 information
+    #
+    # @param address_range [XML element] Address range information
+    #
+    # @return              [Object]      Object with IPv6 information
+    def get_ipv6(address_range)
+        ipv6 = { :global_prefix => nil,
+                 :ula_prefix    => nil,
+                 :link_prefix   => nil }
+
+        unless address_range.at_xpath('GLOBAL_PREFIX').nil?
+            g_prefix             = address_range.at_xpath('GLOBAL_PREFIX').text
+            ipv6[:global_prefix] = ip6_prefix_s_to_i(g_prefix)
+        end
 
-                        first_ip_st.strip!
+        unless address_range.at_xpath('ULA_PREFIX').nil?
+            u_prefix          = address_range.at_xpath('ULA_PREFIX').text
+            ipv6[:ula_prefix] = ip6_prefix_s_to_i(u_prefix)
+        end
 
-                        net_ar.at_xpath("IP").content = first_ip_st
-                    end
+        type = address_range.at_xpath('TYPE').text
 
-                    first_ip = IPAddr.new(first_ip_st, Socket::AF_INET)
-                end
+        if %w[IP6 IP4_6].include? type
+            ipv6[:link_prefix] = 0xfe80000000000000
+        end
 
-                # IP6
-                global_prefix = nil
-                if !net_ar.at_xpath("GLOBAL_PREFIX").nil?
-                    global_prefix = ip6_prefix_s_to_i(
-                                net_ar.at_xpath("GLOBAL_PREFIX").text)
-                end
+        ipv6
+    end
 
-                ula_prefix = nil
-                if !net_ar.at_xpath("ULA_PREFIX").nil?
-                    ula_prefix = ip6_prefix_s_to_i(
-                                net_ar.at_xpath("ULA_PREFIX").text)
-                end
+    # Check that the IP address is valid
+    #
+    # @param oid    [Integer]     VNet ID
+    # @param ar_id  [Integer]     Address range ID
+    # @param ip     [String]      IP address
+    # @param net_ar [XML element] Address range information
+    # @param error  [Boolean]     True if there was an error, false if not
+    #
+    # @return       [Boolean/IP]  The current value of error and the fixed IP
+    def check_ip(oid, ar_id, ip, net_ar, error)
+        if ip != ip.strip
+            log_error("VNet #{oid} AR #{ar_id} " \
+                      "IP \"#{ip}\" contains whitespaces")
+
+            error = true
+
+            ip.strip!
+
+            net_ar.at_xpath('IP').content = ip
+        end
 
-                link_prefix = nil
-                type = net_ar.at_xpath("TYPE").text
-                if ( type == "IP6" || type == "IP4_6" )
-                    link_prefix = 0xfe80000000000000
-                end
+        [error, IPAddr.new(ip, Socket::AF_INET)]
+    end
 
-                # Allocated leases
-                allocated_e = net_ar.at_xpath("ALLOCATED")
+    # Check VNet VN_MAD
+    #
+    # @param doc   [XML element] XML document with all the information
+    # @param oid   [Integer]     VNet ID
+    # @param error [Boolean]     True if there was an error, false if not
+    #
+    # @return      [Booleam]     The current value of error
+    def check_vn_mad(doc, oid, error)
+        vn_mad_e = doc.root.at_xpath('VN_MAD')
+
+        if vn_mad_e.nil?
+            log_error("VNet #{oid} VN_MAD element is missing", false)
+        else
+            vn_mad        = vn_mad_e.text
+            vn_mad_tmpl_e = doc.root.at_xpath('TEMPLATE/VN_MAD')
+
+            if vn_mad_tmpl_e.nil? || vn_mad_tmpl_e.text != vn_mad
+                log_error("VNet #{oid} VN_MAD element is missing " \
+                          'from the TEMPLATE')
 
-                allocated = allocated_e.nil? ? "" : allocated_e.text
+                error = true
 
-                leases = allocated.scan(/(\d+) (\d+)/)
+                doc.root.at_xpath('TEMPLATE')
+                   .add_child(doc.create_element('VN_MAD')).content = vn_mad
+            end
+        end
 
-                size = net_ar.at_xpath("SIZE").text.to_i
+        error
+    end
 
-                if leases.length > size
-                    log_error("VNet #{oid} AR #{ar_id} allocated leases "\
-                      "(#{leases.length}) is greater than the AR size (#{size}"\
-                      "). SIZE can be increased with onevnet updatear"\
-                      " #{oid} #{ar_id}")
+    # Check ar leases
+    #
+    # @param oid           [Integer]     VNet ID
+    # @param doc           [XML element] XML document with all the information
+    # @param ar_leasses    [Array]       Address range leases to check
+    # @param counter_no_ar [Array]       Counters for no address range info
+    #
+    # @return              [Boolean/Interger] error if any, number leases used
+    def check_ar_leases(oid, doc, ar_leases, counter_no_ar)
+        new_used_leases = 0
+        error           = false
+
+        ar_leases.each do |ar_id, counter_ar|
+            net_ar = doc.root.at_xpath("AR_POOL/AR[AR_ID=#{ar_id}]")
+
+            if net_ar.nil?
+                # TODO: shouldn't happen?
+            end
 
-                    error = true
-                    net_ar.at_xpath("SIZE").content = leases.length.to_s
-                end
+            # MAC
+            first_mac = mac_s_to_i(net_ar.at_xpath('MAC').text)
 
-                new_leases = []
-
-                leases.each do |lease_str|
-                    index = lease_str[0].to_i
-                    binary_magic = lease_str[1].to_i
-
-                    lease = {
-                        :ip         => nil,
-                        :ip6_global => nil,
-                        :ip6_link   => nil,
-                        :ip6_ula    => nil,
-                        :mac        => nil,
-                        :vm         => nil,
-                        :vnet       => nil,
-                        :vrouter    => nil
-                    }
-
-                    # MAC
-                    mac = (first_mac & 0xFFFF00000000) +
-                          (((first_mac & 0xFFFFFFFF) + index) % 0x100000000)
-
-                    lease[:mac] = mac_i_to_s(mac)
-
-                    # IP
-                    if (!first_ip.nil?)
-                        lease[:ip] = IPAddr.new(first_ip.to_i + index,
-                                                Socket::AF_INET).to_s
-                    end
+            # IP
+            unless net_ar.at_xpath('IP').nil?
+                ip              = net_ar.at_xpath('IP').text
+                error, first_ip = check_ip(oid, ar_id, ip, net_ar, error)
+            end
 
-                    # IP6
-                    ip6_suffix = mac_to_ip6_suffix(mac)
+            # IP6
+            ipv6 = get_ipv6(net_ar)
 
-                    if (!global_prefix.nil?)
-                        lease[:ip6_global] = IPAddr.new(
-                            (global_prefix << 64) | ip6_suffix,
-                            Socket::AF_INET6 ).to_s
-                    end
+            # Allocated leases
+            allocated_e = net_ar.at_xpath('ALLOCATED')
 
-                    if (!ula_prefix.nil?)
-                        lease[:ip6_ula] = IPAddr.new(
-                            (ula_prefix << 64) | ip6_suffix,
-                            Socket::AF_INET6 ).to_s
-                    end
+            allocated_e.nil? ? allocated = '' : allocated = allocated_e.text
 
-                    if (!link_prefix.nil?)
-                        lease[:ip6_link] = IPAddr.new(
-                            (link_prefix << 64) | ip6_suffix,
-                            Socket::AF_INET6 ).to_s
-                    end
+            leases = allocated.scan(/(\d+) (\d+)/)
 
-                    # OID
-                    lease_oid = binary_magic & 0x00000000FFFFFFFF
-                    lease_obj = ""
-
-                    if (binary_magic & VM_BIN != 0)
-                        lease[:vm] = lease_oid
-                        lease_obj = "VM"
-                    elsif (binary_magic & NET_BIN != 0)
-                        lease[:vnet] = lease_oid
-                        lease_obj = "VNet"
-                    else #(binary_magic & VROUTER_BIN != 0)
-                        lease[:vrouter] = lease_oid
-                        lease_obj = "VRouter"
-                    end
+            size = net_ar.at_xpath('SIZE').text.to_i
 
-                    counter_lease = counter_ar[mac]
-                    counter_ar.delete(mac)
+            if leases.length > size
+                log_error("VNet #{oid} AR #{ar_id} allocated leases " \
+                          "(#{leases.length}) is greater than the " \
+                          "AR size (#{size}). SIZE can be increased " \
+                          "with onevnet updatear #{oid} #{ar_id}")
 
-                    if counter_lease.nil?
-                        counter_lease = counter_no_ar[mac]
-                        counter_no_ar.delete(mac)
-                    end
-
-                    if counter_lease.nil?
-                        if(lease[:vm] != HOLD)
-                            log_error(
-                            "VNet #{oid} AR #{ar_id} has leased #{lease_to_s(lease)} "<<
-                            "to #{lease_obj} #{lease_oid}, but it is actually free")
+                error = true
 
-                            error = true
-                        else
-                            new_leases << lease_str
-                        end
-                    else
-                        if counter_lease != lease
-
-                            # Things that can be fixed
-                            if (counter_lease[:vm]      != lease[:vm] ||
-                                counter_lease[:vnet]    != lease[:vnet] ||
-                                counter_lease[:vrouter] != lease[:vrouter])
-
-                                new_lease_obj = ""
-                                new_lease_oid = 0
-                                new_binary_magic = 0
-
-                                if !counter_lease[:vm].nil?
-                                    new_lease_obj = "VM"
-                                    new_lease_oid = counter_lease[:vm].to_i
-
-                                    new_binary_magic = (VM_BIN |
-                                        (new_lease_oid & 0xFFFFFFFF))
-                                elsif !counter_lease[:vnet].nil?
-                                    new_lease_obj = "VNet"
-                                    new_lease_oid = counter_lease[:vnet].to_i
-
-                                    new_binary_magic = (NET_BIN |
-                                        (new_lease_oid & 0xFFFFFFFF))
-                                else #if !counter_lease[:vrouter].nil?
-                                    new_lease_obj = "VRouter"
-                                    new_lease_oid = counter_lease[:vrouter].to_i
-
-                                    new_binary_magic = (VROUTER_BIN |
-                                        (new_lease_oid & 0xFFFFFFFF))
-                                end
-
-                                if (lease[:vm] == HOLD)
-                                    log_error(
-                                        "VNet #{oid} AR #{ar_id} has lease "<<
-                                        "#{lease_to_s(lease)} on hold, but it is "<<
-                                        "actually used by "<<
-                                        "#{new_lease_obj} #{new_lease_oid}")
-                                    error = true
-                                else
-                                    log_error(
-                                        "VNet #{oid} AR #{ar_id} has leased #{lease_to_s(lease)} "<<
-                                        "to #{lease_obj} #{lease_oid}, but it is "<<
-                                        "actually used by "<<
-                                        "#{new_lease_obj} #{new_lease_oid}")
-                                    error = true
-                                end
-
-                                lease_str[1] = new_binary_magic.to_s
-                            end
-
-                            # Things that can't be fixed
-
-                            [:ip, :ip6_global, :ip6_link, :ip6_ula].each do |key|
-                                if (counter_lease[key] != lease[key])
-                                    log_error(
-                                    "VNet #{oid} AR #{ar_id} has a wrong "<<
-                                    "lease for "<<
-                                    "#{lease_obj} #{lease_oid}. #{key.to_s.upcase} "<<
-                                    "does not match: "<<
-                                    "#{counter_lease[key]} != #{lease[key]}. "<<
-                                    "This can't be fixed", false)
-                                end
-                            end
-                        end
-
-                        new_leases << lease_str
-                    end
-                end
+                net_ar.at_xpath('SIZE').content = leases.length.to_s
+            end
 
-                counter_ar.each do |mac, counter_lease|
-                    next if mac.nil?
+            ids      = { :o => oid, :ar => ar_id }
+            addrs    = { :mac => first_mac, :ip => first_ip, :ipv6 => ipv6 }
+            counters = { :ar => counter_ar, :no_ar => counter_no_ar }
 
-                    index = ((mac & 0xFFFFFFFF) - (first_mac & 0xFFFFFFFF) ) % 0x100000000
+            error, new_leases = calculate_new_leases(leases,
+                                                     ids,
+                                                     addrs,
+                                                     counters,
+                                                     error)
 
-                    new_lease_obj = ""
-                    new_lease_oid = 0
-                    new_binary_magic = 0
+            counter_ar.each do |mac, counter_lease|
+                next if mac.nil?
 
-                    if !counter_lease[:vm].nil?
-                        new_lease_obj = "VM"
-                        new_lease_oid = counter_lease[:vm].to_i
+                index = ((mac & 0xFFFFFFFF) -
+                        (first_mac & 0xFFFFFFFF)) %
+                        0x100000000
 
-                        new_binary_magic = (VM_BIN |
-                            (new_lease_oid & 0xFFFFFFFF))
-                    elsif !counter_lease[:vnet].nil?
-                        new_lease_obj = "VNet"
-                        new_lease_oid = counter_lease[:vnet].to_i
+                parameters = fix_parameters(counter_lease)
 
-                        new_binary_magic = (NET_BIN |
-                            (new_lease_oid & 0xFFFFFFFF))
-                    else #if !counter_lease[:vrouter].nil?
-                        new_lease_obj = "VRouter"
-                        new_lease_oid = counter_lease[:vrouter].to_i
+                log_error("VNet #{oid} AR #{ar_id} does not have a lease " \
+                          "for #{mac_i_to_s(mac)}, but it is in use by " \
+                          "#{parameters[:new_lease_obj]} " \
+                          "#{parameters[:new_lease_oid]}")
 
-                        new_binary_magic = (VROUTER_BIN |
-                            (new_lease_oid & 0xFFFFFFFF))
-                    end
+                error = true
 
-                    log_error("VNet #{oid} AR #{ar_id} does not have a lease "<<
-                        "for #{mac_i_to_s(mac)}, but it is in use by "<<
-                        "#{new_lease_obj} #{new_lease_oid}")
+                new_leases << [index.to_s, parameters[:new_binary_magic].to_s]
+            end
 
-                    error = true
+            new_used_leases += new_leases.size
 
-                    new_leases << [index.to_s, new_binary_magic.to_s]
-                end
+            allocated_e.remove if allocated_e
 
-                new_used_leases += new_leases.size
+            unless new_leases.empty?
+                add_cdata(net_ar, 'ALLOCATED', " #{new_leases.join(' ')}")
+            end
+        end
 
-                allocated_e.remove if allocated_e
+        [error, new_used_leases]
+    end
 
-                if new_leases.size > 0
-                    add_cdata(net_ar, "ALLOCATED", " #{new_leases.join(" ")}")
-                end
-            end
+    # Fix paramters of the lease
+    #
+    # @param counter_lease [Object] Counters of the lease
+    #
+    # @return              [Object] Object with new lease parameters
+    def fix_parameters(counter_lease)
+        params = { :new_lease_obj    => '',
+                   :new_lease_oid    => 0,
+                   :new_binary_magic => 0 }
+
+        if !counter_lease[:vm].nil?
+            new_lease_obj    = 'VM'
+            new_lease_oid    = counter_lease[:vm].to_i
+            new_binary_magic = (VM_BIN | (new_lease_oid & 0xFFFFFFFF))
+        elsif !counter_lease[:vnet].nil?
+            new_lease_obj    = 'VNet'
+            new_lease_oid    = counter_lease[:vnet].to_i
+            new_binary_magic = (NET_BIN | (new_lease_oid & 0xFFFFFFFF))
+        else
+            new_lease_obj    = 'VRouter'
+            new_lease_oid    = counter_lease[:vrouter].to_i
+            new_binary_magic = (VROUTER_BIN | (new_lease_oid & 0xFFFFFFFF))
+        end
 
-            if (new_used_leases != used_leases)
-                log_error("VNet #{oid} has #{used_leases} used leases, "<<
-                "but it is actually #{new_used_leases}")
+        params[:new_lease_obj]    = new_lease_obj
+        params[:new_lease_oid]    = new_lease_oid
+        params[:new_binary_magic] = new_binary_magic
 
-                error = true
+        params
+    end
 
-                doc.root.at_xpath("USED_LEASES").content =
-                                                new_used_leases.to_s
+    # Calculate new leases information
+    #
+    # @param leases   [Array]   Array with address range leases
+    # @param ids      [Object]  Object with VNet ID and AR ID
+    # @param addrs    [Object]  Object with mac, IP and IPv6 addresses
+    # @param counters [Object]  Object with ar and no_ar counters
+    # @param error    [Boolean] True if there was an error, false if not
+    #
+    # @return         [Boolean/Leases] The current value of error and new leases
+    def calculate_new_leases(leases, ids, addrs, counters, error)
+        new_leases = []
+
+        leases.each do |lease_str|
+            index        = lease_str[0].to_i
+            binary_magic = lease_str[1].to_i
+
+            lease = get_lease(nil, addrs[:mac], addrs[:ip], addrs[:ipv6], index)
+
+            # OID
+            lease_oid = binary_magic & 0x00000000FFFFFFFF
+            lease_obj = ''
+
+            if binary_magic & VM_BIN != 0
+                lease[:vm] = lease_oid
+                lease_obj  = 'VM'
+            elsif binary_magic & NET_BIN != 0
+                lease[:vnet] = lease_oid
+                lease_obj    = 'VNet'
+            else
+                lease[:vrouter] = lease_oid
+                lease_obj       = 'VRouter'
             end
 
-            counter_no_ar.each do |mac, counter_lease|
-                log_error("VM #{counter_lease[:vm]} has a lease from "<<
-                    "VNet #{oid}, but it could not be matched to any AR", false)
+            counter_lease = counters[:ar][lease[:mac_index]]
+            counters[:ar].delete(lease[:mac_index])
+
+            if counter_lease.nil?
+                counter_lease = counters[:no_ar][lease[:mac_index]]
+                counters[:no_ar].delete(lease[:mac_index])
             end
 
-            vn_mad_e = doc.root.at_xpath("VN_MAD")
-            if vn_mad_e.nil?
-                log_error("VNet #{oid} VN_MAD element is missing", false)
+            if counter_lease.nil?
+                if lease[:vm] != HOLD
+                    log_error("VNet #{ids[:o]} AR #{ids[:ar]} has " \
+                              "leased #{lease_to_s(lease)} to #{lease_obj} " \
+                              "#{lease_oid}, but it is actually free")
+
+                    error = true
+                else
+                    new_leases << lease_str
+                end
             else
-                vn_mad = vn_mad_e.text
-                vn_mad_tmpl_e = doc.root.at_xpath("TEMPLATE/VN_MAD")
+                new_leases << lease_str
 
-                if (vn_mad_tmpl_e.nil? || vn_mad_tmpl_e.text != vn_mad)
-                    log_error("VNet #{oid} VN_MAD element is missing from the TEMPLATE")
+                next if counter_lease == lease
 
-                    error = true
+                # Things that can be fixed
+                if counter_lease[:vm]      != lease[:vm] ||
+                   counter_lease[:vnet]    != lease[:vnet] ||
+                   counter_lease[:vrouter] != lease[:vrouter]
 
-                    doc.root.at_xpath("TEMPLATE").add_child(
-                        doc.create_element("VN_MAD")).content = vn_mad
+                    parameters = fix_parameters(counter_lease)
+
+                    if lease[:vm] == HOLD
+                        log_error("VNet #{ids[:o]} AR #{ids[:ar]} has lease " \
+                                  "#{lease_to_s(lease)} on hold, but it is " \
+                                  'actually used by ' \
+                                  "#{parameters[:new_lease_obj]} " \
+                                  "#{parameters[:new_lease_oid]}")
+                    else
+                        log_error("VNet #{ids[:o]} AR #{ids[:ar]} has leased " \
+                                  "#{lease_to_s(lease)} to #{lease_obj} " \
+                                  "#{lease_oid}, but it is actually used by " \
+                                  "#{parameters[:new_lease_obj]} " \
+                                  "#{parameters[:new_lease_oid]}")
+                    end
+
+                    error        = true
+                    lease_str[1] = parameters[:new_binary_magic].to_s
                 end
-            end
 
-            @fixes_network[oid] = doc.root.to_s if error
-        end
-    end
+                # Things that can't be fixed
+                %i[ip ip6_global ip6_link ip6_ula].each do |key|
+                    next if counter_lease[key] == lease[key]
 
-    def fix_network
-        @db.transaction do
-            @fixes_network.each do |id, body|
-                @db[:network_pool].where(oid: id).update(body: body)
+                    log_error("VNet #{ids[:o]} AR #{ids[:ar]} has a wrong " \
+                              "lease for #{lease_obj} #{lease_oid}. " \
+                              "#{key.to_s.upcase} does not match: " \
+                              "#{counter_lease[key]} != #{lease[key]}. " \
+                              'This can\'t be fixed', false)
+                end
             end
+
+            return error, new_leases
         end
     end
-end
 
+end