From 3b9c44f3c493158621436356f4b7c30702156115 Mon Sep 17 00:00:00 2001 From: "Vladmir Sharhsov(warpc)" Date: Tue, 6 Aug 2013 15:29:18 +0400 Subject: [PATCH] Draft version of provision validations and params deduplications. --- astute.gemspec | 1 + bin/astute | 5 +- examples/example_new_provisioning.yaml | 135 ++++++++++------------ lib/astute/cli/enviroment.rb | 75 ++++++++++++ lib/astute/cli/schema.yaml | 152 +++++++++++++++++++++++++ lib/astute/cli/yaml_validator.rb | 43 +++++++ lib/astute/ext/hash.rb | 37 ++++++ 7 files changed, 372 insertions(+), 76 deletions(-) create mode 100644 lib/astute/cli/enviroment.rb create mode 100644 lib/astute/cli/schema.yaml create mode 100644 lib/astute/cli/yaml_validator.rb create mode 100644 lib/astute/ext/hash.rb diff --git a/astute.gemspec b/astute.gemspec index 8d3b1afb..93fcae4b 100644 --- a/astute.gemspec +++ b/astute.gemspec @@ -14,6 +14,7 @@ Gem::Specification.new do |s| s.add_dependency 'mcollective-client', '~> 2.2.4' #'2.3.1' s.add_dependency 'symboltable', '1.0.2' s.add_dependency 'rest-client', '~> 1.6.7' + s.add_dependency 'kwalify', '~> 0.7.2' s.add_development_dependency 'rspec', '2.13.0' s.add_development_dependency 'mocha', '0.13.3' diff --git a/bin/astute b/bin/astute index 84749567..c1a6204f 100755 --- a/bin/astute +++ b/bin/astute @@ -23,7 +23,7 @@ require 'optparse' require 'yaml' require 'astute' require 'astute/version' -require 'astute/enviroment' +require 'astute/cli/enviroment' class ConsoleReporter def report(msg) @@ -64,8 +64,7 @@ end reporter = ConsoleReporter.new Astute.logger = Logger.new(STDOUT) if opts[:verbose] -environment = Astute::Enviroment.load_file(opts[:filename]) -p environment['nodes'][0]['ip'] +environment = Astute::Cli::Enviroment.load_file(opts[:filename]) deploy_engine = nil diff --git a/examples/example_new_provisioning.yaml b/examples/example_new_provisioning.yaml index b617aada..876bf41b 100644 --- a/examples/example_new_provisioning.yaml +++ b/examples/example_new_provisioning.yaml @@ -1,3 +1,4 @@ +--- # Base config task_uuid: deployment_task engine: @@ -5,77 +6,65 @@ engine: username: cobbler password: cobbler -power_info: &power_info - power_type: ssh - power_user: root - name_servers: ! '"10.20.0.2"' - power_pass: /root/.ssh/bootstrap.rsa - netboot_enabled: '1' - -ks_meta: &ks_meta - mco_enable: 1 - mco_vhost: mcollective - mco_pskey: unset - mco_user: mcollective - puppet_enable: 0 - install_log_2_syslog: 1 - mco_password: marionette - puppet_auto_setup: 1 - puppet_master: fuelweb.domain.tld - mco_auto_setup: 1 - auth_key: ! '""' - puppet_version: 2.7.19 - mco_connector: rabbitmq - mco_host: 10.20.0.2 - -node_01: &node1 - name: controller-5 - hostname: controller-5.domain.tld - profile: centos-x86_64 -# fqdn: controller-5.domain.tld -# id: 5 -# uid: 5 -# mac: 08:00:27:E3:BC:28 -# ip: 10.20.0.41 -# power_address: 10.20.0.41 - <<: *power_info - #Write size in megabytes - ks_meta: - <<: *ks_meta - ks_spaces: ! '"[{\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:0d.0-scsi-0:0:0:0\", - \"volumes\": [{\"mount\": \"/boot\", \"type\": \"partition\", \"size\": 209715200}, - {\"type\": \"mbr\"}, {\"size\": 16959668224, \"type\": \"pv\", \"vg\": \"os\"}], - \"size\": 17179869184}, {\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:0d.0-scsi-1:0:0:0\", - \"volumes\": [{\"size\": 536860426240, \"type\": \"pv\", \"vg\": \"os\"}], \"size\": - 536870912000}, {\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:0d.0-scsi-2:0:0:0\", - \"volumes\": [{\"size\": 2411714314240, \"type\": \"pv\", \"vg\": \"os\"}], - \"size\": 2411724800000}, {\"type\": \"vg\", \"id\": \"os\", \"volumes\": [{\"mount\": - \"/\", \"type\": \"lv\", \"name\": \"root\", \"size\": 2963243016192}, {\"mount\": - \"swap\", \"type\": \"lv\", \"name\": \"swap\", \"size\": 2090065920}]}]"' - - interfaces: - eth0: - ip_address: 10.20.0.41 - netmask: 255.255.255.0 - dns_name: controller-5.domain.tld - static: '1' - mac_address: 08:00:27:E3:BC:28 - use_for_provision: true # ip, power_address, mac, fqdn - eth1: - mac_address: 08:00:27:0D:5C:B9 - use_for_provision: false # ip, power_address, mac, fqdn - eth2: - mac_address: 08:00:27:D3:4F:6C - interfaces_extra: - eth2: - onboot: 'no' - peerdns: 'no' - eth1: - onboot: 'no' - peerdns: 'no' - eth0: - onboot: 'yes' - peerdns: 'no' - +# Nodes nodes: -- <<: *node1 + - name: controller-22 + hostname: controller-22.domain.tld + + # Data for provision + profile: centos-x86_64 + ks_meta: + mco_enable: 1 + mco_vhost: mcollective + mco_pskey: unset + mco_user: mcollective + puppet_enable: 0 + install_log_2_syslog: 1 + mco_password: marionette + puppet_auto_setup: 1 + puppet_master: fuelweb.domain.tld + mco_auto_setup: 1 + auth_key: ! '""' + mco_connector: rabbitmq + mco_host: 10.20.0.2 + ks_spaces: ! '"[{\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:0d.0-scsi-0:0:0:0\", + \"volumes\": [{\"mount\": \"/boot\", \"type\": \"partition\", \"size\": 209715200}, + {\"type\": \"mbr\"}, {\"size\": 16959668224, \"type\": \"pv\", \"vg\": \"os\"}], + \"size\": 17179869184}, {\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:0d.0-scsi-1:0:0:0\", + \"volumes\": [{\"size\": 536860426240, \"type\": \"pv\", \"vg\": \"os\"}], \"size\": + 536870912000}, {\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:0d.0-scsi-2:0:0:0\", + \"volumes\": [{\"size\": 2411714314240, \"type\": \"pv\", \"vg\": \"os\"}], + \"size\": 2411724800000}, {\"type\": \"vg\", \"id\": \"os\", \"volumes\": [{\"mount\": + \"/\", \"type\": \"lv\", \"name\": \"root\", \"size\": 2963243016192}, {\"mount\": + \"swap\", \"type\": \"lv\", \"name\": \"swap\", \"size\": 2090065920}]}]"' + + power_type: ssh + power_user: root + name_servers: ! '"10.20.0.2"' + power_pass: /root/.ssh/bootstrap.rsa + netboot_enabled: '1' + interfaces: + - name: eth2 + ip_address: 10.20.0.187 + netmask: 255.255.255.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' + mac_address: 08:00:27:93:54:B0 + onboot: 'no' + peerdns: 'no' + - name: eth0 + ip_address: 10.20.0.6 # ip, power_address + netmask: 255.255.255.0 + dns_name: controller-22.domain.tld # fqdn + static: '0' + mac_address: 08:00:27:DE:91:C5 # mac + onboot: 'yes' + peerdns: 'no' + use_for_provision: true + #End data for provision \ No newline at end of file diff --git a/lib/astute/cli/enviroment.rb b/lib/astute/cli/enviroment.rb new file mode 100644 index 00000000..c8cc0456 --- /dev/null +++ b/lib/astute/cli/enviroment.rb @@ -0,0 +1,75 @@ +# Copyright 2013 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +require 'yaml' +require 'astute/ext/hash' +require 'astute/cli/yaml_validator' + +module Astute + class Cli::Enviroment + + def self.load_file(file) + @config = YAML.load_file(file) + validate_env + convert_to_full_conf + end + + def self.validate_env + validator = Cli::YamlValidator.new() + errors = validator.validate(@config) + + errors.each do |e| + puts "[#{e.path}] #{e.message}" + end + end + + def self.convert_to_full_conf + # Provision + @config['nodes'].each_with_index do |node, index| + provision_eth = node['interfaces'].find {|eth| eth['use_for_provision'] } + if provision_eth + node.reverse_merge!( + 'ip' => provision_eth['ip_address'], + 'power_address' => provision_eth['ip_address'], + 'mac' => provision_eth['mac_address'], + 'fqdn' => provision_eth['dns_name'] + ) + end + missing_keys = node.find_missing_keys(['ip', 'power_address', 'mac', 'fqdn']) + if provision_eth.nil? && missing_keys.empty? + raise "Please set 'use_for_provision' parameter for #{node['name']} + or set manually #{missing_keys.each {|k| p k}}" + end + node.reverse_merge!( + 'id' => index, + 'uid' => index + ) + + # Extend blocks interfaces and interfaces_extra to old formats + formated_interfaces = {} + interfaces_extra_interfaces = {} + node['interfaces'].each do |eth| + formated_interfaces[eth['name']] = eth + interfaces_extra_interfaces[eth['name']] = { + 'onboot' => eth['onboot'], + 'peerdns' => eth['onboot'] + } + end + node['interfaces'] = formated_interfaces + node['extra_interfaces'] = interfaces_extra_interfaces + end + @config + end + end +end \ No newline at end of file diff --git a/lib/astute/cli/schema.yaml b/lib/astute/cli/schema.yaml new file mode 100644 index 00000000..32899e79 --- /dev/null +++ b/lib/astute/cli/schema.yaml @@ -0,0 +1,152 @@ +type: map +mapping: + "task_uuid": + type: text + "engine": + type: map + mapping: + "url": + type: text + required: true + "username": + type: text + required: true + "password": + type: text + required: true + "nodes": + type: seq + sequence: + - type: map + mapping: + "id": + type: int + unique: yes + "uid": + type: int + unique: yes + "name": + type: text + required: true + unique: yes + "hostname": + type: text + required: true + "fqdn": + type: text + "profile": + type: text + required: true + enum: ["centos-x86_64", "ubuntu_1204_x86_64"] + "ip": + type: text + "mac": + type: text + "power_address": + type: text + "power_type": + type: text + required: true + "power_user": + type: text + required: true + "name_servers": + type: text + required: true + "power_pass": + type: text + required: true + "netboot_enabled": + type: text + required: true + "ks_meta": + type: map + mapping: + "mco_enable": + type: int + range: { min: 0, max: 1 } + required: true + "mco_vhost": + type: text + required: true + "mco_pskey": + type: text + required: true + "mco_user": + type: text + required: true + "mco_password": + type: text + required: true + "puppet_enable": + type: int + range: { min: 0, max: 1 } + required: true + "puppet_auto_setup": + type: int + range: { min: 0, max: 1 } + required: true + "puppet_master": + type: text + required: true + "mco_auto_setup": + type: int + range: { min: 0, max: 1 } + required: true + "auth_key": + type: text + required: true + "puppet_version": + type: text + "install_log_2_syslog": + type: int + range: { min: 0, max: 1 } + required: true + "mco_connector": + type: text + required: true + "mco_host": + type: text + required: true + "ks_spaces": + type: text + required: true + "interfaces": + type: seq + sequence: + - type: map + mapping: + "name": + type: text + required: true + unique: yes + "ip_address": + type: text + required: true + unique: yes + "netmask": + type: text + required: true + "dns_name": + type: text + required: true + unique: yes + "static": + type: text + #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 \ No newline at end of file diff --git a/lib/astute/cli/yaml_validator.rb b/lib/astute/cli/yaml_validator.rb new file mode 100644 index 00000000..8d879aa4 --- /dev/null +++ b/lib/astute/cli/yaml_validator.rb @@ -0,0 +1,43 @@ +# Copyright 2013 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +require 'kwalify' + +module Astute + module Cli + class YamlValidator < Kwalify::Validator + + def initialize + gem_path = Gem.loaded_specs['astute'].full_gem_path + schema_path = File.join(gem_path, 'lib', 'astute', 'cli', 'schema.yaml') + @schema = Kwalify::Yaml.load_file(schema_path) + super(@schema) + end + + # hook method called by Validator#validate() + def validate_hook(value, rule, path, errors) + # case rule.name + # when 'use_for_provision' + # if value['name'] == 'bad' + # reason = value['reason'] + # if !reason || reason.empty? + # msg = "reason is required when answer is 'bad'." + # errors << Kwalify::ValidationError.new(msg, path) + # end + # end + # end + end + end + end +end \ No newline at end of file diff --git a/lib/astute/ext/hash.rb b/lib/astute/ext/hash.rb new file mode 100644 index 00000000..32295e76 --- /dev/null +++ b/lib/astute/ext/hash.rb @@ -0,0 +1,37 @@ +# Copyright 2013 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +class Hash + + # The value of the existing keys are not overridden + def reverse_merge(another_hash) + another_hash.merge(self) + end + + def reverse_merge!(another_hash) + replace(reverse_merge(another_hash)) + end + + def find_missing_keys(array) + array.select { |key| is_missing_key?(key) } + #array.all? { |key| !self[key].nil? } + end + + def is_missing_key?(key) + self[key].nil? + #array.all? { |key| !self[key].nil? } + end + +end