From 678fa670743bab42a90e6e054cea1aac9a518e9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Gaidamour?= <jeremie.gaidamour@inria.fr>
Date: Mon, 19 Oct 2015 13:59:36 +0200
Subject: [PATCH] [dev] Prototype written by Simon for the new version of the
 generator

---
 README                                        |   5 +
 generators/input_loader.rb                    |  67 +++++++++++
 generators/reference-api/reference-api.rb     | 109 ++++++++++++++++++
 .../rouen/clusters/pomme/nodes/pomme-1.yml    |  60 ++++++++++
 .../rouen/clusters/pomme/nodes/pomme-2.yml    |  60 ++++++++++
 .../sites/rouen/clusters/pomme/pomme.yml      |  63 ++++++++++
 .../sites/rouen/network/gw-production.yaml    |  91 +++++++++++++++
 7 files changed, 455 insertions(+)
 create mode 100644 README
 create mode 100755 generators/input_loader.rb
 create mode 100755 generators/reference-api/reference-api.rb
 create mode 100644 input/example/input/sites/rouen/clusters/pomme/nodes/pomme-1.yml
 create mode 100644 input/example/input/sites/rouen/clusters/pomme/nodes/pomme-2.yml
 create mode 100644 input/example/input/sites/rouen/clusters/pomme/pomme.yml
 create mode 100644 input/example/input/sites/rouen/network/gw-production.yaml

diff --git a/README b/README
new file mode 100644
index 00000000000..d5f60efcf0c
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+To get JSON input data:
+  ./generator/input_loader.rb
+
+To generate Ref API data in /tmp/data:
+  ./generator/input_loader.rb | ./generators/reference-api/reference-api.rb
diff --git a/generators/input_loader.rb b/generators/input_loader.rb
new file mode 100755
index 00000000000..006a528c5d4
--- /dev/null
+++ b/generators/input_loader.rb
@@ -0,0 +1,67 @@
+#!/usr/bin/ruby
+
+require 'pathname'
+require 'yaml'
+require 'json'
+
+# Extend Hash with helper methods needed to convert input data files to ruby Hash
+class ::Hash
+
+  # Recursively merge this Hash with another
+  def deep_merge(second)
+    merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
+    self.merge(second, &merger)
+  end
+
+  # Merge keys that match "PREFIX-<a-b>" with others keys that begins by
+  #   "PREFIX-" and that ends with x, where a<=x<=b
+  # a and/or b may be ommited, meaning that there are no lower and/or upper bound for x
+  # This is done recursively (for this Hash and every Hashes it contains)
+  #  i.e.:
+  # {"foo-1": {a: 0}, "foo-2": {a: 0}, "foo-3": {a: 0}, "foo-<2->": {b: 1}}.kbracket_merge()
+  #  -> {"foo-2": {a: 0, b:1},  "foo-3": {a: 0, b: 0}}
+  # TODO: only "<->" suffix is currently implemented
+  def kbracket_merge()
+    self.each { |k_all, v_all|
+      if k_all.to_s.end_with?('-<->') and v_all.is_a?(Hash)
+        key_prefix_to_merge = k_all.to_s.sub(/-<->$/,'')
+        self.each { |k, v|
+          if k.to_s.start_with?(key_prefix_to_merge)
+            self[k] = v_all.deep_merge(v)
+          end
+        }
+        self.delete(k_all)
+      end
+    }
+    self.each { |k, v|
+      if v.is_a?(Hash)
+        v.kbracket_merge()
+      end
+    }
+  end
+
+  # Add an element composed of nested Hashes made from elements found in "array" argument
+  # i.e.: from_array([a, b, c],"foo") -> {a: {b: {c: "foo"}}}
+  def self.from_array(array, value)
+    return array.reverse.inject(value) { |a, n| { n => a } }
+  end
+end
+
+#TODO: Ensure that deepest elements in input files hierarchy have lowest priority
+data = Hash.new
+
+Dir.chdir("input")
+Dir['**/*.y*ml'].each { |f|
+   node_path = Pathname.new(f)
+   node_dir, _ = node_path.split()
+
+   node_hierarchy = node_dir.to_s.split('/')
+   node_value = YAML::load_file(node_path)
+
+   node_data = Hash.from_array(node_hierarchy,node_value)
+   data = data.deep_merge(node_data)
+}
+data.kbracket_merge()
+
+puts JSON.generate(data)
+
diff --git a/generators/reference-api/reference-api.rb b/generators/reference-api/reference-api.rb
new file mode 100755
index 00000000000..157d09e01f4
--- /dev/null
+++ b/generators/reference-api/reference-api.rb
@@ -0,0 +1,109 @@
+#!/usr/bin/ruby
+
+require 'pathname'
+require 'json'
+
+refapi_path = "/tmp/data"
+data = JSON.parse(STDIN.read)
+
+# Write pretty and sorted JSON files
+def write_json(filepath, data)
+  def rec_sort(h)
+    case h
+    when Array
+      h.map{|v| rec_sort(v)}.sort_by!{|v| (v.to_s rescue nil) }
+    when Hash
+      Hash[Hash[h.map{|k,v| [rec_sort(k),rec_sort(v)]}].sort_by{|k,v| [(k.to_s rescue nil), (v.to_s rescue nil)]}]
+    else
+      h
+    end
+  end
+  File.open(filepath, 'w') do |f|
+    f.write(JSON.pretty_generate(rec_sort(data)))
+  end
+end
+
+# Parse network equipment description and return switch name and port connected to given node
+#  In the network description, if the node interface is given (using "port" attribute),
+#  the interface parameter must be used.
+def net_switch_port_lookup(site, node_uid, interface='')
+  site["network"].each do |switch_uid, switch|
+    switch["linecards"].each do |lc_uid,lc|
+      lc["ports"].each do |port_uid,port|
+        if port.is_a?(Hash)
+          switch_remote_port = port["port"] || lc["port"] || ""
+          switch_remote_uid = port["uid"]
+        else
+          switch_remote_port = lc["port"] || ""
+          switch_remote_uid = port
+        end
+        if switch_remote_uid == node_uid and switch_remote_port == interface
+            # Build port name from snmp_naming_pattern
+            # Example: '3 2 GigabitEthernet%LINECARD%/%PORT%' -> 'GigabitEthernet3/2'
+            port_name = lc["snmp_pattern"].sub("%LINECARD%",lc_uid.to_s).sub("%PORT%",port_uid.to_s)
+            return switch_uid, port_name
+        end
+      end
+    end
+  end
+  return nil
+end
+
+data["sites"].each do |site_uid, site|
+
+  site["clusters"].each do |cluster_uid, cluster|
+
+    cluster_path = Pathname.new(refapi_path).join("sites",site_uid,"clusters",cluster_uid)
+    cluster_path.join("nodes").mkpath()
+
+    # Write cluster info w/o nodes entries
+    write_json(cluster_path.join("#{cluster_uid}.json"),
+               cluster.reject {|k, v| k == "nodes"})
+
+    cluster["nodes"].each do |node_uid, node|
+
+      node["uid"] = node_uid
+
+      # TODO: fix inconsistency?
+      node["storage_devices"].each {|s| s.update(node["block_devices"].values.at(node["storage_devices"].index(s)))}
+      node.delete("block_devices")
+
+      # TODO: fix inconsistency?
+      node["chassis"]["name"] = node["chassis"]["product_name"]
+      node["chassis"]["serial"] = node["chassis"]["serial_number"]
+      node["chassis"].delete("product_name")
+      node["chassis"].delete("serial_number")
+
+      # TODO: fix inconsistency?
+      node["network_adapters"].each do |iface|
+        node["network_interfaces"].select {|k| k.to_s == iface["device"]}.each do |k, v|
+          iface.update(v)
+        end
+      end
+      node.delete("network_interfaces")
+
+      # Network Reference
+      node["network_adapters"].each do |iface|
+        if iface["enabled"]
+          if iface["management"]
+            # Managment iface
+            iface["network_address"] = "#{node_uid}-bmc.#{site_uid}.grid5000.fr"
+          elsif iface["mounted"]
+            # Primary iface
+            iface["bridged"] = true
+            iface["network_address"] = "#{node_uid}.#{site_uid}.grid5000.fr"
+            # Interface may not be specified in Network Reference for primary iface
+            iface["switch"], iface["port"] = \
+              net_switch_port_lookup(site, node_uid) || net_switch_port_lookup(site, node_uid, iface["device"])
+          else
+            # Secondary iface(s)
+            iface["network_address"] = "#{node_uid}-#{iface["device"]}.#{site}.grid5000.fr"
+            iface["switch"], iface["port"] = net_switch_port_lookup(site, node_uid, iface["device"])
+          end
+        end
+      end
+
+      write_json(cluster_path.join("nodes","#{node_uid}.json"), node)
+    end
+  end
+end
diff --git a/input/example/input/sites/rouen/clusters/pomme/nodes/pomme-1.yml b/input/example/input/sites/rouen/clusters/pomme/nodes/pomme-1.yml
new file mode 100644
index 00000000000..a78dfa53d3c
--- /dev/null
+++ b/input/example/input/sites/rouen/clusters/pomme/nodes/pomme-1.yml
@@ -0,0 +1,60 @@
+---
+pomme-1:
+  bios:
+    version: V1.35.2.2
+    release_date: 04/25/2006
+    vendor: Phoenix Technologies Ltd.
+  network_interfaces:
+    eth0:
+      mounted: false
+      mac: 00:09:3d:12:9e:13
+      management: false
+      enabled: false
+      interface: Ethernet
+      driver: tg3
+      mountable: false
+      ip: 172.16.49.101
+    eth1:
+      mounted: true
+      switch_port: "1:1"
+      rate: 1000000000
+      mac: 00:09:3d:12:9e:14
+      ip6: fe80::209:3dff:fe12:9e14
+      management: false
+      enabled: true
+      interface: Ethernet
+      driver: tg3
+      mountable: true
+      ip: 172.16.49.1
+    bmc:
+      mac: 00:09:3d:12:9e:15
+      ip: 172.17.49.1
+  block_devices:
+    sda:
+      model: MAT3073NC
+      device: sda
+      size: 73543163904
+      rev: 104
+  supported_job_types:
+    virtual: false
+  chassis:
+    serial_number: XG052536201
+    manufacturer: Sun Microsystems
+    product_name: Sun Fire V20z
+  main_memory:
+    ram_size: 2146435072
+  processor:
+    model: AMD Opteron
+    cache_l1d: 65536
+    clock_speed: 2400000000
+    other_description: AMD Opteron(tm) Processor 250
+    version: 250
+    cache_l2: 1048576
+    cache_l3: 0
+    vendor: AMD
+    instruction_set: x86-64
+    cache_l1i: 65536
+  architecture:
+    platform_type: x86_64
+    smt_size: 2
+    smp_size: 2
diff --git a/input/example/input/sites/rouen/clusters/pomme/nodes/pomme-2.yml b/input/example/input/sites/rouen/clusters/pomme/nodes/pomme-2.yml
new file mode 100644
index 00000000000..369263b53e9
--- /dev/null
+++ b/input/example/input/sites/rouen/clusters/pomme/nodes/pomme-2.yml
@@ -0,0 +1,60 @@
+---
+pomme-2:
+  bios:
+    version: V1.35.2.2
+    release_date: 04/25/2006
+    vendor: Phoenix Technologies Ltd.
+  network_interfaces:
+    eth0:
+      mounted: false
+      mac: 00:09:3d:12:9e:13
+      management: false
+      enabled: false
+      interface: Ethernet
+      driver: tg3
+      mountable: false
+      ip: 172.16.49.101
+    eth1:
+      mounted: true
+      switch_port: "1:1"
+      rate: 1000000000
+      mac: 00:09:3d:12:9e:14
+      ip6: fe80::209:3dff:fe12:9e14
+      management: false
+      enabled: true
+      interface: Ethernet
+      driver: tg3
+      mountable: true
+      ip: 172.16.49.1
+    bmc:
+      mac: 00:09:3d:12:9e:15
+      ip: 172.17.49.1
+  block_devices:
+    sda:
+      model: MAT3073NC
+      device: sda
+      size: 73543163904
+      rev: 104
+  supported_job_types:
+    virtual: false
+  chassis:
+    serial_number: XG052536201
+    manufacturer: Sun Microsystems
+    product_name: Sun Fire V20z
+  main_memory:
+    ram_size: 2146435072
+  processor:
+    model: AMD Opteron
+    cache_l1d: 65536
+    clock_speed: 2400000000
+    other_description: AMD Opteron(tm) Processor 250
+    version: 250
+    cache_l2: 1048576
+    cache_l3: 0
+    vendor: AMD
+    instruction_set: x86-64
+    cache_l1i: 65536
+  architecture:
+    platform_type: x86_64
+    smt_size: 2
+    smp_size: 2
diff --git a/input/example/input/sites/rouen/clusters/pomme/pomme.yml b/input/example/input/sites/rouen/clusters/pomme/pomme.yml
new file mode 100644
index 00000000000..2fd8e4b5a23
--- /dev/null
+++ b/input/example/input/sites/rouen/clusters/pomme/pomme.yml
@@ -0,0 +1,63 @@
+---
+model: Sun Fire V20z
+created_at: "Sat, 01 Jul 2006 12:00:00 GMT"
+kavlan: true
+production: true
+type: cluster
+uid: pomme
+
+nodes:
+  pomme-<->:
+    type: node
+    performance:
+      core_flops: 3929000000
+      node_flops: 7440000000
+    main_memory:
+      virtual_size: null
+    processor:
+      cache_l1: null
+    supported_job_types:
+      deploy: true
+      besteffort: true
+      virtual: true
+    storage_devices:
+      - interface: SCSI
+        driver: mptspi
+        storage: HDD
+    network_adapters:
+      - rate: 1000000000
+        bridged: false
+        device: eth0
+        vendor: Broadcom
+        model: BCM5704
+      - rate: 1000000000
+        bridged: false
+        device: eth1
+        vendor: Broadcom
+        model: BCM5704
+      - rate: 100000000
+        device: bmc
+        enabled: true
+        management: true
+        mountable: false
+        mounted: false
+        interface: Ethernet
+        driver: bnx2
+    gpu:
+      gpu: false
+    monitoring:
+      wattmeter: true
+    sensors:
+      power:
+        available: true
+        per_outlets: true
+        via:
+          api:
+            metric: pdu
+          www:
+            url: http://wattmetre.lyon.grid5000.fr/GetWatts-json.php
+
+  pomme-2:
+    sensors:
+      power:
+        available: false
diff --git a/input/example/input/sites/rouen/network/gw-production.yaml b/input/example/input/sites/rouen/network/gw-production.yaml
new file mode 100644
index 00000000000..fed978d49b9
--- /dev/null
+++ b/input/example/input/sites/rouen/network/gw-production.yaml
@@ -0,0 +1,91 @@
+---
+gw-production:
+  model: Extreme Networks version 12.1.3.14
+  kind: router
+  site: lyon
+  snmp_community: public
+  vlans:
+    naming_pattern: Vlan%VLANID%
+    "vlan100":
+      administrative: yes
+      addresses:
+        - 172.16.63.254
+    "vlan101":
+      addresses:
+        - 172.17.63.254
+    "vlan500":
+      addresses:
+        - 192.168.4.13
+    "vlan701":
+      name: kavlan-1
+      addresses:
+        - 192.168.192.0/20
+    "vlan702":
+      name: kavlan-2
+      addresses:
+        - 192.168.208.0/20
+    "vlan703":
+      name: kavlan-3
+      addresses:
+        - 192.168.224.0/20
+    "vlan704":
+      name: kavlan-4
+      addresses:
+        - 10.12.0.0/18
+    "vlan705":
+      name: kavlan-5
+      addresses:
+        - 10.12.64.0/18	
+    "vlan706":
+      name: kavlan-6
+      addresses:
+        - 10.12.128.0/18
+    "vlan707":
+      name: kavlan-7
+      addresses:
+        - 10.12.192.0/18	
+    "vlan708":
+      name: kavlan-8
+      addresses:
+        - 10.13.0.0/18	
+    "vlan709":
+      name: kavlan-9
+      addresses:
+        - 10.13.64.0/18
+    "vlan713":
+      name: kavlan-13
+      addresses:
+        - 10.15.192.0/18
+  routes: {}
+  backplane_bps: 800000000000
+  mtu: 9216
+  linecards:
+    2:
+      naming_pattern: "%LINECARD%:%PORT%"
+      snmp_pattern: "BD-8810 Port %LINECARD%:%PORT%"
+      backplane_bps: 48000000000,
+      kind: node
+      rate: 1000000000
+      port: eth1
+      ports:
+        1: pomme-1
+        2: pomme-2
+    9:
+      naming_pattern: "%LINECARD%:%PORT%"
+      snmp_pattern: "BD-8810 Port %LINECARD%:%PORT%"
+      backplane_bps: 48000000000,
+      kind: switch
+      rate: 10000000000
+      ports:
+        2:
+          uid: force10
+          port: "0:42"
+        3:
+          uid: salome
+          port: "0:49"
+        4:
+          uid: renater-lyon
+          site: renater
+          kind: virtual
+  channels:
+    naming_pattern: Po%CHANNELID%
-- 
GitLab