diff --git a/examples/example_new_deploy.yaml b/examples/example_new_deploy.yaml index dfcbe751..653d8752 100644 --- a/examples/example_new_deploy.yaml +++ b/examples/example_new_deploy.yaml @@ -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' diff --git a/examples/example_new_provisioning.yaml b/examples/example_new_provisioning.yaml index 29098fdb..074d9d87 100644 --- a/examples/example_new_provisioning.yaml +++ b/examples/example_new_provisioning.yaml @@ -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 diff --git a/lib/astute/cli/deploy_schema.yaml b/lib/astute/cli/deploy_schema.yaml index 2e4a2296..785be2de 100644 --- a/lib/astute/cli/deploy_schema.yaml +++ b/lib/astute/cli/deploy_schema.yaml @@ -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: diff --git a/lib/astute/cli/enviroment.rb b/lib/astute/cli/enviroment.rb index 82bf76bf..446f52a3 100644 --- a/lib/astute/cli/enviroment.rb +++ b/lib/astute/cli/enviroment.rb @@ -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 diff --git a/lib/astute/cli/provision_schema.yaml b/lib/astute/cli/provision_schema.yaml index 2ec34463..b380347c 100644 --- a/lib/astute/cli/provision_schema.yaml +++ b/lib/astute/cli/provision_schema.yaml @@ -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 diff --git a/lib/astute/cli/yaml_validator.rb b/lib/astute/cli/yaml_validator.rb index 37f36213..c7cc8627 100644 --- a/lib/astute/cli/yaml_validator.rb +++ b/lib/astute/cli/yaml_validator.rb @@ -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 diff --git a/lib/astute/deployment_engine.rb b/lib/astute/deployment_engine.rb index 3ced83c2..78b9b7b6 100644 --- a/lib/astute/deployment_engine.rb +++ b/lib/astute/deployment_engine.rb @@ -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) diff --git a/lib/astute/ext/hash.rb b/lib/astute/ext/hash.rb index 0bea6a52..dfdbd3b9 100644 --- a/lib/astute/ext/hash.rb +++ b/lib/astute/ext/hash.rb @@ -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) diff --git a/lib/astute/version.rb b/lib/astute/version.rb index e3118f97..18f3eb1a 100644 --- a/lib/astute/version.rb +++ b/lib/astute/version.rb @@ -14,5 +14,5 @@ module Astute - VERSION = '0.0.1' + VERSION = '0.0.2' end