* generate node network data section (deduplication);

* fix validation for quantum params;
* update deploy and provision examples;
* up version to 0.0.2
This commit is contained in:
Vladmir Sharhsov(warpc) 2013-08-20 10:32:06 +04:00
parent 7aa7e9c15c
commit e9c21e6282
9 changed files with 197 additions and 96 deletions

View File

@ -1,6 +1,6 @@
# Nodes # Nodes
nodes: nodes:
- name: controller-5 - name: controller-8
role: controller role: controller
public_br: br-ex public_br: br-ex
internal_br: br-mgmt internal_br: br-mgmt
@ -8,42 +8,48 @@ nodes:
- name: eth2 - name: eth2
ip_address: 10.20.0.187 ip_address: 10.20.0.187
netmask: 255.255.255.0 netmask: 255.255.255.0
static: '0' static: 0
mac_address: '08:00:27:31:09:34' mac_address: '08:00:27:31:09:34'
onboot: 'no' onboot: 'no'
peerdns: 'no' peerdns: 'no'
network_name:
- fixed
- name: eth1 - name: eth1
ip_address: 10.20.0.186 ip_address: 10.20.0.186
netmask: 255.255.255.0 netmask: 255.255.255.0
static: '0' static: 0
mac_address: 08:00:27:93:54:B0 mac_address: 08:00:27:93:54:B0
onboot: 'no' onboot: 'no'
peerdns: 'no' peerdns: 'no'
network_name:
- management
- storage
- name: eth0 - name: eth0
#ip_address: 10.20.0.49 # ip, power_address #ip_address: 10.20.0.49 # ip, power_address
#netmask: 255.255.255.0 #netmask: 255.255.255.0
dns_name: controller-5.domain.tld # fqdn dns_name: controller-8.domain.tld # fqdn
static: '0' static: 0
mac_address: 08:00:27:53:0B:6C # mac mac_address: 08:00:27:83:80:92 # mac
onboot: 'yes' onboot: 'yes'
peerdns: 'no' peerdns: 'no'
use_for_provision: true use_for_provision: true
#End data for provision network_name:
- public
default_gateway: 10.20.0.1 default_gateway: 10.20.0.1
network_data: # network_data:
- name: public # - name: public
ip: 172.18.94.41 # ip: 172.18.94.41
dev: eth1 # dev: eth1
netmask: 255.255.255.0 # netmask: 255.255.255.0
gateway: 172.18.94.33 # gateway: 172.18.94.33
- name: # - name:
- management # - management
- storage # - storage
ip: 10.20.0.45 # ip: 10.20.0.45
dev: eth0 # dev: eth0
netmask: 255.255.255.0 # netmask: 255.255.255.0
- name: fixed # - name: fixed
dev: eth2 # dev: eth2
attributes: attributes:
master_ip: 10.20.0.2 master_ip: 10.20.0.2
@ -92,6 +98,7 @@ attributes:
password: cinder password: cinder
user: cinder user: cinder
floating_network_range: 10.20.0.150/28 floating_network_range: 10.20.0.150/28
#fixed_network_range: CIDR
fixed_network_range: 10.20.1.0/24 fixed_network_range: 10.20.1.0/24
base_syslog: base_syslog:
syslog_port: '514' syslog_port: '514'

View File

@ -6,7 +6,6 @@ engine:
username: cobbler username: cobbler
password: cobbler password: cobbler
# These parameters can be overridden in the specification of a particular node # These parameters can be overridden in the specification of a particular node
common_node_settings: common_node_settings:
name_servers: "10.20.0.2" name_servers: "10.20.0.2"
@ -37,8 +36,8 @@ common_ks_meta:
# Nodes # Nodes
nodes: nodes:
- name: controller-5 - name: controller-8
hostname: controller-5.domain.tld hostname: controller-8.domain.tld
# Data for provision # Data for provision
profile: centos-x86_64 profile: centos-x86_64
@ -95,23 +94,23 @@ nodes:
- name: eth2 - name: eth2
ip_address: 10.20.0.187 ip_address: 10.20.0.187
netmask: 255.255.255.0 netmask: 255.255.255.0
static: '0' static: 0
mac_address: '08:00:27:31:09:34' mac_address: '08:00:27:31:09:34'
onboot: 'no' onboot: 'no'
peerdns: 'no' peerdns: 'no'
- name: eth1 - name: eth1
ip_address: 10.20.0.186 ip_address: 10.20.0.186
netmask: 255.255.255.0 netmask: 255.255.255.0
static: '0' static: 0
mac_address: 08:00:27:93:54:B0 mac_address: 08:00:27:93:54:B0
onboot: 'no' onboot: 'no'
peerdns: 'no' peerdns: 'no'
- name: eth0 - name: eth0
#ip_address: 10.20.0.49 # ip, power_address #ip_address: 10.20.0.49 # ip, power_address
#netmask: 255.255.255.0 #netmask: 255.255.255.0
dns_name: controller-22.domain.tld # fqdn dns_name: controller-8.domain.tld # fqdn
static: '0' static: 1
mac_address: 08:00:27:53:0B:6C # mac mac_address: 08:00:27:83:80:92 # mac
onboot: 'yes' onboot: 'yes'
peerdns: 'no' peerdns: 'no'
use_for_provision: true use_for_provision: true

View File

@ -26,6 +26,9 @@ mapping:
type: text type: text
required: true required: true
enum: ["primary-controller", "controller", "storage", "swift-proxy", "primary-swift-proxy", "compute", "quantum"] enum: ["primary-controller", "controller", "storage", "swift-proxy", "primary-swift-proxy", "compute", "quantum"]
"status":
type: text
enum: ["ready", "provisioned", "discover"]
# Quantum true # Quantum true
"public_br": "public_br":
type: text type: text
@ -34,10 +37,53 @@ mapping:
"internal_br": "internal_br":
type: text type: text
desc: Name of the internal bridge for Quantum-enabled configuration desc: Name of the internal bridge for Quantum-enabled configuration
"interfaces":
type: seq
required: true
sequence:
- type: map
mapping:
"name":
type: text
required: true
unique: yes
"ip_address":
type: text
unique: yes
"netmask":
type: text
"dns_name":
type: text
unique: yes
"static":
type: int
range: { min: 0, max: 1 }
"mac_address":
type: text
required: true
unique: yes
"onboot":
type: text
required: true
enum: ['yes', 'no']
"peerdns":
type: text
required: true
enum: ['yes', 'no']
"use_for_provision":
type: bool
default: false
name: use_for_provision
"network_name":
type: seq
desc: Array of OpenStack network names
sequence:
- type: text
enum: ["public", "management", "storage", "fixed"]
# Full config block
"network_data": "network_data":
type: seq type: seq
desc: Array of network interfaces hashes desc: Array of network interfaces hashes
required: true
sequence: sequence:
- type: map - type: map
mapping: mapping:

View File

@ -34,6 +34,9 @@ module Astute
PROVISION_OPERATIONS = [:provision, :provision_and_deploy] PROVISION_OPERATIONS = [:provision, :provision_and_deploy]
DEPLOY_OPERATIONS = [:deploy, :provision_and_deploy] DEPLOY_OPERATIONS = [:deploy, :provision_and_deploy]
CIDR_REGEXP = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|
2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$'
def initialize(file, operation) def initialize(file, operation)
@config = YAML.load_file(file) @config = YAML.load_file(file)
validate_enviroment(operation) validate_enviroment(operation)
@ -68,25 +71,29 @@ module Astute
if DEPLOY_OPERATIONS.include? operation if DEPLOY_OPERATIONS.include? operation
define_meta_interfaces(node) define_meta_interfaces(node)
define_fqdn(node) define_fqdn(node)
define_network_data(node)
end end
end end
end end
def validate_enviroment(operation) def validate_enviroment(operation)
validator = YamlValidator.new(operation) validator = YamlValidator.new(operation)
errors = validator.validate(@config) errors = validator.validate(@config)
errors.each do |e| errors.each do |e|
title = e.message.include?("is undefined") ? "WARNING" : "ERROR" if e.message.include?("is undefined")
puts "#{title}: [#{e.path}] #{e.message}" Astute.logger.debug "WARNING: [#{e.path}] #{e.message}"
else
Astute.logger.error "ERROR: [#{e.path}] #{e.message}"
end
end end
if errors.select {|e| !e.message.include?("is undefined") }.size > 0 if errors.select {|e| !e.message.include?("is undefined") }.size > 0
raise Enviroment::ValidationError, "Environment validation failed" raise Enviroment::ValidationError, "Environment validation failed"
end end
if PROVISION_OPERATIONS.include? operation && @config['attributes']['quantum'] if DEPLOY_OPERATIONS.include?(operation)
if @config['attributes']['quantum']
@config['nodes'].each do |node| @config['nodes'].each do |node|
['public_br', 'internal_br'].each do |br| ['public_br', 'internal_br'].each do |br|
if node[br].nil? || node[br].empty? if node[br].nil? || node[br].empty?
@ -95,6 +102,35 @@ module Astute
end end
end end
end end
errors = []
['quantum_parameters', 'quantum_access'].each do |param|
errors << param unless @config['attributes'].present?(param)
end
errors.each do |field|
msg = "#{field} is required when quantim is true"
raise Enviroment::ValidationError, msg
end
if !is_cidr_notation?(@config['attributes']['floating_network_range'])
msg = "'floating_network_range' is required CIDR notation when quantum is 'true'"
raise Enviroment::ValidationError, msg
end
if !is_cidr_notation?(@config['attributes']['floating_network_range'])
msg = "'floating_network_range' is required CIDR notation"
raise Enviroment::ValidationError, msg
end
else
if @config['attributes']['floating_network_range'].is_a?(Array)
msg = "'floating_network_range' is required array of IPs when quantum is 'false'"
raise Enviroment::ValidationError, msg
end
end
if !is_cidr_notation?(@config['attributes']['fixed_network_range'])
msg = "'fixed_network_range' is required CIDR notation"
raise Enviroment::ValidationError, msg
end
end end
end end
@ -105,8 +141,8 @@ module Astute
@api_data = JSON.parse(response).freeze @api_data = JSON.parse(response).freeze
end end
if node['mac'] if node['mac']
node = @api_data.find{ |n| n['mac'].upcase == node['mac'].upcase } api_node = @api_data.find{ |n| n['mac'].upcase == node['mac'].upcase }
return node if node return api_node if api_node
end end
raise Enviroment::ValidationError, "Node #{node['name']} with mac address #{node['mac']} raise Enviroment::ValidationError, "Node #{node['name']} with mac address #{node['mac']}
not find among discovered nodes" not find among discovered nodes"
@ -250,6 +286,44 @@ module Astute
node['meta']['interfaces'] = find_node_api_data(node)['meta']['interfaces'] node['meta']['interfaces'] = find_node_api_data(node)['meta']['interfaces']
end end
# Add network_data section for node:
# network_data:
# - dev: eth1
# ip: 10.108.1.8
# name: public
# netmask: 255.255.255.0
# - dev: eth0
# ip: 10.108.0.8
# name:
# - management
# - storage
def define_network_data(node)
return if node['network_data'].is_a?(Array) && !node['network_data'].empty?
node['network_data'] = []
# If define_interfaces_and_interfaces_extra was call or format of config is full
if node['interfaces'].is_a?(Hash)
node['interfaces'].each do |key, value|
node['network_data'] << {
'dev' => key,
'ip' => value['ip_address'],
'name' => value['network_name'],
'netmask' => value['netmask']
}
end
else
node['interfaces'].each do |eth|
node['network_data'] << {
'dev' => eth['name'],
'ip' => eth['ip_address'],
'name' => eth['network_name'],
'netmask' => eth['netmask']
}
end
end
end
# Generate 'ks_spaces' param from 'ks_disks' param in section 'ks_meta' # Generate 'ks_spaces' param from 'ks_disks' param in section 'ks_meta'
# Example input for 'ks_disks' param: # Example input for 'ks_disks' param:
# [{ # [{
@ -283,6 +357,12 @@ module Astute
node['ks_meta']['ks_spaces'] = '"' + node['ks_meta']['ks_disks'].to_json.gsub("\"", "\\\"") + '"' node['ks_meta']['ks_spaces'] = '"' + node['ks_meta']['ks_disks'].to_json.gsub("\"", "\\\"") + '"'
node['ks_meta'].delete('ks_disks') node['ks_meta'].delete('ks_disks')
end end
def is_cidr_notation?(value)
cidr = Regexp.new(CIDR_REGEXP)
!cidr.match(value).nil?
end
end # class end end # class end
class Enviroment::ValidationError < StandardError; end class Enviroment::ValidationError < StandardError; end

View File

@ -141,9 +141,9 @@ mapping:
type: text type: text
desc: Password/credentials for cobbler to manage power of this machine desc: Password/credentials for cobbler to manage power of this machine
"netboot_enabled": "netboot_enabled":
type: text type: int
range: { min: 0, max: 1 }
desc: Disable/enable netboot for this node. desc: Disable/enable netboot for this node.
enum: ['0', '1']
"ks_meta": "ks_meta":
type: map type: map
required: true required: true
@ -240,8 +240,8 @@ mapping:
type: text type: text
unique: yes unique: yes
"static": "static":
type: text type: int
#range: { min: 0, max: 1 } range: { min: 0, max: 1 }
"mac_address": "mac_address":
type: text type: text
required: true required: true

View File

@ -14,9 +14,6 @@
require 'kwalify' require 'kwalify'
CIDR_REGEXP = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|
2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$'
module Astute module Astute
module Cli module Cli
class YamlValidator < Kwalify::Validator class YamlValidator < Kwalify::Validator
@ -32,59 +29,18 @@ module Astute
schema_hashes = [] schema_hashes = []
schema_dir_path = File.expand_path(File.dirname(__FILE__)) schema_dir_path = File.expand_path(File.dirname(__FILE__))
schemas.each do |schema_name| schemas.each do |schema_name|
schema_path = File.join(schema_dir_path, "#{schema_name}_schema.yaml") schema_path = File.join(schema_dir_path, "#{schema_name}_schema.yaml")
schema_hashes << YAML.load_file(schema_path) schema_hashes << YAML.load_file(schema_path)
end end
#p schema_hashes[0].recursive_merge!(schema_hashes[1])
#FIXME: key 'hostname:' is undefined for provision_and_deploy. Why? #FIXME: key 'hostname:' is undefined for provision_and_deploy. Why?
@schema = schema_hashes.size == 1 ? schema_hashes.first : schema_hashes[0].deep_merge(schema_hashes[1]) @schema = schema_hashes.size == 1 ? schema_hashes.first : schema_hashes[0].deep_merge(schema_hashes[1])
super(@schema) super(@schema)
end end
# hook method called by Validator#validate()
def validate_hook(value, rule, path, errors)
case rule.name
when 'Attributes'
require_field(value, path, errors, 'quantum', true, 'quantum_parameters')
require_field(value, path, errors, 'quantum', true, 'fixed_network_range')
require_field(value, path, errors, 'quantum', true, 'quantum_access')
#require_field(value, path, errors, 'quantum', false, 'floating_network_range')
if value['quantum']
is_cidr_notation?(value['floating_network_range'])
msg = "'floating_network_range' is required CIDR notation when quantum is 'true'"
errors << Kwalify::ValidationError.new(msg, path)
elsif !floating_network_range.is_a?(Array)
msg = "'floating_network_range' is required array of IPs when quantum is 'false'"
errors << Kwalify::ValidationError.new(msg, path)
end
if !is_cidr_notation?(value['fixed_network_range'])
msg = "'floating_network_range' is required CIDR notation"
errors << Kwalify::ValidationError.new(msg, path)
end
when 'Nodes'
#require_field(value, path, errors, 'quantum', true, 'public_br')
#require_field(value, path, errors, 'quantum', true, 'internal_br')
end
end
private
def is_cidr_notation?(value)
cidr = Regexp.new(CIDR_REGEXP)
!cidr.match(value).nil?
end
def require_field(value, path, errors, condition_key, condition_value, key)
return if value[condition_key] != condition_value
field_value = value[key]
if field_value.nil? || field_value.empty?
msg = "#{key} is required when #{condition_key} is '#{condition_value}'"
errors << Kwalify::ValidationError.new(msg, path)
end
end
end # YamlValidator end # YamlValidator
end # Cli end # Cli
end # Astute end # Astute

View File

@ -31,7 +31,7 @@ module Astute
attrs['use_cinder'] ||= nodes.any?{|n| n['role'] == 'cinder'} attrs['use_cinder'] ||= nodes.any?{|n| n['role'] == 'cinder'}
@ctx.deploy_log_parser.deploy_type = attrs['deployment_mode'] @ctx.deploy_log_parser.deploy_type = attrs['deployment_mode']
Astute.logger.info "Deployment mode #{attrs['deployment_mode']}" Astute.logger.info "Deployment mode #{attrs['deployment_mode']}"
result = self.send("deploy_#{attrs['deployment_mode']}", nodes, attrs) self.send("deploy_#{attrs['deployment_mode']}", nodes, attrs)
end end
def method_missing(method, *args) def method_missing(method, *args)

View File

@ -36,6 +36,19 @@ class Hash
!absent?(key) !absent?(key)
end end
# def recursive_merge!(other)
# other.keys.each do |k|
# if self[k].is_a?(Array) && other[k].is_a?(Array)
# self[k] += other[k]
# elsif self[k].is_a?(Hash) && other[k].is_a?(Hash)
# self[k].recursive_merge!(other[k])
# else
# self[k] = other[k]
# end
# end
# self
# end
def deep_merge(other_hash) def deep_merge(other_hash)
self.merge(other_hash) do |key, oldval, newval| self.merge(other_hash) do |key, oldval, newval|
oldval = oldval.to_hash if oldval.respond_to?(:to_hash) oldval = oldval.to_hash if oldval.respond_to?(:to_hash)

View File

@ -14,5 +14,5 @@
module Astute module Astute
VERSION = '0.0.1' VERSION = '0.0.2'
end end