* 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:
parent
7aa7e9c15c
commit
e9c21e6282
|
@ -1,6 +1,6 @@
|
|||
# Nodes
|
||||
nodes:
|
||||
- name: controller-5
|
||||
- name: controller-8
|
||||
role: controller
|
||||
public_br: br-ex
|
||||
internal_br: br-mgmt
|
||||
|
@ -8,42 +8,48 @@ nodes:
|
|||
- name: eth2
|
||||
ip_address: 10.20.0.187
|
||||
netmask: 255.255.255.0
|
||||
static: '0'
|
||||
static: 0
|
||||
mac_address: '08:00:27:31:09:34'
|
||||
onboot: 'no'
|
||||
peerdns: 'no'
|
||||
network_name:
|
||||
- fixed
|
||||
- name: eth1
|
||||
ip_address: 10.20.0.186
|
||||
netmask: 255.255.255.0
|
||||
static: '0'
|
||||
static: 0
|
||||
mac_address: 08:00:27:93:54:B0
|
||||
onboot: 'no'
|
||||
peerdns: 'no'
|
||||
network_name:
|
||||
- management
|
||||
- storage
|
||||
- name: eth0
|
||||
#ip_address: 10.20.0.49 # ip, power_address
|
||||
#netmask: 255.255.255.0
|
||||
dns_name: controller-5.domain.tld # fqdn
|
||||
static: '0'
|
||||
mac_address: 08:00:27:53:0B:6C # mac
|
||||
dns_name: controller-8.domain.tld # fqdn
|
||||
static: 0
|
||||
mac_address: 08:00:27:83:80:92 # mac
|
||||
onboot: 'yes'
|
||||
peerdns: 'no'
|
||||
use_for_provision: true
|
||||
#End data for provision
|
||||
network_name:
|
||||
- public
|
||||
default_gateway: 10.20.0.1
|
||||
network_data:
|
||||
- name: public
|
||||
ip: 172.18.94.41
|
||||
dev: eth1
|
||||
netmask: 255.255.255.0
|
||||
gateway: 172.18.94.33
|
||||
- name:
|
||||
- management
|
||||
- storage
|
||||
ip: 10.20.0.45
|
||||
dev: eth0
|
||||
netmask: 255.255.255.0
|
||||
- name: fixed
|
||||
dev: eth2
|
||||
# network_data:
|
||||
# - name: public
|
||||
# ip: 172.18.94.41
|
||||
# dev: eth1
|
||||
# netmask: 255.255.255.0
|
||||
# gateway: 172.18.94.33
|
||||
# - name:
|
||||
# - management
|
||||
# - storage
|
||||
# ip: 10.20.0.45
|
||||
# dev: eth0
|
||||
# netmask: 255.255.255.0
|
||||
# - name: fixed
|
||||
# dev: eth2
|
||||
|
||||
attributes:
|
||||
master_ip: 10.20.0.2
|
||||
|
@ -92,6 +98,7 @@ attributes:
|
|||
password: cinder
|
||||
user: cinder
|
||||
floating_network_range: 10.20.0.150/28
|
||||
#fixed_network_range: CIDR
|
||||
fixed_network_range: 10.20.1.0/24
|
||||
base_syslog:
|
||||
syslog_port: '514'
|
||||
|
|
|
@ -6,7 +6,6 @@ engine:
|
|||
username: cobbler
|
||||
password: cobbler
|
||||
|
||||
|
||||
# These parameters can be overridden in the specification of a particular node
|
||||
common_node_settings:
|
||||
name_servers: "10.20.0.2"
|
||||
|
@ -37,9 +36,9 @@ common_ks_meta:
|
|||
|
||||
# Nodes
|
||||
nodes:
|
||||
- name: controller-5
|
||||
hostname: controller-5.domain.tld
|
||||
|
||||
- name: controller-8
|
||||
hostname: controller-8.domain.tld
|
||||
|
||||
# Data for provision
|
||||
profile: centos-x86_64
|
||||
ks_meta:
|
||||
|
@ -95,23 +94,23 @@ nodes:
|
|||
- name: eth2
|
||||
ip_address: 10.20.0.187
|
||||
netmask: 255.255.255.0
|
||||
static: '0'
|
||||
static: 0
|
||||
mac_address: '08:00:27:31:09:34'
|
||||
onboot: 'no'
|
||||
peerdns: 'no'
|
||||
- name: eth1
|
||||
ip_address: 10.20.0.186
|
||||
netmask: 255.255.255.0
|
||||
static: '0'
|
||||
static: 0
|
||||
mac_address: 08:00:27:93:54:B0
|
||||
onboot: 'no'
|
||||
peerdns: 'no'
|
||||
- name: eth0
|
||||
#ip_address: 10.20.0.49 # ip, power_address
|
||||
#netmask: 255.255.255.0
|
||||
dns_name: controller-22.domain.tld # fqdn
|
||||
static: '0'
|
||||
mac_address: 08:00:27:53:0B:6C # mac
|
||||
dns_name: controller-8.domain.tld # fqdn
|
||||
static: 1
|
||||
mac_address: 08:00:27:83:80:92 # mac
|
||||
onboot: 'yes'
|
||||
peerdns: 'no'
|
||||
use_for_provision: true
|
||||
|
|
|
@ -26,6 +26,9 @@ mapping:
|
|||
type: text
|
||||
required: true
|
||||
enum: ["primary-controller", "controller", "storage", "swift-proxy", "primary-swift-proxy", "compute", "quantum"]
|
||||
"status":
|
||||
type: text
|
||||
enum: ["ready", "provisioned", "discover"]
|
||||
# Quantum true
|
||||
"public_br":
|
||||
type: text
|
||||
|
@ -34,10 +37,53 @@ mapping:
|
|||
"internal_br":
|
||||
type: text
|
||||
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":
|
||||
type: seq
|
||||
desc: Array of network interfaces hashes
|
||||
required: true
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
|
|
|
@ -34,10 +34,13 @@ module Astute
|
|||
PROVISION_OPERATIONS = [:provision, :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)
|
||||
@config = YAML.load_file(file)
|
||||
validate_enviroment(operation)
|
||||
to_full_config(operation)
|
||||
to_full_config(operation)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
|
@ -68,32 +71,65 @@ module Astute
|
|||
if DEPLOY_OPERATIONS.include? operation
|
||||
define_meta_interfaces(node)
|
||||
define_fqdn(node)
|
||||
define_network_data(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def validate_enviroment(operation)
|
||||
validator = YamlValidator.new(operation)
|
||||
errors = validator.validate(@config)
|
||||
|
||||
errors.each do |e|
|
||||
title = e.message.include?("is undefined") ? "WARNING" : "ERROR"
|
||||
puts "#{title}: [#{e.path}] #{e.message}"
|
||||
if e.message.include?("is undefined")
|
||||
Astute.logger.debug "WARNING: [#{e.path}] #{e.message}"
|
||||
else
|
||||
Astute.logger.error "ERROR: [#{e.path}] #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
if errors.select {|e| !e.message.include?("is undefined") }.size > 0
|
||||
raise Enviroment::ValidationError, "Environment validation failed"
|
||||
end
|
||||
|
||||
if PROVISION_OPERATIONS.include? operation && @config['attributes']['quantum']
|
||||
@config['nodes'].each do |node|
|
||||
['public_br', 'internal_br'].each do |br|
|
||||
if node[br].nil? || node[br].empty?
|
||||
raise Enviroment::ValidationError, "Node #{node['name'] || node['hostname']}
|
||||
required 'public_br' and 'internal_br' when quantum is 'true'"
|
||||
if DEPLOY_OPERATIONS.include?(operation)
|
||||
if @config['attributes']['quantum']
|
||||
@config['nodes'].each do |node|
|
||||
['public_br', 'internal_br'].each do |br|
|
||||
if node[br].nil? || node[br].empty?
|
||||
raise Enviroment::ValidationError, "Node #{node['name'] || node['hostname']}
|
||||
required 'public_br' and 'internal_br' when quantum is 'true'"
|
||||
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
|
||||
|
@ -105,8 +141,8 @@ module Astute
|
|||
@api_data = JSON.parse(response).freeze
|
||||
end
|
||||
if node['mac']
|
||||
node = @api_data.find{ |n| n['mac'].upcase == node['mac'].upcase }
|
||||
return node if node
|
||||
api_node = @api_data.find{ |n| n['mac'].upcase == node['mac'].upcase }
|
||||
return api_node if api_node
|
||||
end
|
||||
raise Enviroment::ValidationError, "Node #{node['name']} with mac address #{node['mac']}
|
||||
not find among discovered nodes"
|
||||
|
@ -249,6 +285,44 @@ module Astute
|
|||
def define_meta_interfaces(node)
|
||||
node['meta']['interfaces'] = find_node_api_data(node)['meta']['interfaces']
|
||||
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'
|
||||
# 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'].delete('ks_disks')
|
||||
end
|
||||
|
||||
def is_cidr_notation?(value)
|
||||
cidr = Regexp.new(CIDR_REGEXP)
|
||||
!cidr.match(value).nil?
|
||||
end
|
||||
|
||||
end # class end
|
||||
|
||||
class Enviroment::ValidationError < StandardError; end
|
||||
|
|
|
@ -141,9 +141,9 @@ mapping:
|
|||
type: text
|
||||
desc: Password/credentials for cobbler to manage power of this machine
|
||||
"netboot_enabled":
|
||||
type: text
|
||||
type: int
|
||||
range: { min: 0, max: 1 }
|
||||
desc: Disable/enable netboot for this node.
|
||||
enum: ['0', '1']
|
||||
"ks_meta":
|
||||
type: map
|
||||
required: true
|
||||
|
@ -240,8 +240,8 @@ mapping:
|
|||
type: text
|
||||
unique: yes
|
||||
"static":
|
||||
type: text
|
||||
#range: { min: 0, max: 1 }
|
||||
type: int
|
||||
range: { min: 0, max: 1 }
|
||||
"mac_address":
|
||||
type: text
|
||||
required: true
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
|
||||
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 Cli
|
||||
class YamlValidator < Kwalify::Validator
|
||||
|
@ -31,59 +28,18 @@ module Astute
|
|||
end
|
||||
|
||||
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|
|
||||
schema_path = File.join(schema_dir_path, "#{schema_name}_schema.yaml")
|
||||
schema_hashes << YAML.load_file(schema_path)
|
||||
end
|
||||
|
||||
#p schema_hashes[0].recursive_merge!(schema_hashes[1])
|
||||
#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])
|
||||
super(@schema)
|
||||
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 # Cli
|
||||
|
|
|
@ -31,7 +31,7 @@ module Astute
|
|||
attrs['use_cinder'] ||= nodes.any?{|n| n['role'] == 'cinder'}
|
||||
@ctx.deploy_log_parser.deploy_type = 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
|
||||
|
||||
def method_missing(method, *args)
|
||||
|
|
|
@ -36,6 +36,19 @@ class Hash
|
|||
!absent?(key)
|
||||
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)
|
||||
self.merge(other_hash) do |key, oldval, newval|
|
||||
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
|
||||
|
|
|
@ -14,5 +14,5 @@
|
|||
|
||||
|
||||
module Astute
|
||||
VERSION = '0.0.1'
|
||||
VERSION = '0.0.2'
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue