Commit 968b5376 authored by Jérémie Gaidamour's avatar Jérémie Gaidamour
Browse files

[dev] input validator: check yaml format

parent 7150822f
module HashValidator::Validations
class Multi
attr_reader :validation
def initialize(validation)
@validation = validation
end
def get_validation()
@validation
end
end
end
class HashValidator::Validator::MultiHashValidator < HashValidator::Validator::Base
def initialize
super('_multihash')
end
def should_validate?(rhs)
rhs.is_a?(HashValidator::Validations::Multi)
end
def validate(key, values, validations, errors)
# pp "key: #{key} #{values} - #{validations}"
# Validate hash
unless values.is_a?(Hash)
errors[key] = presence_error_message
return
end
errors = (errors[key] = {})
values.each do |k, v|
HashValidator.validator_for(validations.get_validation()).validate(k, v, validations.get_validation(), errors)
end
# Cleanup errors (remove any empty nested errors)
errors.delete_if { |k,v| v.empty? }
end
end
HashValidator.append_validator(HashValidator::Validator::MultiHashValidator.new)
# YAML schema validator
# Inspired from XML DTD
# It uses https://github.com/jamesbrooks/hash_validator
# See also related tool:
# * http://rx.codesimply.com/
# * http://www.kuwata-lab.com/kwalify/ruby/users-guide.html
require 'json'
require 'hash_validator' # https://github.com/jamesbrooks/hash_validator
require './lib/multihash_validator' # custom validator
# Simple required_hash validator
HashValidator.append_validator(HashValidator::Validator::SimpleValidator.new('required_hash', lambda { |v| v.is_a?(Hash) }))
# Date validator
HashValidator.append_validator(HashValidator::Validator::SimpleValidator.new('date', lambda { |v| v.is_a?(Date) }))
# Recursively replace 'optional_' values of the validation hash by HashValidator::Optional objects
def add_optional_validator(h)
h.each_with_object({}) { |(k,v),g|
g[k] = if (Hash === v)
add_optional_validator(v)
elsif String === v
if v == 'optional_hash' || v == 'optional'
HashValidator.optional('required')
elsif v.start_with?('optional_')
HashValidator.optional(v.gsub('optional_', ''))
else
v
end
else
v
end
}
end
# Recursively replace hash containing '<multi>' by HashValidator::MultiHash objects
def add_multihash_validator(h)
h.each_with_object({}) { |(k,v),g|
g[k] = if (Hash === v)
if v.key?('<multi>')
HashValidator::Validations::Multi.new(v['<multi>'])
elsif v.key?('<optional_hash>')
HashValidator.optional(v['<optional_hash>'])
else
add_multihash_validator(v)
end
else
v
end
}
end
# Monkey patching of the SimpleValidator to get more useful error messages
class HashValidator::Validator::SimpleValidator < HashValidator::Validator::Base
def validate(key, value, validations, errors)
unless lambda.call(value)
errors[key] = "#{self.name} required (current value: #{value.class}:#{value})"
end
end
end
# Load validation schema from a YAML file
def load_yaml_schema(filename)
schema = YAML::load_file(filename)
schema = add_optional_validator(schema)
schema = add_multihash_validator(schema)
end
---
nodes: required_hash
model: string
created_at: required # string or date ?
kavlan: boolean
queues: array
priority: optional_string
uid: string
sites: required_hash
---
architecture:
platform_type: string
smp_size: integer
smt_size: integer
bios:
release_date: string
vendor: optional ## optional_string or null
version: required # string ?
chassis:
manufacturer: string
name: string
serial: optional # required # string ?
gpu:
<optional_hash>:
gpu: optional_boolean # required ?
gpu_count: optional_integer
gpu_vendor: optional_string
gpu_model: optional_string
kavlan: optional_hash
main_memory:
ram_size: integer
mic: optional_hash
monitoring:
<optional_hash>:
metric: optional_string
wattmeter: optional
temperature: optional_boolean
network_adapters:
<multi>:
network_address: optional
interface: string
enabled: boolean
mountable: boolean
mounted: boolean
bridged: optional_boolean # boolean ?
management: boolean
rate: optional ## numeric # integer ?
vendor: optional ## optional_string
model: optional_string
driver: optional_string
ip: optional_string
mac: optional_string
ib_switch_card: optional_string
ib_switch_card_pos: optional_string
switch: optional_string
switch_port: optional_string
hwid: optional_string
ip6: optional_string
guid: optional_string
version: optional # optional_string
line_card: optional_string
position: optional_integer
operating_system:
kernel: string
name: string
release: string
version: string
pdu: optional_hash
performance:
core_flops: numeric # integer ?
node_flops: numeric # integer ?
processor:
cache_l1: optional_integer
cache_l1d: integer
cache_l1i: integer
cache_l2: integer
cache_l3: integer
clock_speed: integer
instruction_set: string
model: string
other_description: string
vendor: string
version: required # string or numeric ?
sensors: optional_hash
status: optional_string
storage_devices:
<multi>:
interface: string
driver: string
vendor: optional # string
storage: string
model: string
rev: required # string or numeric ?
size: integer
raid: optional
timeread: optional_numeric
timewrite: optional_numeric
supported_job_types:
besteffort: boolean
deploy: boolean
max_walltime: integer
queues: array
virtual: optional # optional_boolean # string
addressing_plan: optional
\ No newline at end of file
---
clusters: required_hash
networks: required_hash
dom0: optional_hash
name: string
location: string
web: string
description: string
latitude: float
longitude: float
email_contact: email
sys_admin_contact: email
security_contact: email
user_support_contact: email
compilation_server: boolean
kavlan_ip_range: string
virt_ip_range: string
storage5k: boolean
production: boolean
laptops: optional_hash
g5ksubnet:
network: string
gateway: string
kavlans:
<multi>:
network: string
gateway: string
pdu: optional_hash
#!/usr/bin/ruby
require 'pp'
require 'fileutils'
require 'pathname'
require '../lib/input_loader'
require './lib/schema_validator'
schema_global = load_yaml_schema('schema-global.yaml')
schema_site = load_yaml_schema('schema-site.yaml')
schema_cluster = load_yaml_schema('schema-cluster.yaml')
schema_node = load_yaml_schema('schema-node.yaml')
def run_validator(uid, data, schema)
validator = HashValidator.validate(data, schema, strict = true)
if ! validator.valid?
errors = {uid => validator.errors}
puts errors.to_yaml
end
end
global_hash = load_yaml_file_hierarchy("../input/grid5000/")
run_validator('global', global_hash, schema_global) #
global_hash["sites"].each do |site_uid, site|
run_validator(site_uid, site, schema_site) #
site["clusters"].each do |cluster_uid, cluster|
run_validator(cluster_uid, cluster, schema_cluster) #
cluster["nodes"].each do |node_uid, node|
run_validator(node_uid, node, schema_node) #
end
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment