diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..f68a9031 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gemspec + diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..772929d2 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,44 @@ +PATH + remote: . + specs: + astute (0.0.1) + activesupport (= 3.0.10) + mcollective-client (~> 2.2.4) + rest-client (~> 1.6.7) + symboltable (= 1.0.2) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (3.0.10) + diff-lcs (1.2.4) + json (1.8.0) + mcollective-client (2.2.4) + json + stomp + systemu + metaclass (0.0.1) + mime-types (1.23) + mocha (0.13.3) + metaclass (~> 0.0.1) + rest-client (1.6.7) + mime-types (>= 1.16) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + stomp (1.2.11) + symboltable (1.0.2) + systemu (2.5.2) + +PLATFORMS + ruby + +DEPENDENCIES + astute! + mocha (= 0.13.3) + rspec (= 2.13.0) diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 00000000..3dca57f4 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,2 @@ +* CLI client should be provided for API (with all available operations supported) +* Component to be used for orchestration transport layer (MComponent for orchestration transport layer should be pluggable. In addition to default option (MCollective) user should be able to prepare custom plugin (to use Salt, SSH, python Fabric etc) and rComponent for orchestration transport layer should be pluggable. In addition to default option (MCollective) user should be able to prepare custom plugin (to use Salt, SSH, python Fabric etc) and rCollective by default): diff --git a/astute.gemspec b/astute.gemspec index f65404be..8d3b1afb 100644 --- a/astute.gemspec +++ b/astute.gemspec @@ -11,8 +11,9 @@ Gem::Specification.new do |s| s.email = ['mscherbakov@mirantis.com'] s.add_dependency 'activesupport', '3.0.10' - s.add_dependency 'mcollective-client', '2.3.1' + 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_development_dependency 'rspec', '2.13.0' s.add_development_dependency 'mocha', '0.13.3' diff --git a/astute2.yaml b/astute2.yaml new file mode 100644 index 00000000..d6fe0fad --- /dev/null +++ b/astute2.yaml @@ -0,0 +1,207 @@ +##Network section of node configuration +node_01: &node_01 + role: primary-controller + network_data: + - name: public + ip: 10.108.2.94 + dev: eth0 + netmask: 255.255.255.0 + gateway: 10.108.2.1 + - name: + - management + - storage + ip: 10.20.1.94 + dev: eth1 + - name: fixed + dev: eth2 + public_br: br-ex + internal_br: br-mgmt + id: 01 + default_gateway: 10.20.0.1 + uid: 01 + mac: 52:54:00:de:bc:77 + name: controller-01 + ip: 10.108.2.94 + profile: centos-x86_64 + fqdn: controller-01.domain.tld + power_type: ssh + power_user: root + power_pass: /root/.ssh/bootstrap.rsa + power_address: 10.108.2.94 + netboot_enabled: '1' + name_servers: ! '"10.108.2.2"' + ks_meta: + ks_spaces: '"[ + {\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:01.1-scsi-0:0:0:0\", + \"volumes\": + [ + {\"mount\": \"/boot\", \"type\": \"partition\", \"size\": 200 }, + {\"type\": \"mbr\"}, {\"size\": 7989, \"type\": \"pv\", \"vg\": \"os\"} + ], + \"size\": 8190 + }, + {\"type\": \"vg\", \"id\": \"os\", \"volumes\": + [ + {\"mount\": \"/\", \"type\": \"lv\", \"name\": \"root\", \"size\": 7166 }, + {\"mount\":\"swap\", \"type\": \"lv\", \"name\": \"swap\", \"size\": 1024 } + ] + } + ]"' + 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: fuel.domain.tld + mco_auto_setup: 1 + auth_key: ! '""' + puppet_version: 2.7.19 + mco_connector: rabbitmq + mco_host: 10.108.2.2 + interfaces: + eth0: + ip_address: 10.108.2.94 + netmask: 255.255.255.0 + dns_name: controller-01.domain.tld + static: '1' + mac_address: 52:54:00:de:bc:77 + interfaces_extra: + eth2: + onboot: 'no' + peerdns: 'no' + eth1: + onboot: 'no' + peerdns: 'no' + eth0: + onboot: 'yes' + peerdns: 'no' + meta: + memory: + total: 778694656 + interfaces: + - mac: 64:D8:E1:F6:66:43 + max_speed: 100 + name: eth2 + ip: 10.22.0.94 + netmask: 255.255.255.0 + current_speed: 100 + - mac: 64:C8:E2:3B:FD:6E + max_speed: 100 + name: eth1 + ip: 10.21.0.94 + netmask: 255.255.255.0 + current_speed: 100 + - name: eth0 + ip: 10.20.0.94 + netmask: 255.255.255.0 + mac: 64:43:7B:CA:56:DD + max_speed: 100 + current_speed: 100 + disks: + - model: VBOX HARDDISK + disk: disk/by-path/pci-0000:00:0d.0-scsi-2:0:0:0 + name: sdc + size: 2411724800000 + - model: VBOX HARDDISK + disk: disk/by-path/pci-0000:00:0d.0-scsi-1:0:0:0 + name: sdb + size: 536870912000 + - model: VBOX HARDDISK + disk: disk/by-path/pci-0000:00:0d.0-scsi-0:0:0:0 + name: sda + size: 17179869184 + system: + serial: '0' + version: '1.2' + fqdn: bootstrap + family: Virtual Machine + manufacturer: VirtualBox + cpu: + real: 0 + total: 1 + spec: + - model: Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz + frequency: 2397 + error_type: + +nodes: + - <<: *node_01 + +attributes: + use_cow_images: true + libvirt_type: qemu + dns_nameservers: + - 10.20.0.1 + verbose: true|false + debug: true|false + auto_assign_floating_ip: true + start_guests_on_host_boot: true + create_networks: true + compute_scheduler_driver: nova.scheduler.multi.MultiScheduler + quantum: true + master_hostname: controller-01 + nagios: false + proj_name: test + nagios_master: fuelweb.domain.tld + management_vip: 10.108.2.2 + public_vip: 10.108.2.2 + #Nova-network part, gets ignored if $quantum = `false` + novanetwork_parameters: + fixed_network_range: CIDR + vlan_start: <1-1024> + network_manager: String + network_size: + #Quantum part, used only if quantum='true' + quantum_parameters: + tenant_network_type: gre + segment_range: ! '300:500' + metadata_proxy_shared_secret: quantum + mysql: + root_password: root + glance: + db_password: glance + user_password: glance + swift: + user_password: swift_pass + nova: + db_password: nova + user_password: nova + access: + password: admin + user: admin + tenant: admin + email: admin@example.org + keystone: + db_password: keystone + admin_token: nova + quantum_access: + user_password: quantum + db_password: quantum + rabbit: + password: nova + user: nova + cinder: + password: cinder + user: cinder + floating_network_range: + - 10.20.0.100 + base_syslog: + syslog_port: '514' + syslog_server: 10.108.2.2 + syslog: + syslog_port: '514' + syslog_transport: udp + syslog_server: '' + deployment_id: 1 + deployment_mode: ha + deployment_source: cli + deployment_engine: nailyfact + + +engine: + url: http://localhost/cobbler_api + username: cobbler + password: cobbler diff --git a/bin/astute b/bin/astute index ba5fb285..24a510f2 100755 --- a/bin/astute +++ b/bin/astute @@ -23,6 +23,7 @@ require 'optparse' require 'yaml' require 'astute' require 'astute/version' +require 'astute/enviroment' class ConsoleReporter def report(msg) @@ -63,7 +64,8 @@ end reporter = ConsoleReporter.new Astute.logger = Logger.new(STDOUT) if opts[:verbose] -environment = YAML.load_file(opts[:filename]) +environment = Astute::Enviroment.load_file(opts[:filename]) +p environment['nodes'][0]['ip'] deploy_engine = nil @@ -84,7 +86,8 @@ def console_provision(orchestrator, reporter, environment) res = orchestrator.fast_provision(reporter, environment['engine'], environment['nodes']) if res == Astute::SUCCESS puts "restarting nodes..." - sleep 5 + sleep 25 + puts "start watching progress" res = orchestrator.provision(reporter, environment['task_uuid'], environment['nodes']) end res diff --git a/coverage.data b/coverage.data new file mode 100644 index 00000000..66eca433 Binary files /dev/null and b/coverage.data differ diff --git a/coverage/.last_run.json b/coverage/.last_run.json new file mode 100644 index 00000000..c5cf1c71 --- /dev/null +++ b/coverage/.last_run.json @@ -0,0 +1,5 @@ +{ + "result": { + "covered_percent": 91.2 + } +} diff --git a/coverage/.resultset.json b/coverage/.resultset.json new file mode 100644 index 00000000..b574213e --- /dev/null +++ b/coverage/.resultset.json @@ -0,0 +1,4788 @@ +{ + "RSpec": { + "coverage": { + "/Users/warpc/Projects/astute/lib/astute.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 1, + 1, + null, + 1, + 913, + 0, + 0, + 0, + 0, + null, + null, + 913, + null, + null, + 1, + 1, + null, + null, + 1, + 1, + null + ], + "/Users/warpc/Projects/astute/lib/astute/ruby_removed_functions.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1223, + 1223, + 1223, + 1223, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/config.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + 0, + 0, + null, + null, + null, + 1, + 1, + 1, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + null, + 1, + 0, + 0, + null, + null, + null, + 1, + 107, + 107, + 107, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/logparser.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + null, + 1, + 1, + null, + null, + 1, + null, + null, + null, + 1, + 6, + null, + null, + null, + 1, + 1, + null, + 1, + 8, + 8, + 8, + 8, + null, + null, + 1, + 973, + 973, + 4682, + 1657, + 1657, + 0, + 0, + null, + 1657, + null, + 1657, + 1657, + null, + 0, + 0, + null, + null, + null, + null, + null, + 1657, + null, + 973, + null, + null, + 1, + 4, + 4, + 4, + 4, + null, + null, + null, + 1, + 0, + null, + null, + 1, + null, + 1, + 1657, + 0, + 0, + null, + 1657, + 0, + 0, + null, + 1657, + 1657, + null, + 1657, + 1657, + null, + 1657, + null, + null, + 1657, + 1657, + null, + 1657, + 0, + 0, + null, + 1657, + null, + null, + 1, + null, + null, + null, + null, + 1657, + 1657, + 1599, + 1599, + 1599, + 1599, + 1599, + null, + null, + null, + null, + 1, + 3256, + 0, + 0, + null, + 3256, + 3256, + 3250, + 3250, + 3250, + 3250, + 3250, + 3250, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/orchestrator.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + 24, + 24, + null, + null, + 1, + 1, + 2, + 1, + 1, + 1, + null, + null, + null, + 1, + null, + null, + null, + 1, + 2, + null, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 0, + null, + 1, + 1, + null, + null, + 1, + 8, + null, + 7, + null, + 6, + 6, + 6, + null, + null, + 0, + 0, + null, + null, + null, + null, + 0, + null, + 6, + null, + null, + 6, + 4, + 4, + null, + 2, + 2, + null, + null, + null, + null, + 2, + null, + null, + null, + 1, + 5, + null, + null, + 8, + null, + 8, + null, + 4, + 4, + 4, + 4, + 4, + 4, + null, + 0, + null, + null, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 6, + null, + 3, + 9, + 6, + 3, + null, + 3, + 3, + 3, + null, + 0, + null, + null, + 0, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + null, + null, + 3, + 3, + null, + 3, + 3, + null, + null, + null, + 1, + 5, + null, + null, + 1, + 3, + null, + null, + 1, + null, + 1, + 4, + null, + 4, + 4, + 4, + null, + null, + 1, + 0, + 0, + 0, + 0, + null, + null, + 1, + 7, + 7, + 7, + null, + 1, + null, + 1, + null, + null, + null, + null, + 1, + null, + null, + null, + 1, + 6, + 6, + 6, + 6, + 6, + null, + null, + 0, + 0, + null, + 6, + 6, + null, + 6, + null, + null, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + null, + 3, + 3, + null, + null, + 5, + null, + null, + null, + 0, + 0, + null, + 5, + null, + null, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + null, + null, + 0, + 0, + 0, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/metadata.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + null, + 0, + 0, + 0, + null, + 0, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/deployment_engine.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + 15, + 0, + null, + 15, + null, + null, + 1, + null, + 12, + 46, + 12, + 12, + 12, + null, + null, + 1, + 2, + 2, + null, + null, + 1, + 6, + 6, + 1, + 1, + 1, + null, + null, + 1, + null, + 2, + 2, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + 6, + 6, + null, + null, + 1, + 1, + 1, + null, + null, + null, + null, + null, + 1, + 2, + null, + null, + 1, + null, + 2, + 2, + 2, + 2, + 2, + null, + 2, + 2, + 10, + 2, + 10, + 2, + 10, + null, + null, + 2, + null, + null, + null, + 10, + 10, + null, + null, + 10, + 2, + null, + 2, + 4, + 2, + 2, + 2, + 2, + 2, + null, + null, + 1, + 3, + null, + null, + 1, + 1, + null, + null, + 1, + 36, + 36, + 7, + 7, + 3, + null, + null, + 36, + 36, + 36, + 36, + 36, + 7, + null, + null, + 7, + 7, + null, + 7, + 17, + null, + 7, + 7, + null, + 7, + 7, + null, + 7, + 7, + null, + null, + null, + 1, + 1, + 59, + null, + null, + 1, + 40, + 15, + 15, + null, + 25, + null, + null, + 1, + 12, + 12, + 12, + 12, + 56, + 56, + 56, + null, + 0, + null, + 56, + 56, + null, + 56, + 56, + 0, + 0, + null, + 0, + null, + 56, + 0, + null, + 56, + 34, + null, + 56, + 11, + null, + null, + 56, + null, + 12, + 12, + 24, + 24, + null, + null, + 12, + 92, + 92, + null, + 12, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/network.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 3, + 1, + null, + null, + null, + null, + 1, + null, + 1, + null, + null, + null, + null, + 1, + null, + null, + 3, + null, + 1, + null, + 1, + 1, + null, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + null, + 1, + 1, + 1, + 2, + null, + 2, + null, + null, + 2, + 2, + null, + null, + null, + 1, + 1, + 2, + null, + 2, + null, + null, + 2, + 2, + null, + null, + null, + 1, + 4, + 4, + 4, + null, + null, + 4, + null, + null, + 1, + 3, + 1, + null, + null, + null, + null, + null, + 2, + null, + null, + null, + 1, + 2, + null, + null, + null, + 4, + null, + 2, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/puppetd.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + null, + 1, + 6, + 6, + 14, + 14, + 36, + 14, + 14, + 6, + 6, + null, + 14, + 14, + 8, + null, + 6, + 6, + 6, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + 24, + null, + null, + 12, + 12, + null, + 6, + 3, + 3, + null, + 18, + 12, + null, + null, + 24, + null, + 12, + 12, + 0, + 0, + null, + 12, + null, + null, + 1, + 1, + null, + 10, + null, + 5, + 10, + 5, + 5, + 5, + 5, + null, + 0, + 5, + 5, + 5, + 5, + 5, + 5, + 12, + 12, + null, + null, + 12, + 14, + null, + null, + 12, + 12, + 3, + 1, + 1, + null, + 1, + null, + 2, + 2, + null, + null, + 12, + 1, + 1, + null, + 2, + 2, + null, + null, + null, + 12, + 6, + null, + 6, + 6, + 0, + null, + 0, + 0, + null, + null, + 0, + null, + null, + null, + 12, + null, + null, + 12, + 12, + null, + 7, + 7, + 7, + null, + null, + 5, + 5, + 5, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/rpuppet.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + 0, + 0, + 0, + null, + 0, + 0, + null, + null, + null, + null, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/deployment_engine/simple_puppet.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + 1, + 23, + 16, + 16, + 39, + 16, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/deployment_engine/nailyfact.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 6, + 5, + null, + null, + 1, + null, + null, + null, + 12, + 12, + 12, + 12, + null, + null, + null, + null, + null, + 12, + 219, + 96, + null, + null, + 123, + null, + null, + null, + null, + 12, + 56, + 56, + null, + 0, + null, + 56, + 56, + 34, + null, + null, + null, + null, + 68, + null, + 12, + 1, + null, + null, + 12, + null, + null, + 1, + 17, + 9, + null, + 9, + 9, + 11, + null, + 9, + null, + 9, + 20, + 9, + null, + null, + 1, + 1, + 1, + null, + 0, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/cobbler.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + null, + 1, + 14, + null, + 14, + 7, + 7, + 7, + null, + 7, + 7, + 7, + null, + 14, + 14, + null, + 14, + 14, + 14, + 14, + null, + null, + 1, + 11, + null, + null, + 11, + 11, + null, + 11, + 11, + null, + 11, + null, + null, + 11, + 99, + 91, + 91, + null, + null, + null, + 11, + 8, + 8, + null, + null, + null, + null, + 11, + 11, + null, + null, + 1, + 10, + null, + null, + 1, + 0, + null, + null, + 1, + 19, + null, + null, + 1, + 0, + null, + null, + 1, + 9, + 1, + null, + 8, + 8, + null, + 9, + null, + null, + 1, + 6, + null, + null, + 1, + 0, + 0, + null, + null, + 1, + 0, + null, + null, + 1, + 0, + null, + null, + 1, + 0, + null, + null, + 1, + 0, + null, + null, + null, + null, + 1, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 27, + 27, + 26, + 25, + 300, + null, + null, + null, + 1, + 24, + 24, + 24, + null, + 24, + 276, + 276, + 0, + null, + 1, + null, + null, + null, + 275, + 33, + 33, + null, + null, + null, + null, + 242, + 17, + 0, + 0, + null, + null, + 17, + null, + 0, + null, + null, + null, + null, + null, + 242, + 13, + 13, + null, + null, + null, + 229, + 11, + 11, + null, + null, + 218, + null, + 23, + 23, + null, + null, + 1, + null, + 387, + null, + null, + 1548, + 387, + 387, + null, + null, + 1, + null, + 275, + null, + null, + 1, + 111, + null, + null, + 1, + 13, + 13, + 23, + 111, + 111, + 0, + null, + 111, + 0, + 0, + null, + 111, + 111, + null, + null, + 13, + null, + null, + 1, + null, + 11, + 11, + 21, + 42, + 42, + null, + null, + 11, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/logparser_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + 1, + 1, + null, + null, + 6, + 6, + 6, + 6, + 6, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + null, + null, + null, + 6, + 6, + 6, + null, + null, + 6, + 6, + 6, + 6, + 6, + 6, + 1147, + 1147, + 1147, + 1147, + 1147, + 1147, + null, + null, + 6, + 6, + 6, + 6, + 6, + 6, + null, + 6, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 6, + null, + null, + 1, + 532, + 532, + 532, + 532, + 532, + 532, + null, + null, + null, + null, + 1, + 58, + 58, + 531, + 531, + 531, + 474, + null, + 57, + 57, + null, + null, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + null, + 58, + 58, + 58, + 58, + null, + 58, + 58, + 58, + 58, + null, + null, + 1, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + null, + null, + 1, + 1, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + 1, + null, + 1, + null, + null, + null, + 1, + 1, + 8, + null, + 3, + 3, + 3, + 3, + null, + 3, + null, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + null, + null, + null, + 923, + null, + 915, + 1599, + 1091, + 1091, + 1091, + 1091, + null, + 508, + null, + null, + null, + 915, + 915, + 3882, + 1599, + 1599, + 1089, + 1089, + null, + null, + null, + null, + 3, + 5, + null, + null, + 3, + 5, + 5, + 5, + null, + null, + null, + 3, + null, + null, + 1, + 1, + null, + null, + null, + null, + 1, + 3, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 2, + null, + null, + 1, + 1, + null, + null, + null, + null, + 1, + 3, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/mclient_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + 1, + 1, + null, + 1, + 4, + 4, + 4, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + 4, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + 1, + null, + null, + 4, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + null, + 1, + null, + null, + 4, + 1, + 2, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + null, + 1, + null, + null, + 4, + 1, + 2, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/mclient.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + 31, + 31, + 77, + 31, + 31, + 31, + 31, + null, + null, + 1, + 3, + 3, + null, + null, + 1, + 99, + null, + 99, + 32, + 32, + null, + null, + null, + 67, + null, + 67, + null, + 65, + null, + null, + 1, + null, + 1, + 45, + null, + null, + 45, + null, + 2, + 2, + 2, + 4, + 2, + 2, + 2, + 2, + 2, + null, + 2, + 2, + 2, + null, + 2, + 6, + 2, + 2, + 0, + null, + 2, + null, + null, + null, + 99, + 45, + 1, + 1, + null, + 45, + 2, + 2, + null, + null, + null, + null, + 1, + 103, + null, + 0, + null, + null, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + null, + null, + 1, + 31, + 31, + 31, + 31, + 31, + null, + null, + 0, + 0, + 0, + null, + null, + 1, + 69, + 80, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/nailyfact_deploy_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 1, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + null, + null, + null, + null, + null, + null, + null, + null, + 7, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 7, + 21, + null, + 7, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 7, + 7, + 7, + null, + 7, + 7, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + 1, + null, + 1, + 1, + null, + null, + 1, + 1, + 1, + 2, + null, + null, + 1, + 1, + 1, + null, + 4, + 4, + 1, + 1, + 1, + null, + null, + 1, + 1, + 6, + 1, + 6, + 1, + 1, + 2, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 6, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/node_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 2, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 2, + 2, + 2, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + null, + null, + 1, + 2, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/node.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + 56, + 55, + 55, + null, + 1, + null, + 55, + null, + null, + 1, + 4, + null, + null, + 1, + 16, + null, + null, + 1, + 60, + null, + null, + 1, + 3, + null, + null, + 1, + 11, + null, + null, + null, + 1, + 1, + 1, + null, + 1, + 27, + 27, + 21, + 21, + null, + null, + null, + 1, + 56, + 56, + 56, + null, + null, + 1, + 4, + 1, + null, + null, + 1, + 5, + null, + null, + 1, + null, + 1, + 56, + null, + null, + 1, + 56, + 32, + null, + 24, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/orchestrator_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + null, + 1, + 24, + 24, + 24, + null, + null, + 1, + 1, + 2, + null, + null, + null, + null, + null, + null, + null, + null, + 3, + null, + null, + null, + 1, + 1, + 1, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + 2, + null, + 2, + 4, + null, + 2, + null, + null, + null, + 2, + null, + null, + null, + 1, + null, + 1, + 1, + null, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + null, + 1, + 1, + null, + 1, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + null, + 1, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + null, + null, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + null, + 1, + 2, + null, + null, + null, + 1, + 1, + null, + 1, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + 1, + null, + null, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + null, + 1, + 3, + 1, + null, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + null, + 1, + 3, + 1, + null, + null, + 1, + null, + null, + 1, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 2, + null, + null, + null, + null, + 1, + 1, + null, + 7, + 7, + 7, + null, + 7, + 7, + 7, + null, + null, + null, + 1, + 2, + null, + null, + null, + 1, + 1, + 0, + null, + 1, + 1, + null, + null, + 8, + null, + 1, + null, + 4, + null, + null, + 1, + 1, + null, + null, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + 0, + null, + 1, + null, + null, + null, + null, + 1, + 3, + null, + null, + 1, + 1, + 0, + null, + 1, + 1, + null, + null, + null, + null, + 1, + 2, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + 5, + 7, + null, + null, + null, + 1, + 2, + null, + null, + null, + 1, + 1, + 0, + null, + 1, + 1, + null, + 1, + null, + null, + 1, + 1, + 0, + null, + null, + 1, + 1, + null, + 1, + null, + null, + 1, + 1, + 1, + null, + 1, + null, + null, + 1, + 1, + 0, + null, + null, + 1, + null, + 1, + 1, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/puppetd_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + 1, + 1, + null, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + null, + null, + 1, + 1, + 1, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + null, + null, + null, + 1, + null, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + null, + null, + null, + 1, + null, + 1, + null, + null, + 1, + 1, + null, + null, + 1, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + null, + null, + null, + 1, + null, + 1, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + 1, + 1, + null, + null, + 1, + 1, + null, + 1, + null, + null, + null, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + null, + null, + 1, + null, + 1, + 1, + null, + null, + 1, + 1, + null, + 1, + null, + null, + null, + null, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + null, + 1, + null, + 1, + null, + 1, + 1, + 1, + 1, + null, + 1, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/reporter_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + 1, + 1, + 1, + 20, + 20, + null, + null, + 20, + 20, + null, + null, + 1, + 1, + 1, + null, + null, + 1, + 1, + 6, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + null, + 1, + 1, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 2, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 6, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + null, + null, + 1, + 1, + 1, + 2, + null, + null, + 1, + 1, + 1, + null, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + 5, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + 5, + null, + null, + 1, + 1, + null, + null, + null, + 1, + 1, + 1, + 1, + 5, + null, + null, + 1, + 1, + null, + null, + null, + null, + 1, + 1, + 1, + 5, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/spec/unit/simplepuppet_deploy_spec.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 1, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + 1, + 1, + null, + null, + 1, + 1, + 1, + 2, + null, + null, + null, + 1, + 1, + 1, + null, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + 1, + 1, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + 6, + 6, + 1, + 1, + null, + 1, + 1, + 1, + 2, + null, + 1, + null, + 1, + 1, + null, + null, + 1, + 1, + 1, + null, + null, + null, + null, + 12, + 1, + 12, + 12, + 12, + 12, + 12, + 1, + 1, + null, + 1, + 1, + 1, + 2, + null, + 1, + null, + 1, + null, + 1, + 1, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/logparser/provision.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + 1, + 1, + 1, + 5, + 5, + null, + null, + 1, + null, + 58, + 116, + 58, + 1, + null, + null, + 58, + null, + null, + 1, + 1, + 58, + null, + 0, + null, + 58, + null, + 58, + null, + null, + 1, + 1, + 0, + null, + 1, + null, + 1, + 1, + null, + 3, + 1, + null, + 1, + null, + null, + 0, + null, + null, + 14, + 1, + 1, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 58, + 58, + 58, + 58, + 58, + 0, + 0, + null, + null, + 58, + 13, + 13, + 103, + 12, + null, + 91, + null, + null, + 13, + null, + null, + 58, + 64, + 64, + 64, + 64, + 32, + null, + 32, + null, + 64, + 64, + null, + null, + 58, + 76, + 76, + null, + null, + null, + 58, + 58, + 52, + 52, + 52, + null, + 52, + 52, + 52, + 52, + 52, + null, + 52, + 617, + 617, + 7694, + 51, + null, + 39, + null, + 39, + null, + null, + null, + null, + null, + null, + null, + null, + 12, + 12, + null, + 12, + 12, + null, + 168, + 12, + 12, + 12, + null, + 12, + null, + 12, + 12, + 12, + null, + 12, + null, + 12, + null, + 0, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0, + 0, + 0, + 0, + 0, + null, + null, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + null, + 0, + null, + null, + null, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/logparser/parser_patterns.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + 1, + 14, + null, + null, + 1, + 0, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/logparser/deployment.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + null, + 3, + null, + null, + 1, + 0, + 0, + null, + null, + 1, + null, + 915, + 4566, + 1599, + 5, + null, + 5, + 5, + 5, + null, + null, + 915, + null, + null, + 1, + 1, + 1599, + null, + 0, + null, + 1599, + null, + 1599, + null, + null, + 1, + null, + null, + null, + null, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + null, + 0, + 0, + null, + 0, + 0, + null, + null, + 0, + 0, + 0, + 0, + null, + 0, + null, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1599, + 1599, + 1599, + 0, + 0, + null, + null, + 1599, + 1599, + 1599, + 1599, + 1599, + null, + null, + 1599, + 91161, + 91161, + 790841, + 790841, + 5454693, + 118528, + null, + 5336165, + null, + 5454693, + null, + null, + 790841, + 170, + null, + null, + null, + null, + null, + 13679, + 1599, + 1599, + 13679, + 1599, + 1599, + null, + 1599, + 1599, + 1599, + 12080, + 12080, + 12080, + 12080, + null, + 0, + null, + null, + null, + 1599, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/context.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + null, + 1, + 10, + 10, + 10, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/nodes_remover.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + null, + 1, + 5, + 5, + null, + null, + 1, + null, + null, + null, + 5, + 5, + null, + null, + 5, + null, + null, + 5, + null, + 5, + 1, + 1, + null, + 1, + null, + null, + null, + 5, + 2, + 2, + null, + 2, + null, + null, + 5, + null, + 5, + null, + null, + 1, + 1, + 8, + null, + null, + 1, + 27, + 7, + 28, + null, + 20, + 20, + 20, + 20, + 43, + 20, + 8, + null, + 20, + 20, + 20, + 23, + 23, + 0, + 0, + null, + 12, + 12, + null, + 11, + null, + null, + 20, + null, + null, + 1, + 10, + 22, + 22, + 7, + 7, + null, + 22, + 15, + null, + null, + null, + null, + null + ], + "/Users/warpc/Projects/astute/lib/astute/reporter.rb": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 1, + 1, + 1, + 37, + 37, + null, + null, + 1, + 70, + 70, + 70, + 80, + 78, + null, + null, + 68, + 50, + 50, + null, + 49, + 118, + 54, + 27, + null, + 47, + null, + null, + null, + null, + null, + 1, + null, + 1, + null, + 80, + 80, + 1, + null, + 79, + null, + 80, + 0, + null, + 80, + 2, + 2, + 2, + null, + null, + null, + 78, + 55, + 1, + null, + 1, + null, + 55, + 1, + null, + 1, + null, + null, + 78, + 17, + null, + 17, + null, + null, + null, + 174, + 78, + 30, + 30, + 30, + 30, + null, + 30, + 6, + null, + null, + 6, + null, + 24, + 3, + null, + null, + 3, + null, + null, + null, + 82, + null, + null, + 55, + null, + null, + null + ] + }, + "timestamp": 1374221130 + } +} diff --git a/coverage/rcov/assets/0.2.3/jquery-1.3.2.min.js b/coverage/rcov/assets/0.2.3/jquery-1.3.2.min.js new file mode 100644 index 00000000..b1ae21d8 --- /dev/null +++ b/coverage/rcov/assets/0.2.3/jquery-1.3.2.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/coverage/rcov/assets/0.2.3/jquery.tablesorter.min.js b/coverage/rcov/assets/0.2.3/jquery.tablesorter.min.js new file mode 100644 index 00000000..594ac95b --- /dev/null +++ b/coverage/rcov/assets/0.2.3/jquery.tablesorter.min.js @@ -0,0 +1,15 @@ +/* + * + * TableSorter 2.0 - Client-side table sorting with ease! + * Version 2.0.3 + * @requires jQuery v1.2.3 + * + * Copyright (c) 2007 Christian Bach + * Examples and docs at: http://tablesorter.com + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;ib)?1:0));};function sortTextDesc(a,b){return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i + + + + Astute C0 Coverage Information - SimpleCov - RCov style + + + + + + + +

Astute C0 Coverage Information - SimpleCov - RCov style

+ + + +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
TOTAL47232022
96.23%
+
+
+
+
91.20%
+
+
+
+
lib/astute.rb6337
93.65%
+
+
+
+
89.19%
+
+
+
+
lib/astute/cobbler.rb300129
95.00%
+
+
+
+
88.37%
+
+
+
+
lib/astute/config.rb6634
93.94%
+
+
+
+
88.24%
+
+
+
+
lib/astute/context.rb267
100.00%
+
+
+
+
100.00%
+
+
+
+
lib/astute/deployment_engine.rb217128
97.24%
+
+
+
+
95.31%
+
+
+
+
lib/astute/deployment_engine/nailyfact.rb9138
97.80%
+
+
+
+
94.74%
+
+
+
+
lib/astute/deployment_engine/simple_puppet.rb267
100.00%
+
+
+
+
100.00%
+
+
+
+
lib/astute/logparser.rb14878
91.22%
+
+
+
+
83.33%
+
+
+
+
lib/astute/logparser/deployment.rb17188
82.46%
+
+
+
+
65.91%
+
+
+
+
lib/astute/logparser/parser_patterns.rb5348
99.81%
+
+
+
+
87.50%
+
+
+
+
lib/astute/logparser/provision.rb254122
86.61%
+
+
+
+
72.13%
+
+
+
+
lib/astute/mclient.rb13575
91.11%
+
+
+
+
84.00%
+
+
+
+
lib/astute/metadata.rb309
86.67%
+
+
+
+
55.56%
+
+
+
+
lib/astute/network.rb11245
100.00%
+
+
+
+
100.00%
+
+
+
+
lib/astute/node.rb9344
100.00%
+
+
+
+
100.00%
+
+
+
+
lib/astute/nodes_remover.rb10553
98.10%
+
+
+
+
96.23%
+
+
+
+
lib/astute/orchestrator.rb256141
90.23%
+
+
+
+
82.27%
+
+
+
+
lib/astute/puppetd.rb17086
95.88%
+
+
+
+
91.86%
+
+
+
+
lib/astute/reporter.rb12457
99.19%
+
+
+
+
98.25%
+
+
+
+
lib/astute/rpuppet.rb4217
71.43%
+
+
+
+
29.41%
+
+
+
+
lib/astute/ruby_removed_functions.rb236
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/logparser_spec.rb278153
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/mclient_spec.rb9551
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/nailyfact_deploy_spec.rb28670
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/node_spec.rb9552
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/orchestrator_spec.rb423183
98.58%
+
+
+
+
96.72%
+
+
+
+
spec/unit/puppetd_spec.rb233105
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/reporter_spec.rb205126
100.00%
+
+
+
+
100.00%
+
+
+
+
spec/unit/simplepuppet_deploy_spec.rb12273
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + + + diff --git a/coverage/rcov/lib-astute-cobbler_rb.html b/coverage/rcov/lib-astute-cobbler_rb.html new file mode 100644 index 00000000..2f168ac7 --- /dev/null +++ b/coverage/rcov/lib-astute-cobbler_rb.html @@ -0,0 +1,959 @@ + + + + lib/astute/cobbler.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/cobbler.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/cobbler.rb300129
95.00%
+
+
+
+
88.37%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'xmlrpc/client'
18 
19 module Astute
20   module Provision
21     class CobblerError < RuntimeError; end
22 
23     class Cobbler
24 
25       attr_reader :remote, :token
26 
27       def initialize(o={})
28         Astute.logger.debug("Cobbler options: #{o.inspect}")
29 
30         if (match = /^http:\/\/([^:]+?):?(\d+)?(\/.+)/.match(o['url']))
31           host = match[1]
32           port = match[2] || '80'
33           path = match[3]
34         else
35           host = o['host'] || 'localhost'
36           port = o['port'] || '80'
37           path = o['path'] || '/cobbler_api'
38         end
39         username = o['username'] || 'cobbler'
40         password = o['password'] || 'cobbler'
41 
42         Astute.logger.debug("Connecting to cobbler with: host: #{host} port: #{port} path: #{path}")
43         @remote = XMLRPC::Client.new(host, path, port)
44         Astute.logger.debug("Trying to log in to cobbler with username: #{username}, password: #{password}")
45         @token = remote.call('login', username, password)
46       end
47 
48       def item_from_hash(what, name, data, opts = {})
49         options = {
50           :item_preremove => true,
51         }.merge!(opts)
52         cobsh = Cobsh.new(data.merge({'what' => what, 'name' => name}))
53         cobblerized = cobsh.cobblerized
54 
55         Astute.logger.debug("Creating/editing item from hash: #{cobsh.inspect}")
56         remove_item(what, name) if options[:item_preremove]
57         # get existent item id or create new one
58         item_id = get_item_id(what, name)
59 
60         # defining all item options
61         cobblerized.each do |opt, value|
62           next if opt == 'interfaces'
63           Astute.logger.debug("Setting #{what} #{name} opt: #{opt}=#{value}")
64           remote.call('modify_item', what, item_id, opt, value, token)
65         end
66 
67         # defining system interfaces
68         if what == 'system' && cobblerized.has_key?('interfaces')
69           Astute.logger.debug("Defining system interfaces #{name} #{cobblerized['interfaces']}")
70           remote.call('modify_system', item_id, 'modify_interface',
71                   cobblerized['interfaces'], token)
72         end
73 
74         # save item into cobbler database
75         Astute.logger.debug("Saving #{what} #{name}")
76         remote.call('save_item', what, item_id, token)
77       end
78 
79       def remove_item(what, name, recursive=true)
80         remote.call('remove_item', what, name, token, recursive) if item_exists(what, name)
81       end
82 
83       def remove_system(name)
84         remove_item('system', name)
85       end
86 
87       def item_exists(what, name)
88         remote.call('has_item', what, name)
89       end
90 
91       def system_exists(name)
92         item_exists('system', name)
93       end
94 
95       def get_item_id(what, name)
96         if item_exists(what, name)
97           item_id = remote.call('get_item_handle', what, name, token)
98         else
99           item_id = remote.call('new_item', what, token)
100           remote.call('modify_item', what, item_id, 'name', name, token)
101         end
102         item_id
103       end
104 
105       def sync
106         remote.call('sync', token)
107       end
108 
109       def power(name, action)
110         options = {"systems" => [name], "power" => action}
111         remote.call('background_power_system', options, token)
112       end
113 
114       def power_on(name)
115         power(name, 'on')
116       end
117 
118       def power_off(name)
119         power(name, 'off')
120       end
121 
122       def power_reboot(name)
123         power(name, 'reboot')
124       end
125 
126       def event_status(event_id)
127         remote.call('get_task_status', event_id)
128       end
129 
130     end
131 
132     class Cobsh < ::Hash
133       ALIASES = {
134         'ks_meta' => ['ksmeta'],
135         'mac_address' => ['mac'],
136         'ip_address' => ['ip'],
137       }
138 
139       # these fields can be get from the cobbler code
140       # you can just import cobbler.item_distro.FIELDS
141       # or cobbler.item_system.FIELDS
142       FIELDS = {
143         'system' => {
144           'fields' => [
145             'name', 'owners', 'profile', 'image', 'status', 'kernel_options',
146             'kernel_options_post', 'ks_meta', 'enable_gpxe', 'proxy',
147             'netboot_enabled', 'kickstart', 'comment', 'server',
148             'virt_path', 'virt_type', 'virt_cpus', 'virt_file_size',
149             'virt_disk_driver', 'virt_ram', 'virt_auto_boot', 'power_type',
150             'power_address', 'power_user', 'power_pass', 'power_id',
151             'hostname', 'gateway', 'name_servers', 'name_servers_search',
152             'ipv6_default_device', 'ipv6_autoconfiguration', 'mgmt_classes',
153             'mgmt_parameters', 'boot_files', 'fetchable_files',
154             'template_files', 'redhat_management_key', 'redhat_management_server',
155             'repos_enabled', 'ldap_enabled', 'ldap_type', 'monit_enabled',
156           ],
157           'interfaces_fields' => [
158             'mac_address', 'mtu', 'ip_address', 'interface_type',
159             'interface_master', 'bonding_opts', 'bridge_opts',
160             'management', 'static', 'netmask', 'dhcp_tag', 'dns_name',
161             'static_routes', 'virt_bridge', 'ipv6_address', 'ipv6_secondaries',
162             'ipv6_mtu', 'ipv6_static_routes', 'ipv6_default_gateway'
163           ],
164           'special' => ['interfaces', 'interfaces_extra']
165         },
166         'profile' => {
167           'fields' => [
168             'name', 'owners', 'distro', 'parent', 'enable_gpxe',
169             'enable_menu', 'kickstart', 'kernel_options', 'kernel_options_post',
170             'ks_meta', 'proxy', 'repos', 'comment', 'virt_auto_boot',
171             'virt_cpus', 'virt_file_size', 'virt_disk_driver',
172             'virt_ram', 'virt_type', 'virt_path', 'virt_bridge',
173             'dhcp_tag', 'server', 'name_servers', 'name_servers_search',
174             'mgmt_classes', 'mgmt_parameters', 'boot_files', 'fetchable_files',
175             'template_files', 'redhat_management_key', 'redhat_management_server'
176           ]
177         },
178         'distro' => {
179           'fields' => ['name', 'owners', 'kernel', 'initrd', 'kernel_options',
180             'kernel_options_post', 'ks_meta', 'arch', 'breed',
181             'os_version', 'comment', 'mgmt_classes', 'boot_files',
182             'fetchable_files', 'template_files', 'redhat_management_key',
183             'redhat_management_server']
184         }
185 
186       }
187 
188       def initialize(h)
189         Astute.logger.debug("Cobsh is initialized with: #{h.inspect}")
190         raise CobblerError, "Cobbler hash must have 'name' key" unless h.has_key? 'name'
191         raise CobblerError, "Cobbler hash must have 'what' key" unless h.has_key? 'what'
192         raise CobblerError, "Unsupported 'what' value" unless FIELDS.has_key? h['what']
193         h.each{|k, v| store(k, v)}
194       end
195 
196 
197       def cobblerized
198         Astute.logger.debug("Cobblerizing hash: #{inspect}")
199         ch = {}
200         ks_meta = ""
201 
202         each do |k, v|
203           k = aliased(k)
204           if ch.has_key?(k) && ch[k] == v
205             next
206           elsif ch.has_key?(k)
207             raise CobblerError, "Wrong cobbler data: #{k} is duplicated"
208           end
209 
210           # skiping not valid item options
211           unless valid_field?(k)
212             Astute.logger.debug("Key #{k} is not valid. Will be skipped.")
213             next
214           end
215 
216           # here we don't store ks_meta directly in ch (cobblerized hash)
217           # instead we just append ks_meta value to store it later
218           if k == 'ks_meta'
219             if v.kind_of?(Hash)
220               v.each do |ks_meta_key, ks_meta_value|
221                 ks_meta << " #{ks_meta_key}=#{ks_meta_value}"
222               end
223             elsif v.kind_of?(String)
224               ks_meta << " #{v}"
225             else
226               raise "Wrong ks_meta format. It must be Hash or String"
227             end
228           end
229 
230           # special handling for system interface fields
231           # which are the only objects in cobbler that will ever work this way
232           if k == 'interfaces'
233             ch.store('interfaces', cobblerized_interfaces)
234             next
235           end
236 
237           # here we convert interfaces_extra options into ks_meta format
238           if k == 'interfaces_extra'
239             ks_meta << cobblerized_interfaces_extra
240             next
241           end
242 
243           ch.store(k, v)
244         end # each do |k, v|
245         ch.store('ks_meta', ks_meta.strip) if ks_meta.strip.length > 0
246         ch
247       end
248 
249       def aliased(k)
250         # converting 'foo-bar' keys into 'foo_bar' keys
251         k1 = k.gsub(/-/,'_')
252         # converting orig keys into alias keys
253         # example: 'ksmeta' into 'ks_meta'
254         k2 = ALIASES.each_key.select{|ak| ALIASES[ak].include?(k1)}[0] || k1
255         Astute.logger.debug("Key #{k} aliased with #{k2}") if k != k2
256         k2
257       end
258 
259       def valid_field?(k)
260         (FIELDS[fetch('what')]['fields'].include?(k) or
261           (FIELDS[fetch('what')]['special'] or []).include?(k))
262       end
263 
264       def valid_interface_field?(k)
265         (FIELDS[fetch('what')]['interfaces_fields'] or []).include?(k)
266       end
267 
268       def cobblerized_interfaces
269         interfaces = {}
270         fetch('interfaces').each do |iname, ihash|
271           ihash.each do |iopt, ivalue|
272             iopt = aliased(iopt)
273             if interfaces.has_key?("#{iopt}-#{iname}")
274               raise CobblerError, "Wrong interface cobbler data: #{iopt} is duplicated"
275             end
276             unless valid_interface_field?(iopt)
277               Astute.logger.debug("Interface key #{iopt} is not valid. Skipping")
278               next
279             end
280             Astute.logger.debug("Defining interfaces[#{iopt}-#{iname}] = #{ivalue}")
281             interfaces["#{iopt}-#{iname}"] = ivalue
282           end
283         end
284         interfaces
285       end
286 
287       def cobblerized_interfaces_extra
288         # here we just want to convert interfaces_extra into ks_meta
289         interfaces_extra_str = ""
290         fetch('interfaces_extra').each do |iname, iextra|
291           iextra.each do |k, v|
292             Astute.logger.debug("Adding into ks_meta interface_extra_#{iname}_#{k}=#{v}")
293             interfaces_extra_str << " interface_extra_#{iname}_#{k}=#{v}"
294           end
295         end
296         interfaces_extra_str
297       end
298     end
299 
300   end
301 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-config_rb.html b/coverage/rcov/lib-astute-config_rb.html new file mode 100644 index 00000000..9f64140a --- /dev/null +++ b/coverage/rcov/lib-astute-config_rb.html @@ -0,0 +1,257 @@ + + + + lib/astute/config.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/config.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/config.rb6634
93.94%
+
+
+
+
88.24%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'symboltable'
18 require 'singleton'
19 
20 module Astute
21   class ConfigError < StandardError; end
22   class UnknownOptionError < ConfigError
23     attr_reader :name
24 
25     def initialize(name)
26       super("Unknown config option #{name}")
27       @name = name
28     end
29   end
30 
31   class MyConfig
32     include Singleton
33     attr_reader :configtable
34 
35     def initialize
36       @configtable = SymbolTable.new
37     end
38   end
39 
40   class ParseError < ConfigError
41     attr_reader :line
42 
43     def initialize(message, line)
44       super(message)
45       @line = line
46     end
47   end
48 
49   def self.config
50     config = MyConfig.instance.configtable
51     config.update(default_config) if config.empty?
52     return config
53   end
54 
55   def self.default_config
56     conf = {}
57     conf[:PUPPET_TIMEOUT] = 60*60         # maximum time it waits for the whole deployment
58     conf[:PUPPET_DEPLOY_INTERVAL] = 2     # sleep for ## sec, then check puppet status again
59     conf[:PUPPET_FADE_TIMEOUT] = 60       # how long it can take for puppet to exit after dumping to last_run_summary
60     conf[:MC_RETRIES] = 5                 # MClient tries to call mcagent before failure
61     conf[:MC_RETRY_INTERVAL] = 1          # MClient sleeps for ## sec between retries
62     conf[:PUPPET_FADE_INTERVAL] = 1       # retry every ## seconds to check puppet state if it was running
63     conf[:PROVISIONING_TIMEOUT] = 90 * 60 # timeout for booting target OS in provision
64     conf[:REBOOT_TIMEOUT] = 120           # how long it can take for node to reboot 
65     return conf
66   end
67 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-context_rb.html b/coverage/rcov/lib-astute-context_rb.html new file mode 100644 index 00000000..0182d169 --- /dev/null +++ b/coverage/rcov/lib-astute-context_rb.html @@ -0,0 +1,137 @@ + + + + lib/astute/context.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/context.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/context.rb267
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 module Astute
18   class Context
19     attr_accessor :task_id, :reporter, :deploy_log_parser
20 
21     def initialize(task_id, reporter, deploy_log_parser=nil)
22       @task_id = task_id
23       @reporter = reporter
24       @deploy_log_parser = deploy_log_parser
25     end
26   end
27 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-deployment_engine-nailyfact_rb.html b/coverage/rcov/lib-astute-deployment_engine-nailyfact_rb.html new file mode 100644 index 00000000..6448dce7 --- /dev/null +++ b/coverage/rcov/lib-astute-deployment_engine-nailyfact_rb.html @@ -0,0 +1,332 @@ + + + + lib/astute/deployment_engine/nailyfact.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/deployment_engine/nailyfact.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/deployment_engine/nailyfact.rb9138
97.80%
+
+
+
+
94.74%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 class Astute::DeploymentEngine::NailyFact < Astute::DeploymentEngine
18 
19   def deploy(nodes, attrs)
20     attrs_for_mode = self.send("attrs_#{attrs['deployment_mode']}", nodes, attrs)
21     super(nodes, attrs_for_mode)
22   end
23 
24   def create_facts(node, attrs)
25     # calculate_networks method is common and you can find it in superclass
26     # if node['network_data'] is undefined, we use empty list because we later try to iterate over it
27     #   otherwise we will get KeyError
28     node_network_data = node['network_data'].nil? ? [] : node['network_data']
29     interfaces = node['meta']['interfaces']
30     network_data_puppet = calculate_networks(node_network_data, interfaces)
31     metadata = {
32       'role' => node['role'],
33       'uid'  => node['uid'],
34       'network_data' => network_data_puppet.to_json
35     }
36 
37     attrs.each do |k, v|
38       if v.is_a? String
39         metadata[k] = v
40       else
41         # And it's the problem on the puppet side now to decode json
42         metadata[k] = v.to_json
43       end
44     end
45 
46     # Let's calculate interface settings we need for OpenStack:
47     node_network_data.each do |iface|
48       device = if iface['vlan'] && iface['vlan'] > 0
49         [iface['dev'], iface['vlan']].join('.')
50       else
51         iface['dev']
52       end
53       metadata["#{iface['name']}_interface"] = device
54       if iface['ip']
55         metadata["#{iface['name']}_address"] = iface['ip'].split('/')[0]
56       end
57     end
58 
59     # internal_address is required for HA..
60     metadata['internal_address'] = node['network_data'].select{|nd| nd['name'] == 'management' }[0]['ip'].split('/')[0]
61 
62     if metadata['network_manager'] == 'VlanManager' && !metadata['fixed_interface']
63       metadata['fixed_interface'] = get_fixed_interface(node)
64     end
65 
66     Astute::Metadata.publish_facts(@ctx, node['uid'], metadata)
67   end
68 
69   def deploy_piece(nodes, attrs, retries=2, change_node_status=true)
70     return false unless validate_nodes(nodes)
71     @ctx.reporter.report nodes_status(nodes, 'deploying', {'progress' => 0})
72 
73     Astute.logger.info "#{@ctx.task_id}: Calculation of required attributes to pass, include netw.settings"
74     nodes.each do |node|
75       create_facts(node, attrs)
76     end
77     Astute.logger.info "#{@ctx.task_id}: All required attrs/metadata passed via facts extension. Starting deployment."
78 
79     Astute::PuppetdDeployer.deploy(@ctx, nodes, retries, change_node_status)
80     nodes_roles = nodes.map { |n| { n['uid'] => n['role'] } }
81     Astute.logger.info "#{@ctx.task_id}: Finished deployment of nodes => roles: #{nodes_roles.inspect}"
82   end
83 
84   private
85   def get_fixed_interface(node)
86     return node['vlan_interface'] if node['vlan_interface']
87 
88     Astute.logger.warn "Can not find vlan_interface for node #{node['uid']}"
89     nil
90   end
91 
92 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-deployment_engine-simple_puppet_rb.html b/coverage/rcov/lib-astute-deployment_engine-simple_puppet_rb.html new file mode 100644 index 00000000..179af44d --- /dev/null +++ b/coverage/rcov/lib-astute-deployment_engine-simple_puppet_rb.html @@ -0,0 +1,137 @@ + + + + lib/astute/deployment_engine/simple_puppet.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/deployment_engine/simple_puppet.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/deployment_engine/simple_puppet.rb267
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 class Astute::DeploymentEngine::SimplePuppet < Astute::DeploymentEngine
18   # It is trivial puppet run. It's assumed that user has prepared site.pp
19   #   with all required parameters for modules
20   def deploy_piece(nodes, attrs, retries=2, change_node_status=true)
21     return false unless validate_nodes(nodes)
22     @ctx.reporter.report nodes_status(nodes, 'deploying', {'progress' => 0})
23     Astute::PuppetdDeployer.deploy(@ctx, nodes, retries, change_node_status)
24     nodes_roles = nodes.map { |n| { n['uid'] => n['role'] } }
25     Astute.logger.info "#{@ctx.task_id}: Finished deployment of nodes => roles: #{nodes_roles.inspect}"
26   end
27 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-deployment_engine_rb.html b/coverage/rcov/lib-astute-deployment_engine_rb.html new file mode 100644 index 00000000..994d86d6 --- /dev/null +++ b/coverage/rcov/lib-astute-deployment_engine_rb.html @@ -0,0 +1,710 @@ + + + + lib/astute/deployment_engine.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/deployment_engine.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/deployment_engine.rb217128
97.24%
+
+
+
+
95.31%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'json'
18 require 'timeout'
19 
20 module Astute
21   class DeploymentEngine
22     def initialize(context)
23       if self.class.superclass.name == 'Object'
24         raise "Instantiation of this superclass is not allowed. Please subclass from #{self.class.name}."
25       end
26       @ctx = context
27     end
28 
29     def deploy(nodes, attrs)
30       # See implementation in subclasses, this may be everriden
31       attrs['deployment_mode'] ||= 'multinode'  # simple multinode deployment is the default
32       attrs['use_cinder'] ||= nodes.any?{|n| n['role'] == 'cinder'}
33       @ctx.deploy_log_parser.deploy_type = attrs['deployment_mode']
34       Astute.logger.info "Deployment mode #{attrs['deployment_mode']}"
35       result = self.send("deploy_#{attrs['deployment_mode']}", nodes, attrs)
36     end
37 
38     def method_missing(method, *args)
39       Astute.logger.error "Method #{method} is not implemented for #{self.class}, raising exception."
40       raise "Method #{method} is not implemented for #{self.class}"
41     end
42 
43     def attrs_singlenode(nodes, attrs)
44       ctrl_management_ip = nodes[0]['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip']
45       ctrl_public_ip = nodes[0]['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip']
46       attrs['controller_node_address'] = ctrl_management_ip.split('/')[0]
47       attrs['controller_node_public'] = ctrl_public_ip.split('/')[0]
48       attrs
49     end
50 
51     def deploy_singlenode(nodes, attrs)
52       # TODO(mihgen) some real stuff is needed
53       Astute.logger.info "Starting deployment of single node OpenStack"
54       deploy_piece(nodes, attrs)
55     end
56 
57     # we mix all attrs and prepare them for Puppet
58     # Works for multinode deployment mode
59     def attrs_multinode(nodes, attrs)
60       ctrl_nodes = attrs['controller_nodes']
61       # TODO(mihgen): we should report error back if there are not enough metadata passed
62       ctrl_management_ips = []
63       ctrl_public_ips = []
64       ctrl_nodes.each do |n|
65         ctrl_management_ips << n['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip']
66         ctrl_public_ips << n['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip']
67       end
68 
69       attrs['controller_node_address'] = ctrl_management_ips[0].split('/')[0]
70       attrs['controller_node_public'] = ctrl_public_ips[0].split('/')[0]
71       attrs
72     end
73 
74     # This method is called by Ruby metaprogramming magic from deploy method
75     # It should not contain any magic with attributes, and should not directly run any type of MC plugins
76     # It does only support of deployment sequence. See deploy_piece implementation in subclasses.
77     def deploy_multinode(nodes, attrs)
78       deploy_ha_full(nodes, attrs)
79     end
80 
81     def attrs_ha(nodes, attrs)
82       # TODO(mihgen): we should report error back if there are not enough metadata passed
83       ctrl_nodes = attrs['controller_nodes']
84       ctrl_manag_addrs = {}
85       ctrl_public_addrs = {}
86       ctrl_storage_addrs = {}
87       ctrl_nodes.each do |n|
88         # current puppet modules require `hostname -s`
89         hostname = n['fqdn'].split(/\./)[0]
90         ctrl_manag_addrs.merge!({hostname =>
91                    n['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip'].split(/\//)[0]})
92         ctrl_public_addrs.merge!({hostname =>
93                    n['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip'].split(/\//)[0]})
94         ctrl_storage_addrs.merge!({hostname =>
95                    n['network_data'].select {|nd| nd['name'] == 'storage'}[0]['ip'].split(/\//)[0]})
96       end
97 
98       attrs['nodes'] = ctrl_nodes.map do |n|
99         {
100           'name'                 => n['fqdn'].split(/\./)[0],
101           'role'                 => 'controller',
102           'internal_address'     => n['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip'].split(/\//)[0],
103           'public_address'       => n['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip'].split(/\//)[0],
104           'mountpoints'          => "1 1\n2 2",
105           'zone'                 => n['id'],
106           'storage_local_net_ip' => n['network_data'].select {|nd| nd['name'] == 'storage'}[0]['ip'].split(/\//)[0],
107         }
108       end
109       attrs['nodes'].first['role'] = 'primary-controller'
110       attrs['ctrl_hostnames'] = ctrl_nodes.map {|n| n['fqdn'].split(/\./)[0]}
111       attrs['master_hostname'] = ctrl_nodes[0]['fqdn'].split(/\./)[0]
112       attrs['ctrl_public_addresses'] = ctrl_public_addrs
113       attrs['ctrl_management_addresses'] = ctrl_manag_addrs
114       attrs['ctrl_storage_addresses'] = ctrl_storage_addrs
115       attrs
116     end
117 
118     def deploy_ha(nodes, attrs)
119       deploy_ha_full(nodes, attrs)
120     end
121 
122     def deploy_ha_compact(nodes, attrs)
123       deploy_ha_full(nodes, attrs)
124     end
125 
126     def deploy_ha_full(nodes, attrs)
127       primary_ctrl_nodes = nodes.select {|n| n['role'] == 'primary-controller'}
128       ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
129       unless primary_ctrl_nodes.any?
130         if ctrl_nodes.size > 1
131           primary_ctrl_nodes = [ctrl_nodes.shift]
132         end
133       end
134       compute_nodes = nodes.select {|n| n['role'] == 'compute'}
135       quantum_nodes = nodes.select {|n| n['role'] == 'quantum'}
136       storage_nodes = nodes.select {|n| n['role'] == 'storage'}
137       proxy_nodes = nodes.select {|n| n['role'] == 'swift-proxy'}
138       primary_proxy_nodes = nodes.select {|n| n['role'] == 'primary-swift-proxy'}
139       other_nodes = nodes - ctrl_nodes - primary_ctrl_nodes - \
140         primary_proxy_nodes - quantum_nodes
141 
142       Astute.logger.info "Starting deployment of primary controller"
143       deploy_piece(primary_ctrl_nodes, attrs, 0, false)
144 
145       Astute.logger.info "Starting deployment of all controllers one by one"
146       ctrl_nodes.each {|n| deploy_piece([n], attrs)}
147 
148       Astute.logger.info "Starting deployment of 1st controller and 1st proxy"
149       deploy_piece(primary_ctrl_nodes + primary_proxy_nodes, attrs)
150 
151       Astute.logger.info "Starting deployment of quantum nodes"
152       deploy_piece(quantum_nodes, attrs)
153 
154       Astute.logger.info "Starting deployment of other nodes"
155       deploy_piece(other_nodes, attrs)
156       return
157     end
158 
159     private
160     def nodes_status(nodes, status, data_to_merge)
161       {'nodes' => nodes.map { |n| {'uid' => n['uid'], 'status' => status}.merge(data_to_merge) }}
162     end
163 
164     def validate_nodes(nodes)
165       if nodes.empty?
166         Astute.logger.info "#{@ctx.task_id}: Nodes to deploy are not provided. Do nothing."
167         return false
168       end
169       return true
170     end
171 
172     def calculate_networks(data, hwinterfaces)
173       interfaces = {}
174       data ||= []
175       Astute.logger.info "calculate_networks function was provided with #{data.size} interfaces"
176       data.each do |net|
177         Astute.logger.debug "Calculating network for #{net.inspect}"
178         if net['vlan'] && net['vlan'] != 0
179           name = [net['dev'], net['vlan']].join('.')
180         else
181           name = net['dev']
182         end
183         unless interfaces.has_key?(name)
184           interfaces[name] = {'interface' => name, 'ipaddr' => []}
185         end
186         iface = interfaces[name]
187         if net['name'] == 'admin'
188           if iface['ipaddr'].size > 0
189             Astute.logger.error "Admin network interferes with openstack nets"
190           end
191           iface['ipaddr'] += ['dhcp']
192         else
193           if iface['ipaddr'].any?{|x| x == 'dhcp'}
194             Astute.logger.error "Admin network interferes with openstack nets"
195           end
196           if net['ip']
197             iface['ipaddr'] += [net['ip']]
198           end
199           if net['gateway'] && net['name'] =~ /^public$/i
200             iface['gateway'] = net['gateway']
201           end
202         end
203         Astute.logger.debug "Calculated network for interface: #{name}, data: #{iface.inspect}"
204       end
205       interfaces['lo'] = {'interface'=>'lo', 'ipaddr'=>['127.0.0.1/8']} unless interfaces.has_key?('lo')
206       hwinterfaces.each do |i|
207         unless interfaces.has_key?(i['name'])
208           interfaces[i['name']] = {'interface' => i['name'], 'ipaddr' => []}
209         end
210       end
211       interfaces.keys.each do |i|
212         interfaces[i]['ipaddr'] = 'none' if interfaces[i]['ipaddr'].size == 0
213         interfaces[i]['ipaddr'] = 'dhcp' if interfaces[i]['ipaddr'] == ['dhcp']
214       end
215       interfaces
216     end
217   end
218 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-logparser-deployment_rb.html b/coverage/rcov/lib-astute-logparser-deployment_rb.html new file mode 100644 index 00000000..39aa49ba --- /dev/null +++ b/coverage/rcov/lib-astute-logparser-deployment_rb.html @@ -0,0 +1,572 @@ + + + + lib/astute/logparser/deployment.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/logparser/deployment.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/logparser/deployment.rb17188
82.46%
+
+
+
+
65.91%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 module Astute
18   module LogParser
19     class ParseDeployLogs <ParseNodeLogs
20       attr_reader :deploy_type
21       def initialize(deploy_type='multinode')
22         @deploy_type = deploy_type
23         pattern_spec = Patterns::get_default_pattern(
24           "puppet-log-components-list-#{@deploy_type}-controller")
25         super(pattern_spec)
26       end
27 
28       def deploy_type= (deploy_type)
29         @deploy_type = deploy_type
30         @nodes_states = {}
31       end
32 
33       def progress_calculate(uids_to_calc, nodes)
34         # Just create correct pattern for each node and then call parent method.
35         uids_to_calc.each do |uid|
36           node = nodes.select {|n| n['uid'] == uid}[0]
37           unless @nodes_states[uid]
38             pattern_spec = Patterns::get_default_pattern(
39               "puppet-log-components-list-#{@deploy_type}-#{node['role']}")
40             pattern_spec['path_prefix'] ||= PATH_PREFIX.to_s
41             pattern_spec['separator'] ||= SEPARATOR.to_s
42             @nodes_states[uid] = pattern_spec
43           end
44         end
45         super(uids_to_calc, nodes)
46       end
47 
48       private
49       def calculate(fo, node_pattern_spec)
50         case node_pattern_spec['type']
51         when 'count-lines'
52           progress = simple_line_counter(fo, node_pattern_spec)
53         when 'components-list'
54           progress = component_parser(fo, node_pattern_spec)
55         end
56         return progress
57       end
58 
59 	    def simple_line_counter(fo, pattern_spec)
60 	      # Pattern specification example:
61 	      # pattern_spec = {'type' => 'count-lines',
62 	      #   'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
63 	      #   'expected_line_number' => 500}
64 	      # Use custom separator if defined.
65 	      separator = pattern_spec['separator']
66 	      counter = 0
67 	      end_of_scope = false
68 	      previous_subchunk = ''
69 	      until end_of_scope
70 	        chunk = get_chunk(fo, pattern_spec['chunk_size'])
71 	        break unless chunk
72 	        # Trying to find separator on border between chunks.
73 	        subchunk = chunk.slice((1-separator.size)..-1)
74 	        # End of file reached. Exit from cycle.
75 	        end_of_scope = true unless subchunk
76 	        if subchunk and (subchunk + previous_subchunk).include?(separator)
77 	          # Separator found on border between chunks. Exit from cycle.
78 	          end_of_scope = true
79 	          continue
80 	        end
81 
82 	        pos = chunk.rindex(separator)
83 	        if pos
84 	          end_of_scope = true
85 	          chunk = chunk.slice((pos + separator.size)..-1)
86 	        end
87 	        counter += chunk.count("\n")
88 	      end
89 	      number = pattern_spec['expected_line_number']
90 	      unless number
91 	        Astute.logger.warn("Wrong pattern #{pattern_spec.inspect} defined for calculating progress via log.")
92 	        return 0
93 	      end
94 	      progress = counter.to_f / number
95 	      progress = 1 if progress > 1
96 	      return progress
97 	    end
98 
99 	    def component_parser(fo, pattern_spec)
100 	      # Pattern specification example:
101 	      # pattern_spec = {'type' => 'components-list',
102 	      #   'chunk_size' => 40000,
103 	        # 'components_list' => [
104 	        #   {'name' => 'Horizon', 'weight' => 10, 'patterns' => [
105 	        #      {'pattern' => '/Stage[main]/Horizon/Package[mod_wsgi]/ensure) created', 'progress' => 0.1},
106 	        #      {'pattern' => '/Stage[main]/Horizon/File_line[horizon_redirect_rule]/ensure) created', 'progress' => 0.3},
107 	        #      {'pattern' => '/Stage[main]/Horizon/File[/etc/openstack-dashboard/local_settings]/group)', 'progress' => 0.7},
108 	        #      {'pattern' => '/Stage[main]/Horizon/Service[$::horizon::params::http_service]/ensure)'\
109 	        #                    ' ensure changed \'stopped\' to \'running\'', 'progress' => 1},
110 	        #      ]
111 	        #   },
112 	        #   ]
113 	        # }
114 	      # Use custom separator if defined.
115 	      separator = pattern_spec['separator']
116 	      components_list = pattern_spec['components_list']
117 	      unless components_list
118 	        Astute.logger.warn("Wrong pattern #{pattern_spec.inspect} defined for calculating progress via logs.")
119 	        return 0
120 	      end
121 
122 	      chunk = get_chunk(fo, pos=pattern_spec['file_pos'])
123 	      return 0 unless chunk
124 	      pos = chunk.rindex(separator)
125 	      chunk = chunk.slice((pos + separator.size)..-1) if pos
126 	      block = chunk.split("\n")
127 
128 	      # Update progress of each component.
129 	      while block.any?
130 	        string = block.pop
131 	        components_list.each do |component|
132 	          matched_pattern = nil
133 	          component['patterns'].each do |pattern|
134 	            if pattern['regexp']
135 	              matched_pattern = pattern if string.match(pattern['pattern'])
136 	            else
137 	              matched_pattern = pattern if string.include?(pattern['pattern'])
138 	            end
139 	            break if matched_pattern
140 	          end
141 	          if matched_pattern and
142 	              (not component['_progress'] or matched_pattern['progress'] > component['_progress'])
143 	            component['_progress'] = matched_pattern['progress']
144 	          end
145 	        end
146 	      end
147 
148 	      # Calculate integral progress.
149 	      weighted_components = components_list.select{|n| n['weight']}
150 	      weight_sum = 0.0
151 	      if weighted_components.any?
152 	        weighted_components.each{|n| weight_sum += n['weight']}
153 	        weight_sum = weight_sum * components_list.length / weighted_components.length
154 	        raise "Total weight of weighted components equal to zero." if weight_sum == 0
155 	      end
156 	      nonweighted_delta = 1.0 / components_list.length
157 	      progress = 0
158 	      components_list.each do |component|
159 	        component['_progress'] = 0.0 unless component['_progress']
160 	        weight = component['weight']
161 	        if weight
162 	          progress += component['_progress'] * weight / weight_sum
163 	        else
164 	          progress += component['_progress'] * nonweighted_delta
165 	        end
166 	      end
167 
168 	      return progress
169 	    end
170 		end
171   end
172 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-logparser-parser_patterns_rb.html b/coverage/rcov/lib-astute-logparser-parser_patterns_rb.html new file mode 100644 index 00000000..dafc87a8 --- /dev/null +++ b/coverage/rcov/lib-astute-logparser-parser_patterns_rb.html @@ -0,0 +1,1661 @@ + + + + lib/astute/logparser/parser_patterns.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/logparser/parser_patterns.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/logparser/parser_patterns.rb5348
99.81%
+
+
+
+
87.50%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 module Astute
18   module LogParser
19     module Patterns
20       def self.get_default_pattern(key)
21         return Marshal.load(Marshal.dump(@default_patterns[key]))
22       end
23 
24       def self.list_default_patterns
25         return @default_patterns.keys
26       end
27 
28       @default_patterns = {
29         'anaconda-log-supposed-time-baremetal' => # key for default baremetal provision pattern
30           {'type' => 'supposed-time',
31           'chunk_size' => 10000,
32           'date_format' => '%Y-%m-%dT%H:%M:%S',
33           'date_regexp' => '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}',
34           'pattern_list' => [
35             {'pattern' => 'Running anaconda script', 'supposed_time' => 60},
36             {'pattern' => 'moving (1) to step enablefilesystems', 'supposed_time' => 3},
37             {'pattern' => "notifying kernel of 'change' event on device", 'hdd_size_multiplier' => 0.3},
38             {'pattern' => 'Preparing to install packages', 'supposed_time' => 9},
39             {'pattern' => 'Installing glibc-common-2.12', 'supposed_time' => 9},
40             {'pattern' => 'Installing bash-4.1.2', 'supposed_time' => 11},
41             {'pattern' => 'Installing coreutils-8.4-19', 'supposed_time' => 20},
42             {'pattern' => 'Installing centos-release-6-3', 'supposed_time' => 21},
43             {'pattern' => 'Installing attr-2.4.44', 'supposed_time' => 23},
44             {'pattern' => 'leaving (1) step installpackages', 'supposed_time' => 60},
45             {'pattern' => 'moving (1) to step postscripts', 'supposed_time' => 4},
46             {'pattern' => 'leaving (1) step postscripts', 'supposed_time' => 130},
47             {'pattern' => 'wait while node rebooting', 'supposed_time' => 20},
48             ].reverse,
49           'filename' => 'install/anaconda.log'
50           },
51 
52         'anaconda-log-supposed-time-kvm' => # key for default kvm provision pattern
53           {'type' => 'supposed-time',
54           'chunk_size' => 10000,
55           'date_format' => '%Y-%m-%dT%H:%M:%S',
56           'date_regexp' => '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}',
57           'pattern_list' => [
58             {'pattern' => 'Running anaconda script', 'supposed_time' => 60},
59             {'pattern' => 'moving (1) to step enablefilesystems', 'supposed_time' => 3},
60             {'pattern' => "notifying kernel of 'change' event on device", 'hdd_size_multiplier' => 1.5},
61             {'pattern' => 'Preparing to install packages', 'supposed_time' => 12},
62             {'pattern' => 'Installing glibc-common-2.12', 'supposed_time' => 15},
63             {'pattern' => 'Installing bash-4.1.2', 'supposed_time' => 15},
64             {'pattern' => 'Installing coreutils-8.4-19', 'supposed_time' => 33},
65             {'pattern' => 'Installing centos-release-6-3', 'supposed_time' => 21},
66             {'pattern' => 'Installing attr-2.4.44', 'supposed_time' => 48},
67             {'pattern' => 'leaving (1) step installpackages', 'supposed_time' => 100},
68             {'pattern' => 'moving (1) to step postscripts', 'supposed_time' => 4},
69             {'pattern' => 'leaving (1) step postscripts', 'supposed_time' => 200},
70             {'pattern' => 'wait while node rebooting', 'supposed_time' => 20},
71             ].reverse,
72           'filename' => 'install/anaconda.log'
73           },
74 
75         'puppet-log-components-list-ha-controller' =>   # key for default HA deploy pattern
76           {'type' => 'components-list',
77           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
78           'chunk_size' => 40000,
79           'filename' => 'puppet-agent.log',
80           'components_list' => [
81             {'name' => 'Galera', 'weight' => 5, 'patterns' => [
82                {'pattern' => '/Stage[main]/Galera/File[/etc/mysql]/ensure) created', 'progress' => 0.1},
83                {'pattern' => '/Stage[main]/Galera/Package[galera]/ensure) created', 'progress' => 0.3},
84                {'pattern' => '/Stage[main]/Galera/Package[MySQL-client]/ensure) created', 'progress' => 0.4},
85                {'pattern' => '/Stage[main]/Galera/Package[MySQL-server]/ensure) created', 'progress' => 0.6},
86                {'pattern' => "/Stage[main]/Galera/Service[mysql-galera]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.8},
87                {'pattern' => '/Stage[main]/Galera/Exec[wait-for-synced-state]/returns) executed successfully', 'progress' => 0.9},
88                {'pattern' => '/Stage[main]/Galera::Galera_master_final_config/Exec'\
89                              '[first-galera-node-final-config]/returns) executed successfully', 'progress' => 1},
90                ]
91             },
92             {'name' => 'Glance', 'weight' => 5, 'patterns' => [
93                {'pattern' => '/Stage[main]/Glance/Package[glance]/ensure) created', 'progress' => 0.1},
94                {'pattern' => '/Stage[main]/Glance::Db::Mysql/Mysql::Db[glance]/Database[glance]/ensure) created', 'progress' => 0.5},
95                {'pattern' => '/Stage[main]/Glance::Db::Mysql/Glance::Db::Mysql::Host_access[]/'\
96                              'Database_user[glance@]/ensure) created', 'progress' => 0.7},
97                {'pattern' => '/Stage[main]/Glance::Registry/Glance_registry_config[keystone_authtoken/'\
98                              'admin_user]/value) value changed', 'progress' => 0.71},
99                {'pattern' => '/Stage[main]/Glance::Keystone::Auth/Keystone_endpoint[glance]/ensure) created', 'progress' => 0.8},
100                {'pattern' => "/Stage[main]/Glance::Registry/Service[glance-registry]/ensure)"\
101                              " ensure changed 'stopped' to 'running'", 'progress' => 0.95},
102                {'pattern' => "/Stage[main]/Glance::Api/Service[glance-api]/ensure) ensure changed"\
103                              " 'stopped' to 'running'", 'progress' => 1},
104                ]
105             },
106             {'name' => 'Haproxy', 'weight' => 5, 'patterns' => [
107                {'pattern' => '/Stage[main]/Haproxy/Concat[/etc/haproxy/haproxy.cfg]/File[/var/lib/puppet/'\
108                              'concat/_etc_haproxy_haproxy.cfg]/ensure) created', 'progress' => 0.1},
109                {'pattern' => '/Stage[main]/Haproxy/Concat[/etc/haproxy/haproxy.cfg]/File[/var/lib/puppet/'\
110                              'concat/_etc_haproxy_haproxy.cfg/fragments.concat.out]/ensure) created', 'progress' => 0.4},
111                {'pattern' => '/Stage[main]/Haproxy/Concat[/etc/haproxy/haproxy.cfg]/Exec[concat_/etc/haproxy/'\
112                              'haproxy.cfg]/returns) executed successfully', 'progress' => 0.8},
113                {'pattern' => "/Stage[main]/Haproxy/Service[haproxy]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
114                ]
115             },
116             {'name' => 'Horizon', 'weight' => 5, 'patterns' => [
117                {'pattern' => '/Stage[main]/Horizon/Package[mod_wsgi]/ensure) created', 'progress' => 0.1},
118                {'pattern' => '/Stage[main]/Horizon/Package[openstack-dashboard]/ensure) created', 'progress' => 0.5},
119                {'pattern' => '/Stage[main]/Horizon/File[/etc/openstack-dashboard/'\
120                              'local_settings]/content) content changed', 'progress' => 0.8},
121                {'pattern' => "/Stage[main]/Horizon/Service[\$::horizon::params::http_service]/"\
122                              "ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
123                ]
124             },
125             {'name' => 'Keepalived', 'weight' => 1, 'patterns' => [
126                {'pattern' => '/Stage[main]/Keepalived::Install/Package[keepalived]/ensure) created', 'progress' => 0.2},
127                {'pattern' => '/Stage[main]/Keepalived::Config/Concat[/etc/keepalived/keepalived.conf]/'\
128                              'File[/etc/keepalived/keepalived.conf]/content) content changed', 'progress' => 0.6},
129                {'pattern' => "/Stage[main]/Keepalived::Service/Service[keepalived]/ensure) ensure"\
130                              " changed 'stopped' to 'running'", 'progress' => 1},
131                ]
132             },
133             {'name' => 'Keystone', 'weight' => 1, 'patterns' => [
134                {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 0.3},
135                {'pattern' => '/Stage[main]/Keystone::Db::Mysql/Mysql::Db[keystone]/Database[keystone]/ensure) created', 'progress' => 0.4},
136                {'pattern' => '/Stage[main]/Keystone/Package[keystone]/ensure) created', 'progress' => 0.6},
137                {'pattern' => '/Stage[main]/Keystone/Keystone_config[DEFAULT/admin_port]/ensure) created', 'progress' => 0.7},
138                {'pattern' => "/Stage[main]/Keystone/Service[keystone]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.8},
139                {'pattern' => '/Stage[main]/Keystone::Roles::Admin/Keystone_user_role[admin@admin]/ensure) created', 'progress' => 1},
140                ]
141             },
142             {'name' => 'Memcached', 'weight' => 1, 'patterns' => [
143                {'pattern' => '/Stage[main]/Memcached/User[memcached]/ensure) created', 'progress' => 0.1},
144                {'pattern' => '/Stage[main]/Memcached/Package[memcached]/ensure) created', 'progress' => 0.4},
145                {'pattern' => "/Stage[main]/Memcached/Service[memcached]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
146                ]
147             },
148             {'name' => 'Rabbitmq', 'weight' => 1, 'patterns' => [
149                {'pattern' => '/Stage[main]/Rabbitmq::Server/Package[rabbitmq-server]/ensure) created', 'progress' => 0.3},
150                {'pattern' => "/Stage[main]/Rabbitmq::Service/Service[rabbitmq-server]/ensure) ensure changed 'stopped' to 'running", 'progress' => 0.7},
151                {'pattern' => '/Stage[main]/Rabbitmq::Server/Rabbitmq_user[guest]/ensure) removed', 'progress' => 1},
152                ]
153             },
154             {'name' => 'Rsync/Xinetd', 'weight' => 1, 'patterns' => [
155                {'pattern' => '/Stage[main]/Xinetd/Package[xinetd]/ensure) created', 'progress' => 0.2},
156                {'pattern' => '(/Stage[main]/Xinetd/File[/etc/xinetd.conf]/content) content changed', 'progress' => 0.3},
157                {'pattern' => '/Stage[main]/Rsync::Server/File[/etc/rsync.d]/ensure) created', 'progress' => 0.5},
158                {'pattern' => '/Stage[main]/Rsync::Server/Xinetd::Service[rsync]/File[/etc/xinetd.d/rsync]/content) content changed', 'progress' => 1},
159                ]
160             },
161             {'name' => 'Swift', 'weight' => 10, 'patterns' => [
162                {'pattern' => '/Stage[main]/Swift::Xfs/Package[xfsprogs]/ensure) created', 'progress' => 0.01},
163                {'pattern' => '/Stage[main]/Swift/File[/etc/swift/swift.conf]/content) content changed', 'progress' => 0.05},
164                {'pattern' => '/Stage[main]/Swift/File[/home/swift]/ensure) created', 'progress' => 0.07},
165                {'pattern' => '/Stage[main]/Swift::Storage::All/File[/srv/node]/ensure) created', 'progress' => 0.1},
166                {'pattern' => '/Stage[main]/Swift::Storage::Account/Swift::Storage::Generic[account]/File'\
167                              '[/etc/swift/account-server/]/ensure) created', 'progress' => 0.12},
168                {'pattern' => '/Stage[main]/Swift::Storage::Object/Swift::Storage::Generic[object]/Package'\
169                              '[swift-object]/ensure) created', 'progress' => 0.15},
170                {'pattern' => "/Stage[main]/Swift::Storage::Account/Swift::Storage::Generic[account]/Service"\
171                              "[swift-account]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.18},
172                {'pattern' => "/Stage[main]/Swift::Storage::Object/Swift::Storage::Generic[object]/Service"\
173                              "[swift-object]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.2},
174                {'pattern' => '/Stage[main]/Swift::Keystone::Auth/Keystone_service[swift]/ensure) created', 'progress' => 0.23},
175                {'pattern' => '/Stage[main]/Swift::Keystone::Auth/Keystone_user_role[swift@services]/ensure) created', 'progress' => 0.28},
176                {'pattern' => '/Stage\[main\]/Swift::Storage::Container/Ring_container_device\[[0-9.:]+\]/ensure\) created',
177                              'regexp' => true, 'progress' => 0.33},
178                {'pattern' => "/Stage[main]/Swift::Storage::Account/Swift::Storage::Generic[account]/File[/etc/swift/"\
179                              "account-server/]/group) group changed 'root' to 'swift'", 'progress' => 0.36},
180                {'pattern' => '/Stage[main]/Swift::Ringbuilder/Swift::Ringbuilder::Rebalance[object]/Exec'\
181                              '[hours_passed_object]/returns) executed successfully', 'progress' => 0.39},
182                {'pattern' => '/Stage[main]/Swift::Ringbuilder/Swift::Ringbuilder::Rebalance[account]/Exec'\
183                              '[hours_passed_account]/returns) executed successfully', 'progress' => 0.42},
184                {'pattern' => '/Stage[main]/Swift::Ringbuilder/Swift::Ringbuilder::Rebalance[account]/Exec'\
185                              '[rebalance_account]/returns) executed successfully', 'progress' => 0.44},
186                {'pattern' => '/Stage[main]/Swift::Ringbuilder/Swift::Ringbuilder::Rebalance[container]/Exec'\
187                              '[hours_passed_container]/returns) executed successfully', 'progress' => 0.49},
188                {'pattern' => '/Stage[main]/Swift::Ringbuilder/Swift::Ringbuilder::Rebalance[container]/Exec'\
189                              '[rebalance_container]/returns) executed successfully', 'progress' => 0.52},
190                {'pattern' => '/Stage[main]/Swift::Proxy/Package[swift-proxy]/ensure) created', 'progress' => 0.55},
191                {'pattern' => '/Service[swift-container-replicator]/ensure) ensure changed \'stopped\'', 'progress' => 0.9},
192                {'pattern' => '/Service[swift-accaunt-replicator]/ensure) ensure changed \'stopped\'', 'progress' => 0.95},
193                {'pattern' => '/Service[swift-object-replicator]/ensure) ensure changed \'stopped\'', 'progress' => 1},
194                ]
195             },
196             {'name' => 'Nova', 'weight' => 10, 'patterns' => [
197                {'pattern' => '/Stage[main]/Nova::Utilities/Package[euca2ools]/ensure) created', 'progress' => 0.1},
198                {'pattern' => '/Stage[main]/Nova::Utilities/Package[parted]/ensure) created', 'progress' => 0.11},
199                {'pattern' => '/Stage[main]/Nova::Api/Nova::Generic_service[api]/Package[nova-api]/ensure) created', 'progress' => 0.13},
200                {'pattern' => '/Stage[main]/Nova::Utilities/Package[unzip]/ensure) created', 'progress' => 0.15},
201                {'pattern' => '/Stage[main]/Nova::Vncproxy/Package[python-numpy]/ensure) created', 'progress' => 0.2},
202                {'pattern' => '(/Stage[main]/Nova::Utilities/Package[libguestfs-tools-c]/ensure) created', 'progress' => 0.25},
203                {'pattern' => '/Stage[main]/Nova::Rabbitmq/Rabbitmq_user_permissions[nova@/]/ensure) created', 'progress' => 0.3},
204                {'pattern' => '/Stage[main]/Nova::Db::Mysql/Mysql::Db[nova]/Database[nova]/ensure) created', 'progress' => 0.35},
205                {'pattern' => "/Stage[main]/Nova::Db::Mysql/Mysql::Db[nova]/Database_grant"\
206                              "[nova@127.0.0.1/nova]/privileges) privileges changed '' to 'all'", 'progress' => 0.4},
207                {'pattern' => '/Stage[main]/Nova::Vncproxy/Nova::Generic_service[vncproxy]/Package'\
208                              '[nova-vncproxy]/ensure) created', 'progress' => 0.45},
209                {'pattern' => '/Stage[main]/Nova::Keystone::Auth/Keystone_service[nova_volume]/ensure) created', 'progress' => 0.5},
210                {'pattern' => '/Stage[main]/Nova::Keystone::Auth/Keystone_user_role[nova@services]/ensure) created', 'progress' => 0.55},
211                {'pattern' => '/Stage[main]/Nova/Exec[post-nova_config]/returns) Nova config has changed', 'progress' => 0.6},
212                {'pattern' => '/Stage[main]/Nova::Api/Exec[nova-db-sync]/returns) executed successfully', 'progress' => 0.7},
213                {'pattern' => "/Stage[main]/Nova::Consoleauth/Nova::Generic_service[consoleauth]/Service"\
214                              "[nova-consoleauth]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.85},
215                {'pattern' => '/Stage[main]/Nova::Network/Nova::Manage::Network[nova-vm-net]/Nova_network'\
216                              'nova-vm-net]/ensure) created', 'progress' => 1},
217                ]
218             },
219             {'name' => 'Openstack', 'weight' => 10, 'patterns' => [
220                {'pattern' => '/Stage[main]/Openstack::Firewall/File[iptables]/ensure) defined content as', 'progress' => 0.1},
221                {'pattern' => '/Stage[main]/Openstack::Glance/Package[swift]/ensure) created', 'progress' => 0.15},
222                {'pattern' => '/Stage[main]/Openstack::Auth_file/File[/root/openrc]/ensure) defined content as', 'progress' => 0.2},
223                {'pattern' => '/Stage[main]/Openstack::Controller_ha/Package[socat]/ensure) created', 'progress' => 0.25},
224                {'pattern' => '/Stage[main]/Openstack::Swift::Storage-node/Swift::Storage::Loopback[1]/File[/srv/loopback-device]/ensure) created', 'progress' => 0.3},
225                {'pattern' => '/Stage[main]/Openstack::Controller_ha/Exec[wait-for-haproxy-mysql-backend]/returns) executed successfully', 'progress' => 0.4},
226                {'pattern' => '/Stage[main]/Openstack::Controller/Nova_config[DEFAULT/memcached_servers]/ensure) created', 'progress' => 0.45},
227                {'pattern' => '/Stage[main]/Openstack::Nova::Controller/Nova_config[DEFAULT/multi_host]/ensure) created', 'progress' => 0.5},
228                {'pattern' => '/Stage[main]/Openstack::Firewall/Exec[startup-firewall]/returns) executed successfully', 'progress' => 0.65},
229                {'pattern' => '/Stage[main]/Openstack::Swift::Proxy/Ring_object_device\[[0-9.:]+\]/ensure\) created',
230                              'regexp' => true, 'progress' => 0.75},
231                {'pattern' => '/Stage[main]/Openstack::Swift::Proxy/Ring_container_device\[[0-9.:]+\]/ensure\) created',
232                              'regexp' => true, 'progress' => 0.8},
233                {'pattern' => '/Stage[main]/Openstack::Img::Cirros/Package[cirros-testvm]/ensure) created', 'progress' => 1},
234                ]
235             },
236             ]
237           },
238 
239         'puppet-log-components-list-ha-compute' =>
240           {'type' => 'components-list',
241           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
242           'chunk_size' => 40000,
243           'filename' => 'puppet-agent.log',
244           'components_list' => [
245             {'name' => 'Keystone', 'weight' => 10, 'patterns' => [
246                {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 1},
247                ]
248             },
249             {'name' => 'Mysql', 'weight' => 10, 'patterns' => [
250                {'pattern' => '/Stage[main]/Mysql::Python/Package[python-mysqldb]/ensure) created', 'progress' => 1},
251                ]
252             },
253             {'name' => 'Nova', 'weight' => 5, 'patterns' => [
254                {'pattern' => '/Stage[main]/Nova::Utilities/Package[euca2ools]/ensure) created', 'progress' => 0.1},
255                {'pattern' => '/Stage[main]/Nova::Utilities/Package[parted]/ensure) created', 'progress' => 0.2},
256                {'pattern' => '/Stage[main]/Nova::Api/Nova::Generic_service[api]/Package[nova-api]/ensure) created', 'progress' => 0.28},
257                {'pattern' => '/Stage[main]/Nova::Utilities/Package[unzip]/ensure) created', 'progress' => 0.32},
258                {'pattern' => '/Stage[main]/Nova::Vncproxy/Package[python-numpy]/ensure) created', 'progress' => 0.35},
259                {'pattern' => '/Stage[main]/Nova::Utilities/Package[libguestfs-tools-c]/ensure) created', 'progress' => 0.4},
260                {'pattern' => '/Stage[main]/Nova::Rabbitmq/Rabbitmq_user_permissions[nova@/]/ensure) created', 'progress' => 0.43},
261                {'pattern' => '/Stage[main]/Nova/Exec[post-nova_config]/returns) Nova config has changed', 'progress' => 0.8},
262                {'pattern' => '/Stage[main]/Nova::Api/Exec[nova-db-sync]/returns) executed successfully', 'progress' => 0.85},
263                {'pattern' => '/Stage[main]/Nova::Network/Nova::Manage::Network[nova-vm-net]/Nova_network'\
264                              'nova-vm-net]/ensure) created', 'progress' => 1},
265                ]
266             },
267             {'name' => 'Nova::Compute', 'weight' => 15, 'patterns' => [
268                {'pattern' => '/Stage[main]/Nova::Compute/Package[bridge-utils]/ensure) created', 'progress' => 0.15},
269                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Exec[symlink-qemu-kvm]/returns) executed successfully', 'progress' => 0.25},
270                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Package[libvirt]/ensure) created', 'progress' => 0.3},
271                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Package[dnsmasq-utils]/ensure) created', 'progress' => 0.5},
272                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Nova_config[DEFAULT/vncserver_listen]/ensure) created', 'progress' => 0.55},
273                {'pattern' => '/Stage[main]/Nova::Compute/Nova::Generic_service[compute]/Package[nova-compute]/ensure) created', 'progress' => 0.88},
274                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Package[avahi]/ensure) created', 'progress' => 0.9},
275                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Service[messagebus]/ensure) ensure changed', 'progress' => 0.93},
276                {'pattern' => '/Stage[main]/Nova::Compute/Nova::Generic_service[compute]/Service[nova-compute]/ensure) ensure changed', 'progress' => 0.97},
277                {'pattern' => '/Stage[main]/Nova::Compute/Nova::Generic_service[compute]/Service[nova-compute]) Triggered', 'progress' => 1},
278                ]
279             },
280             {'name' => 'Openstack', 'weight' => 2, 'patterns' => [
281                {'pattern' => '/Stage[main]/Openstack::Compute/Nova_config[DEFAULT/metadata_host]/ensure) created', 'progress' => 0.2},
282                {'pattern' => '/Stage[main]/Openstack::Compute/Nova_config[DEFAULT/memcached_servers]/ensure) created', 'progress' => 0.4},
283                {'pattern' => '/Stage[main]/Openstack::Compute/Augeas[sysconfig-libvirt]/returns) executed successfully', 'progress' => 0.5},
284                {'pattern' => '/Stage[main]/Openstack::Compute/Nova_config[DEFAULT/multi_host]/ensure) created', 'progress' => 0.8},
285                {'pattern' => '/Stage[main]/Openstack::Compute/Augeas[libvirt-conf]/returns) executed successfully', 'progress' => 1},
286                ]
287             },
288             ]
289           },
290 
291         'puppet-log-components-list-singlenode-controller' =>
292           {'type' => 'components-list',
293           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
294           'chunk_size' => 40000,
295           'filename' => 'puppet-agent.log',
296           'components_list' => [
297             {'name' => 'Glance', 'weight' => 10, 'patterns' => [
298                {'pattern' => '/Stage[main]/Glance/Package[glance]/ensure) created', 'progress' => 0.1},
299                {'pattern' => '/Stage[main]/Glance::Db::Mysql/Mysql::Db[glance]/Database[glance]/ensure) created', 'progress' => 0.5},
300                {'pattern' => '/Stage[main]/Glance::Db::Mysql/Glance::Db::Mysql::Host_access[]/'\
301                              'Database_user[glance@]/ensure) created', 'progress' => 0.7},
302                {'pattern' => '/Stage[main]/Glance::Registry/Glance_registry_config[keystone_authtoken/'\
303                              'admin_user]/value) value changed', 'progress' => 0.71},
304                {'pattern' => '/Stage[main]/Glance::Keystone::Auth/Keystone_endpoint[glance]/ensure) created', 'progress' => 0.8},
305                {'pattern' => "/Stage[main]/Glance::Registry/Service[glance-registry]/ensure)"\
306                              " ensure changed 'stopped' to 'running'", 'progress' => 0.95},
307                {'pattern' => "/Stage[main]/Glance::Api/Service[glance-api]/ensure) ensure changed"\
308                              " 'stopped' to 'running'", 'progress' => 1},
309                ]
310             },
311             {'name' => 'Horizon', 'weight' => 10, 'patterns' => [
312                {'pattern' => '/Stage[main]/Horizon/Package[mod_wsgi]/ensure) created', 'progress' => 0.3},
313                {'pattern' => '/Stage[main]/Horizon/Package[openstack-dashboard]/ensure) created', 'progress' => 0.6},
314                {'pattern' => '/Stage[main]/Horizon/File[/etc/openstack-dashboard/'\
315                              'local_settings]/content) content changed', 'progress' => 0.8},
316                {'pattern' => "/Stage[main]/Horizon/Service[\$::horizon::params::http_service]/"\
317                              "ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
318                ]
319             },
320             {'name' => 'Keystone', 'weight' => 10, 'patterns' => [
321                {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 0.3},
322                {'pattern' => '/Stage[main]/Keystone::Db::Mysql/Mysql::Db[keystone]/Database[keystone]/ensure) created', 'progress' => 0.4},
323                {'pattern' => '/Stage[main]/Keystone/Package[keystone]/ensure) created', 'progress' => 0.6},
324                {'pattern' => '/Stage[main]/Keystone/Keystone_config[DEFAULT/admin_port]/ensure) created', 'progress' => 0.7},
325                {'pattern' => "/Stage[main]/Keystone/Service[keystone]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.8},
326                {'pattern' => '/Stage[main]/Keystone::Roles::Admin/Keystone_user_role[admin@admin]/ensure) created', 'progress' => 1},
327                ]
328             },
329             {'name' => 'Memcached', 'weight' => 10, 'patterns' => [
330                {'pattern' => '/Stage[main]/Memcached/User[memcached]/ensure) created', 'progress' => 0.3},
331                {'pattern' => '/Stage[main]/Memcached/Package[memcached]/ensure) created', 'progress' => 0.6},
332                {'pattern' => "/Stage[main]/Memcached/Service[memcached]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
333                ]
334             },
335             {'name' => 'Rabbitmq', 'weight' => 10, 'patterns' => [
336                {'pattern' => '/Stage[main]/Rabbitmq::Server/Package[rabbitmq-server]/ensure) created', 'progress' => 0.3},
337                {'pattern' => "/Stage[main]/Rabbitmq::Service/Service[rabbitmq-server]/ensure) ensure changed 'stopped' to 'running", 'progress' => 0.7},
338                {'pattern' => '/Stage[main]/Rabbitmq::Server/Rabbitmq_user[guest]/ensure) removed', 'progress' => 1},
339                ]
340             },
341             {'name' => 'Nova', 'weight' => 10, 'patterns' => [
342                {'pattern' => '/Stage[main]/Nova::Utilities/Package[euca2ools]/ensure) created', 'progress' => 0.1},
343                {'pattern' => '/Stage[main]/Nova::Utilities/Package[parted]/ensure) created', 'progress' => 0.2},
344                {'pattern' => '/Stage[main]/Nova::Api/Nova::Generic_service[api]/Package[nova-api]/ensure) created', 'progress' => 0.28},
345                {'pattern' => '/Stage[main]/Nova::Utilities/Package[unzip]/ensure) created', 'progress' => 0.32},
346                {'pattern' => '/Stage[main]/Nova::Vncproxy/Package[python-numpy]/ensure) created', 'progress' => 0.35},
347                {'pattern' => '(/Stage[main]/Nova::Utilities/Package[libguestfs-tools-c]/ensure) created', 'progress' => 0.4},
348                {'pattern' => '/Stage[main]/Nova::Rabbitmq/Rabbitmq_user_permissions[nova@/]/ensure) created', 'progress' => 0.43},
349                {'pattern' => '/Stage[main]/Nova::Db::Mysql/Mysql::Db[nova]/Database[nova]/ensure) created', 'progress' => 0.48},
350                {'pattern' => "/Stage[main]/Nova::Db::Mysql/Mysql::Db[nova]/Database_grant"\
351                              "[nova@127.0.0.1/nova]/privileges) privileges changed '' to 'all'", 'progress' => 0.51},
352                {'pattern' => '/Stage[main]/Nova::Vncproxy/Nova::Generic_service[vncproxy]/Package'\
353                              '[nova-vncproxy]/ensure) created', 'progress' => 0.6},
354                {'pattern' => '/Stage[main]/Nova::Keystone::Auth/Keystone_service[nova_volume]/ensure) created', 'progress' => 0.68},
355                {'pattern' => '/Stage[main]/Nova::Keystone::Auth/Keystone_user_role[nova@services]/ensure) created', 'progress' => 0.75},
356                {'pattern' => '/Stage[main]/Nova/Exec[post-nova_config]/returns) Nova config has changed', 'progress' => 0.8},
357                {'pattern' => '/Stage[main]/Nova::Api/Exec[nova-db-sync]/returns) executed successfully', 'progress' => 0.85},
358                {'pattern' => "/Stage[main]/Nova::Consoleauth/Nova::Generic_service[consoleauth]/Service"\
359                              "[nova-consoleauth]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.9},
360                {'pattern' => '/Stage[main]/Nova::Network/Nova::Manage::Network[nova-vm-net]/Nova_network'\
361                              'nova-vm-net]/ensure) created', 'progress' => 1},
362                ]
363             },
364             {'name' => 'Openstack', 'weight' => 10, 'patterns' => [
365                {'pattern' => '/Stage[main]/Openstack::Firewall/File[iptables]/ensure) defined content as', 'progress' => 0.1},
366                {'pattern' => '/Stage[main]/Openstack::Glance/Package[swift]/ensure) created', 'progress' => 0.15},
367                {'pattern' => '/Stage[main]/Openstack::Auth_file/File[/root/openrc]/ensure) defined content as', 'progress' => 0.2},
368                {'pattern' => '/Stage[main]/Openstack::Controller/Nova_config[DEFAULT/memcached_servers]/ensure) created', 'progress' => 0.45},
369                {'pattern' => '/Stage[main]/Openstack::Nova::Controller/Nova_config[DEFAULT/multi_host]/ensure) created', 'progress' => 0.5},
370                {'pattern' => '/Stage[main]/Openstack::Firewall/Exec[startup-firewall]/returns) executed successfully', 'progress' => 0.65},
371                {'pattern' => '/Stage[main]/Openstack::Img::Cirros/Package[cirros-testvm]/ensure) created', 'progress' => 1},
372                ]
373             },
374             ]
375           },
376 
377         'puppet-log-components-list-multinode-controller' =>
378           {'type' => 'components-list',
379           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
380           'chunk_size' => 40000,
381           'filename' => 'puppet-agent.log',
382           'components_list' => [
383             {'name' => 'Glance', 'weight' => 10, 'patterns' => [
384                {'pattern' => '/Stage[main]/Glance/Package[glance]/ensure) created', 'progress' => 0.1},
385                {'pattern' => '/Stage[main]/Glance::Db::Mysql/Mysql::Db[glance]/Database[glance]/ensure) created', 'progress' => 0.5},
386                {'pattern' => '/Stage[main]/Glance::Db::Mysql/Glance::Db::Mysql::Host_access[]/'\
387                              'Database_user[glance@]/ensure) created', 'progress' => 0.7},
388                {'pattern' => '/Stage[main]/Glance::Registry/Glance_registry_config[keystone_authtoken/'\
389                              'admin_user]/value) value changed', 'progress' => 0.71},
390                {'pattern' => '/Stage[main]/Glance::Keystone::Auth/Keystone_endpoint[glance]/ensure) created', 'progress' => 0.8},
391                {'pattern' => "/Stage[main]/Glance::Registry/Service[glance-registry]/ensure)"\
392                              " ensure changed 'stopped' to 'running'", 'progress' => 0.95},
393                {'pattern' => "/Stage[main]/Glance::Api/Service[glance-api]/ensure) ensure changed"\
394                              " 'stopped' to 'running'", 'progress' => 1},
395                ]
396             },
397             {'name' => 'Horizon', 'weight' => 10, 'patterns' => [
398                {'pattern' => '/Stage[main]/Horizon/Package[mod_wsgi]/ensure) created', 'progress' => 0.3},
399                {'pattern' => '/Stage[main]/Horizon/Package[openstack-dashboard]/ensure) created', 'progress' => 0.6},
400                {'pattern' => '/Stage[main]/Horizon/File[/etc/openstack-dashboard/'\
401                              'local_settings]/content) content changed', 'progress' => 0.8},
402                {'pattern' => "/Stage[main]/Horizon/Service[\$::horizon::params::http_service]/"\
403                              "ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
404                ]
405             },
406             {'name' => 'Keystone', 'weight' => 10, 'patterns' => [
407                {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 0.3},
408                {'pattern' => '/Stage[main]/Keystone::Db::Mysql/Mysql::Db[keystone]/Database[keystone]/ensure) created', 'progress' => 0.4},
409                {'pattern' => '/Stage[main]/Keystone/Package[keystone]/ensure) created', 'progress' => 0.6},
410                {'pattern' => '/Stage[main]/Keystone/Keystone_config[DEFAULT/admin_port]/ensure) created', 'progress' => 0.7},
411                {'pattern' => "/Stage[main]/Keystone/Service[keystone]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.8},
412                {'pattern' => '/Stage[main]/Keystone::Roles::Admin/Keystone_user_role[admin@admin]/ensure) created', 'progress' => 1},
413                ]
414             },
415             {'name' => 'Memcached', 'weight' => 10, 'patterns' => [
416                {'pattern' => '/Stage[main]/Memcached/User[memcached]/ensure) created', 'progress' => 0.3},
417                {'pattern' => '/Stage[main]/Memcached/Package[memcached]/ensure) created', 'progress' => 0.6},
418                {'pattern' => "/Stage[main]/Memcached/Service[memcached]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 1},
419                ]
420             },
421             {'name' => 'Rabbitmq', 'weight' => 10, 'patterns' => [
422                {'pattern' => '/Stage[main]/Rabbitmq::Server/Package[rabbitmq-server]/ensure) created', 'progress' => 0.3},
423                {'pattern' => "/Stage[main]/Rabbitmq::Service/Service[rabbitmq-server]/ensure) ensure changed 'stopped' to 'running", 'progress' => 0.7},
424                {'pattern' => '/Stage[main]/Rabbitmq::Server/Rabbitmq_user[guest]/ensure) removed', 'progress' => 1},
425                ]
426             },
427             {'name' => 'Nova', 'weight' => 10, 'patterns' => [
428                {'pattern' => '/Stage[main]/Nova::Utilities/Package[euca2ools]/ensure) created', 'progress' => 0.1},
429                {'pattern' => '/Stage[main]/Nova::Utilities/Package[parted]/ensure) created', 'progress' => 0.2},
430                {'pattern' => '/Stage[main]/Nova::Api/Nova::Generic_service[api]/Package[nova-api]/ensure) created', 'progress' => 0.28},
431                {'pattern' => '/Stage[main]/Nova::Utilities/Package[unzip]/ensure) created', 'progress' => 0.32},
432                {'pattern' => '/Stage[main]/Nova::Vncproxy/Package[python-numpy]/ensure) created', 'progress' => 0.35},
433                {'pattern' => '(/Stage[main]/Nova::Utilities/Package[libguestfs-tools-c]/ensure) created', 'progress' => 0.4},
434                {'pattern' => '/Stage[main]/Nova::Rabbitmq/Rabbitmq_user_permissions[nova@/]/ensure) created', 'progress' => 0.43},
435                {'pattern' => '/Stage[main]/Nova::Db::Mysql/Mysql::Db[nova]/Database[nova]/ensure) created', 'progress' => 0.48},
436                {'pattern' => "/Stage[main]/Nova::Db::Mysql/Mysql::Db[nova]/Database_grant"\
437                              "[nova@127.0.0.1/nova]/privileges) privileges changed '' to 'all'", 'progress' => 0.51},
438                {'pattern' => '/Stage[main]/Nova::Vncproxy/Nova::Generic_service[vncproxy]/Package'\
439                              '[nova-vncproxy]/ensure) created', 'progress' => 0.6},
440                {'pattern' => '/Stage[main]/Nova::Keystone::Auth/Keystone_service[nova_volume]/ensure) created', 'progress' => 0.68},
441                {'pattern' => '/Stage[main]/Nova::Keystone::Auth/Keystone_user_role[nova@services]/ensure) created', 'progress' => 0.75},
442                {'pattern' => '/Stage[main]/Nova/Exec[post-nova_config]/returns) Nova config has changed', 'progress' => 0.8},
443                {'pattern' => '/Stage[main]/Nova::Api/Exec[nova-db-sync]/returns) executed successfully', 'progress' => 0.85},
444                {'pattern' => "/Stage[main]/Nova::Consoleauth/Nova::Generic_service[consoleauth]/Service"\
445                              "[nova-consoleauth]/ensure) ensure changed 'stopped' to 'running'", 'progress' => 0.9},
446                {'pattern' => '/Stage[main]/Nova::Network/Nova::Manage::Network[nova-vm-net]/Nova_network'\
447                              'nova-vm-net]/ensure) created', 'progress' => 1},
448                ]
449             },
450             {'name' => 'Openstack', 'weight' => 10, 'patterns' => [
451                {'pattern' => '/Stage[main]/Openstack::Firewall/File[iptables]/ensure) defined content as', 'progress' => 0.1},
452                {'pattern' => '/Stage[main]/Openstack::Glance/Package[swift]/ensure) created', 'progress' => 0.15},
453                {'pattern' => '/Stage[main]/Openstack::Auth_file/File[/root/openrc]/ensure) defined content as', 'progress' => 0.2},
454                {'pattern' => '/Stage[main]/Openstack::Controller/Nova_config[DEFAULT/memcached_servers]/ensure) created', 'progress' => 0.45},
455                {'pattern' => '/Stage[main]/Openstack::Nova::Controller/Nova_config[DEFAULT/multi_host]/ensure) created', 'progress' => 0.5},
456                {'pattern' => '/Stage[main]/Openstack::Firewall/Exec[startup-firewall]/returns) executed successfully', 'progress' => 0.65},
457                {'pattern' => '/Stage[main]/Openstack::Img::Cirros/Package[cirros-testvm]/ensure) created', 'progress' => 1},
458                ]
459             },
460             ]
461           },
462 
463         'puppet-log-components-list-multinode-compute' =>
464           {'type' => 'components-list',
465           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
466           'chunk_size' => 40000,
467           'filename' => 'puppet-agent.log',
468           'components_list' => [
469             {'name' => 'Keystone', 'weight' => 10, 'patterns' => [
470                {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 1},
471                ]
472             },
473             {'name' => 'Mysql', 'weight' => 10, 'patterns' => [
474                {'pattern' => '/Stage[main]/Mysql::Python/Package[python-mysqldb]/ensure) created', 'progress' => 1},
475                ]
476             },
477             {'name' => 'Nova', 'weight' => 5, 'patterns' => [
478                {'pattern' => '/Stage[main]/Nova::Utilities/Package[euca2ools]/ensure) created', 'progress' => 0.1},
479                {'pattern' => '/Stage[main]/Nova::Utilities/Package[parted]/ensure) created', 'progress' => 0.2},
480                {'pattern' => '/Stage[main]/Nova::Api/Nova::Generic_service[api]/Package[nova-api]/ensure) created', 'progress' => 0.28},
481                {'pattern' => '/Stage[main]/Nova::Utilities/Package[unzip]/ensure) created', 'progress' => 0.32},
482                {'pattern' => '/Stage[main]/Nova::Vncproxy/Package[python-numpy]/ensure) created', 'progress' => 0.35},
483                {'pattern' => '/Stage[main]/Nova::Utilities/Package[libguestfs-tools-c]/ensure) created', 'progress' => 0.4},
484                {'pattern' => '/Stage[main]/Nova::Rabbitmq/Rabbitmq_user_permissions[nova@/]/ensure) created', 'progress' => 0.43},
485                {'pattern' => '/Stage[main]/Nova/Exec[post-nova_config]/returns) Nova config has changed', 'progress' => 0.8},
486                {'pattern' => '/Stage[main]/Nova::Api/Exec[nova-db-sync]/returns) executed successfully', 'progress' => 0.85},
487                {'pattern' => '/Stage[main]/Nova::Network/Nova::Manage::Network[nova-vm-net]/Nova_network'\
488                              'nova-vm-net]/ensure) created', 'progress' => 1},
489                ]
490             },
491             {'name' => 'Nova::Compute', 'weight' => 15, 'patterns' => [
492                {'pattern' => '/Stage[main]/Nova::Compute/Package[bridge-utils]/ensure) created', 'progress' => 0.15},
493                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Exec[symlink-qemu-kvm]/returns) executed successfully', 'progress' => 0.25},
494                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Package[libvirt]/ensure) created', 'progress' => 0.3},
495                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Package[dnsmasq-utils]/ensure) created', 'progress' => 0.5},
496                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Nova_config[DEFAULT/vncserver_listen]/ensure) created', 'progress' => 0.55},
497                {'pattern' => '/Stage[main]/Nova::Compute/Nova::Generic_service[compute]/Package[nova-compute]/ensure) created', 'progress' => 0.88},
498                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Package[avahi]/ensure) created', 'progress' => 0.9},
499                {'pattern' => '/Stage[main]/Nova::Compute::Libvirt/Service[messagebus]/ensure) ensure changed', 'progress' => 0.93},
500                {'pattern' => '/Stage[main]/Nova::Compute/Nova::Generic_service[compute]/Service[nova-compute]/ensure) ensure changed', 'progress' => 0.97},
501                {'pattern' => '/Stage[main]/Nova::Compute/Nova::Generic_service[compute]/Service[nova-compute]) Triggered', 'progress' => 1},
502                ]
503             },
504             {'name' => 'Openstack', 'weight' => 2, 'patterns' => [
505                {'pattern' => '/Stage[main]/Openstack::Compute/Nova_config[DEFAULT/metadata_host]/ensure) created', 'progress' => 0.2},
506                {'pattern' => '/Stage[main]/Openstack::Compute/Nova_config[DEFAULT/memcached_servers]/ensure) created', 'progress' => 0.4},
507                {'pattern' => '/Stage[main]/Openstack::Compute/Augeas[sysconfig-libvirt]/returns) executed successfully', 'progress' => 0.5},
508                {'pattern' => '/Stage[main]/Openstack::Compute/Nova_config[DEFAULT/multi_host]/ensure) created', 'progress' => 0.8},
509                {'pattern' => '/Stage[main]/Openstack::Compute/Augeas[libvirt-conf]/returns) executed successfully', 'progress' => 1},
510                ]
511             },
512             ]
513           },
514 
515         'puppet-log-components-list-ha-cinder' => {
516           'type' => 'count-lines',
517           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
518           'expected_line_number' => 345
519         },
520 
521         'puppet-log-components-list-multinode-cinder' => {
522           'type' => 'count-lines',
523           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
524           'expected_line_number' => 345
525         },
526 
527         'puppet-log-components-list-singlenode-cinder' => {
528           'type' => 'count-lines',
529           'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
530           'expected_line_number' => 345
531         },
532       }
533     end
534 	end
535 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-logparser-provision_rb.html b/coverage/rcov/lib-astute-logparser-provision_rb.html new file mode 100644 index 00000000..526eb23c --- /dev/null +++ b/coverage/rcov/lib-astute-logparser-provision_rb.html @@ -0,0 +1,821 @@ + + + + lib/astute/logparser/provision.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/logparser/provision.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/logparser/provision.rb254122
86.61%
+
+
+
+
72.13%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'date'
18 
19 module Astute
20   module LogParser
21     class ParseProvisionLogs <ParseNodeLogs
22       def initialize
23         pattern_spec = Patterns::get_default_pattern('anaconda-log-supposed-time-kvm')
24         super(pattern_spec)
25       end
26 
27       def progress_calculate(uids_to_calc, nodes)
28         # Just create correct pattern for each node and then call parent method.
29         uids_to_calc.each do |uid|
30           node = nodes.select {|n| n['uid'] == uid}[0]
31           unless @nodes_states[uid]
32             @nodes_states[uid] = get_pattern_for_node(node)
33           end
34         end
35         super(uids_to_calc, nodes)
36       end
37 
38       private
39       def calculate(fo, node_pattern_spec)
40         case node_pattern_spec['type']
41         when 'pattern-list'
42           progress = simple_pattern_finder(fo, node_pattern_spec)
43         when 'supposed-time'
44           progress = supposed_time_parser(fo, node_pattern_spec)
45         end
46         return progress
47       end
48 
49       def get_pattern_for_node(node)
50         if node['manufacturer'] == 'KVM'
51           pattern_spec = Patterns::get_default_pattern('anaconda-log-supposed-time-kvm')
52         else
53           pattern_spec = Patterns::get_default_pattern('anaconda-log-supposed-time-baremetal')
54         end
55         pattern_spec['path_prefix'] ||= PATH_PREFIX.to_s
56         pattern_spec['separator'] ||= SEPARATOR.to_s
57 
58         hdd = node['meta']['disks'].select{|disk| not disk['removable']}[0]
59         if hdd
60           # Convert size from bytes to GB
61           hdd_size = hdd['size'] / 10 ** 9
62         else
63           # Default hdd size is 20 GB
64           hdd_size = 20
65         end
66 
67         hdd_pattern = pattern_spec['pattern_list'].select {|el| el['hdd_size_multiplier']}[0]
68         hdd_pattern['supposed_time'] = (hdd_pattern['hdd_size_multiplier'] * hdd_size).to_i
69         return pattern_spec
70       end
71 
72 	    def supposed_time_parser(fo, pattern_spec)
73 	      # Pattern specification example:
74 	      # pattern_spec = {'type' => 'supposed-time',
75 	      #   'chunk_size' => 10000,
76 	      #   'date_format' => '%Y-%m-%dT%H:%M:%S',
77 	      #   'date_regexp' => '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}',
78 	      #   'pattern_list' => [
79 	      #     {'pattern' => 'Running anaconda script', 'supposed_time' => 60},
80 	      #     {'pattern' => 'moving (1) to step enablefilesystems', 'supposed_time' => 3},
81 	      #     {'pattern' => "notifying kernel of 'change' event on device", 'supposed_time' => 200},
82 	      #     {'pattern' => 'Preparing to install packages', 'supposed_time' => 9},
83 	      #     {'pattern' => 'Installing glibc-common-2.12', 'supposed_time' => 9},
84 	      #     {'pattern' => 'Installing bash-4.1.2', 'supposed_time' => 11},
85 	      #     {'pattern' => 'Installing coreutils-8.4-19', 'supposed_time' => 20},
86 	      #     {'pattern' => 'Installing centos-release-6-3', 'supposed_time' => 21},
87 	      #     {'pattern' => 'Installing attr-2.4.44', 'supposed_time' => 23},
88 	      #     {'pattern' => 'leaving (1) step installpackages', 'supposed_time' => 60},
89 	      #     {'pattern' => 'moving (1) to step postscripts', 'supposed_time' => 4},
90 	      #     {'pattern' => 'leaving (1) step postscripts', 'supposed_time' => 130},
91 	      #     ].reverse,
92 	      #   'filename' => 'install/anaconda.log'
93 	      #   }
94 	      # Use custom separator if defined.
95 	      separator = pattern_spec['separator']
96 	      log_patterns = pattern_spec['pattern_list']
97 	      date_format = pattern_spec['date_format']
98 	      date_regexp = pattern_spec['date_regexp']
99 	      unless date_regexp and date_format and log_patterns
100 	        Astute.logger.warn("Wrong pattern_spec #{pattern_spec.inspect} defined for calculating progress via logs.")
101 	        return 0
102 	      end
103 
104 	      def self.get_elapsed_time(patterns)
105 	        elapsed_time = 0
106 	        patterns.each do |p|
107 	          if p['_progress']
108 	            break
109 	          else
110 	            elapsed_time += p['supposed_time']
111 	          end
112 	        end
113 	        return elapsed_time
114 	      end
115 
116 	      def self.get_progress(base_progress, elapsed_time, delta_time, supposed_time=nil)
117 	        return 1.0 if elapsed_time.zero?
118 	        k = (1.0 - base_progress) / elapsed_time
119 	        supposed_time ? surplus = delta_time - supposed_time : surplus = nil
120 	        if surplus and surplus > 0
121 	          progress = supposed_time * k + surplus * k/3 + base_progress
122 	        else
123 	          progress = delta_time * k + base_progress
124 	        end
125 	        progress = 1.0 if progress > 1
126 	        return progress
127 	      end
128 
129 	      def self.get_seconds_from_time(date)
130 	        hours, mins, secs, frac = Date::day_fraction_to_time(date)
131 	        return hours*60*60 + mins*60 + secs
132 	      end
133 
134 
135 	      chunk = get_chunk(fo, pattern_spec['chunk_size'])
136 	      return 0 unless chunk
137 	      pos = chunk.rindex(separator)
138 	      chunk = chunk.slice((pos + separator.size)..-1) if pos
139 	      block = chunk.split("\n")
140 
141 	      now = DateTime.now()
142 	      prev_time = pattern_spec['_prev_time'] ||= now
143 	      prev_progress = pattern_spec['_prev_progress'] ||= 0
144 	      elapsed_time = pattern_spec['_elapsed_time'] ||= get_elapsed_time(log_patterns)
145 	      seconds_since_prev = get_seconds_from_time(now - prev_time)
146 
147 	      until block.empty?
148 	        string = block.pop
149 	        log_patterns.each do |pattern|
150 	          if string.include?(pattern['pattern'])
151 	            if pattern['_progress']
152 	              # We not found any new messages. Calculate progress with old data.
153 	              progress = get_progress(prev_progress, elapsed_time,
154 	                                      seconds_since_prev, pattern['supposed_time'])
155 	              return progress
156 
157 	            else
158 	              # We found message that we never find before. We need to: 
159 	              # calculate progress for this message;
160 	              # recalculate control point and elapsed_time;
161 	              # calculate progress for current time.
162 
163 	              # Trying to find timestamp of message.
164 	              date_string = string.match(date_regexp)
165 	              if date_string
166 	                # Get relative time when the message realy occured.
167 	                date = DateTime.strptime(date_string[0], date_format) - prev_time.offset
168 	                real_time = get_seconds_from_time(date - prev_time)
169 	                # Update progress of the message.
170 	                prev_supposed_time = log_patterns.select{|n| n['_progress'] == prev_progress}[0]
171 	                prev_supposed_time = prev_supposed_time['supposed_time'] if prev_supposed_time
172 	                progress = get_progress(prev_progress, elapsed_time, real_time, prev_supposed_time)
173 	                pattern['_progress'] = progress
174 	                # Recalculate elapsed time.
175 	                elapsed_time = pattern_spec['_elapsed_time'] = get_elapsed_time(log_patterns)
176 	                # Update time and progress for control point.
177 	                prev_time = pattern_spec['_prev_time'] = date
178 	                prev_progress = pattern_spec['_prev_progress'] = progress
179 	                seconds_since_prev = get_seconds_from_time(now - date)
180 	                # Calculate progress for current time.
181 	                progress = get_progress(prev_progress, elapsed_time,
182 	                                        seconds_since_prev, pattern['supposed_time'])
183 	                return progress
184 	              else
185 	                Astute.logger.info("Can't gather date (format: '#{date_regexp}') from string: #{string}")
186 	              end
187 	            end
188 	          end
189 	        end
190 	      end
191 	      # We found nothing.
192 	      progress = get_progress(prev_progress, elapsed_time, seconds_since_prev, log_patterns[0]['supposed_time'])
193 	      return progress
194 	    end
195 
196 	    def simple_pattern_finder(fo, pattern_spec)
197 	      # Pattern specification example:
198 	      # pattern_spec = {'type' => 'pattern-list', 'separator' => "custom separator\n",
199 	      #   'chunk_size' => 40000,
200 	        # 'pattern_list' => [
201 	        #   {'pattern' => 'Running kickstart %%pre script', 'progress' => 0.08},
202 	        #   {'pattern' => 'to step enablefilesystems', 'progress' => 0.09},
203 	        #   {'pattern' => 'to step reposetup', 'progress' => 0.13},
204 	        #   {'pattern' => 'to step installpackages', 'progress' => 0.16},
205 	        #   {'pattern' => 'Installing',
206 	        #     'number' => 210, # Now it install 205 packets. Add 5 packets for growth in future.
207 	        #     'p_min' => 0.16, # min percent
208 	        #     'p_max' => 0.87 # max percent
209 	        #     },
210 	        #   {'pattern' => 'to step postinstallconfig', 'progress' => 0.87},
211 	        #   {'pattern' => 'to step dopostaction', 'progress' => 0.92},
212 	        #   ]
213 	        # }
214 	      # Use custom separator if defined.
215 	      separator = pattern_spec['separator']
216 	      log_patterns = pattern_spec['pattern_list']
217 	      unless log_patterns
218 	        Astute.logger.warn("Wrong pattern #{pattern_spec.inspect} defined for calculating progress via logs.")
219 	        return 0
220 	      end
221 
222 	      chunk = get_chunk(fo, pattern_spec['chunk_size'])
223 	      # NOTE(mihgen): Following line fixes "undefined method `rindex' for nil:NilClass" for empty log file
224 	      return 0 unless chunk
225 	      pos = chunk.rindex(separator)
226 	      chunk = chunk.slice((pos + separator.size)..-1) if pos
227 	      block = chunk.split("\n")
228 	      return 0 unless block
229 	      while true
230 	        string = block.pop
231 	        return 0 unless string # If we found nothing
232 	        log_patterns.each do |pattern|
233 	          if string.include?(pattern['pattern'])
234 	            return pattern['progress'] if pattern['progress']
235 	            if pattern['number']
236 	              string = block.pop
237 	              counter = 1
238 	              while string
239 	                counter += 1 if string.include?(pattern['pattern'])
240 	                string = block.pop
241 	              end
242 	              progress = counter.to_f / pattern['number']
243 	              progress = 1 if progress > 1
244 	              progress = pattern['p_min'] + progress * (pattern['p_max'] - pattern['p_min'])
245 	              return progress
246 	            end
247 	            Astute.logger.warn("Wrong pattern #{pattern_spec.inspect} defined for calculating progress via log.")
248 	          end
249 	        end
250 	      end
251 	    end
252 
253     end
254   end
255 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-logparser_rb.html b/coverage/rcov/lib-astute-logparser_rb.html new file mode 100644 index 00000000..96cc4ae6 --- /dev/null +++ b/coverage/rcov/lib-astute-logparser_rb.html @@ -0,0 +1,503 @@ + + + + lib/astute/logparser.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/logparser.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/logparser.rb14878
91.22%
+
+
+
+
83.33%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 # -*- coding: utf-8 -*-
18 module Astute
19   module LogParser
20     LOG_PORTION = 10000
21     # Default values. Can be overrided by pattern_spec.
22     # E.g. pattern_spec = {'separator' => 'new_separator', ...}
23     PATH_PREFIX = '/var/log/remote/'
24     SEPARATOR = "SEPARATOR\n"
25 
26     class NoParsing
27       def initialize(*args)
28       end
29 
30       def method_missing(*args)
31         # We just eat the call if we don't want to deal with logs
32       end
33 
34       def progress_calculate(*args)
35         []
36       end
37     end
38 
39     class ParseNodeLogs
40       attr_reader :pattern_spec
41 
42       def initialize(pattern_spec)
43         @nodes_states = {}
44         @pattern_spec = pattern_spec
45         @pattern_spec['path_prefix'] ||= PATH_PREFIX.to_s
46         @pattern_spec['separator'] ||= SEPARATOR.to_s
47       end
48 
49       def progress_calculate(uids_to_calc, nodes)
50         nodes_progress = []
51         uids_to_calc.each do |uid|
52           node = nodes.select {|n| n['uid'] == uid}[0]  # NOTE: use nodes hash
53           node_pattern_spec = @nodes_states[uid]
54           unless node_pattern_spec
55             node_pattern_spec = Marshal.load(Marshal.dump(@pattern_spec))
56             @nodes_states[uid] = node_pattern_spec
57           end
58           path = "#{@pattern_spec['path_prefix']}#{node['fqdn']}/#{@pattern_spec['filename']}"
59 
60           begin
61             progress = (get_log_progress(path, node_pattern_spec)*100).to_i # Return percent of progress
62           rescue Exception => e
63             Astute.logger.warn "Some error occurred when calculate progress for node '#{uid}': #{e.message}, trace: #{e.backtrace.inspect}"
64             progress = 0
65           end
66 
67           nodes_progress << {
68             'uid' => uid,
69             'progress' => progress
70           }
71         end
72         nodes_progress
73       end
74 
75       def prepare(nodes)
76         @nodes_states = {}
77         nodes.each do |node|
78           path = "#{@pattern_spec['path_prefix']}#{node['ip']}/#{@pattern_spec['filename']}"
79           File.open(path, 'a') {|fo| fo.write @pattern_spec['separator'] } if File.writable?(path)
80         end
81       end
82 
83       def pattern_spec= (pattern_spec)
84         initialise(pattern_spec) # NOTE: bug?
85       end
86 
87     private
88 
89       def get_log_progress(path, node_pattern_spec)
90         unless File.readable?(path)
91           Astute.logger.debug "Can't read file with logs: #{path}"
92           return 0
93         end
94         if node_pattern_spec.nil?
95           Astute.logger.warn "Can't parse logs. Pattern_spec is empty."
96           return 0
97         end
98         progress = nil
99         File.open(path) do |fo|
100           # Try to find well-known ends of log.
101           endlog = find_endlog_patterns(fo, node_pattern_spec)
102           return endlog if endlog
103           # Start reading from end of file.
104           fo.pos = fo.stat.size
105 
106           # Method 'calculate' should be defined at child classes.
107           progress = calculate(fo, node_pattern_spec)
108           node_pattern_spec['file_pos'] = fo.pos
109         end
110         unless progress
111           Astute.logger.warn("Wrong pattern #{node_pattern_spec.inspect} defined for calculating progress via logs.")
112           return 0
113         end
114         progress
115       end
116 
117       def find_endlog_patterns(fo, pattern_spec)
118         # Pattern example:
119         # pattern_spec = {...,
120         #   'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}],
121         # }
122         endlog_patterns = pattern_spec['endlog_patterns']
123         return nil unless endlog_patterns
124         fo.pos = fo.stat.size
125         chunk = get_chunk(fo, 100)
126         return nil unless chunk
127         endlog_patterns.each do |pattern|
128           return pattern['progress'] if chunk.end_with?(pattern['pattern'])
129         end
130         nil
131       end
132 
133       def get_chunk(fo, size=nil, pos=nil)
134         if pos
135           fo.pos = pos
136           return fo.read
137         end
138         size = LOG_PORTION unless size
139         return nil if fo.pos == 0
140         size = fo.pos if fo.pos < size
141         next_pos = fo.pos - size
142         fo.pos = next_pos
143         block = fo.read(size)
144         fo.pos = next_pos
145         block
146       end
147     end
148   end
149 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-mclient_rb.html b/coverage/rcov/lib-astute-mclient_rb.html new file mode 100644 index 00000000..99f1e80e --- /dev/null +++ b/coverage/rcov/lib-astute-mclient_rb.html @@ -0,0 +1,464 @@ + + + + lib/astute/mclient.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/mclient.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/mclient.rb13575
91.11%
+
+
+
+
84.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'mcollective'
18 
19 module Astute
20   class MClient
21     include MCollective::RPC
22 
23     attr_accessor :retries
24 
25     def initialize(ctx, agent, nodes=nil, check_result=true, timeout=nil)
26       @task_id = ctx.task_id
27       @agent = agent
28       @nodes = nodes.map { |n| n.to_s } if nodes
29       @check_result = check_result
30       @retries = Astute.config.MC_RETRIES
31       @timeout = timeout
32       initialize_mclient
33     end
34 
35     def on_respond_timeout(&block)
36       @on_respond_timeout = block
37       self
38     end
39 
40     def method_missing(method, *args)
41       @mc_res = mc_send(method, *args)
42 
43       if method == :discover
44         @nodes = args[0][:nodes]
45         return @mc_res
46       end
47 
48       # Enable if needed. In normal case it eats the screen pretty fast
49       log_result(@mc_res, method)
50 
51       check_results_with_retries(method, args) if @check_result
52 
53       @mc_res
54     end
55 
56   private
57 
58     def check_results_with_retries(method, args)
59       err_msg = ''
60       # Following error might happen because of misconfiguration, ex. direct_addressing = 1 only on client
61       #  or.. could be just some hang? Let's retry if @retries is set
62       if @mc_res.length < @nodes.length
63         # some nodes didn't respond
64         retry_index = 1
65         while retry_index <= @retries
66           sleep rand
67           nodes_responded = @mc_res.map { |n| n.results[:sender] }
68           not_responded = @nodes - nodes_responded
69           Astute.logger.debug "Retry ##{retry_index} to run mcollective agent on nodes: '#{not_responded.join(',')}'"
70           mc_send :discover, :nodes => not_responded
71           @new_res = mc_send(method, *args)
72           log_result(@new_res, method)
73           # @new_res can have some nodes which finally responded
74           @mc_res += @new_res
75           break if @mc_res.length == @nodes.length
76           retry_index += 1
77         end
78         if @mc_res.length < @nodes.length
79           nodes_responded = @mc_res.map { |n| n.results[:sender] }
80           not_responded = @nodes - nodes_responded
81           if @on_respond_timeout
82             @on_respond_timeout.call not_responded
83           else
84             err_msg += "MCollective agents '#{not_responded.join(',')}' didn't respond. \n"
85           end
86         end
87       end
88       failed = @mc_res.select{|x| x.results[:statuscode] != 0 }
89       if failed.any?
90         err_msg += "MCollective call failed in agent '#{@agent}', "\
91                      "method '#{method}', failed nodes: #{failed.map{|x| x.results[:sender]}.join(',')} \n"
92       end
93       unless err_msg.empty?
94         Astute.logger.error err_msg
95         raise "#{@task_id}: #{err_msg}"
96       end
97     end
98 
99 
100     def mc_send(*args)
101       @mc.send(*args)
102     rescue => ex
103       case ex
104       when Stomp::Error::NoCurrentConnection
105         # stupid stomp cannot recover severed connection
106         stomp = MCollective::PluginManager["connector_plugin"]
107         stomp.disconnect rescue nil
108         stomp.instance_variable_set :@connection, nil
109         initialize_mclient
110       end
111       sleep rand
112       Astute.logger.error "Retrying MCollective call after exception: #{ex}"
113       retry
114     end
115 
116     def initialize_mclient
117       @mc = rpcclient(@agent, :exit_on_failure => false)
118       @mc.timeout = @timeout if @timeout
119       @mc.progress = false
120       if @nodes
121         @mc.discover :nodes => @nodes
122       end
123     rescue => ex
124       Astute.logger.error "Retrying RPC client instantiation after exception: #{ex}"
125       sleep 5
126       retry
127     end
128 
129     def log_result(result, method)
130       result.each do |node|
131         Astute.logger.debug "#{@task_id}: MC agent '#{node.agent}', method '#{method}', "\
132                             "results: #{node.results.inspect}"
133       end
134     end
135   end
136 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-metadata_rb.html b/coverage/rcov/lib-astute-metadata_rb.html new file mode 100644 index 00000000..d5988649 --- /dev/null +++ b/coverage/rcov/lib-astute-metadata_rb.html @@ -0,0 +1,149 @@ + + + + lib/astute/metadata.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/metadata.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/metadata.rb309
86.67%
+
+
+
+
55.56%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'json'
18 require 'ipaddr'
19 
20 module Astute
21   module Metadata
22     def self.publish_facts(ctx, uid, metadata)
23       # This is synchronious RPC call, so we are sure that data were sent and processed remotely
24       Astute.logger.info "#{ctx.task_id}: nailyfact - storing metadata for node uid=#{uid}"
25       Astute.logger.debug "#{ctx.task_id}: nailyfact stores metadata: #{metadata.inspect}"
26       nailyfact = MClient.new(ctx, "nailyfact", [uid])
27       # TODO(mihgen) check results!
28       stats = nailyfact.post(:value => metadata.to_json)
29     end
30   end
31 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-network_rb.html b/coverage/rcov/lib-astute-network_rb.html new file mode 100644 index 00000000..43e8b8c0 --- /dev/null +++ b/coverage/rcov/lib-astute-network_rb.html @@ -0,0 +1,395 @@ + + + + lib/astute/network.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/network.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/network.rb11245
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 module Astute
18   module Network
19 
20     def self.check_network(ctx, nodes)
21       if nodes.empty?
22         Astute.logger.info(
23           "#{ctx.task_id}: Network checker: nodes list is empty. Nothing to check.")
24         return {
25           'status' => 'error',
26           'error' => "Network verification requires a minimum of two nodes."
27         }
28       elsif nodes.length == 1
29         Astute.logger.info(
30           "#{ctx.task_id}: Network checker: nodes list contains one node only. Do nothing.")
31         return {'nodes' => [{
32           'uid' => nodes[0]['uid'],
33           'networks' => nodes[0]['networks']
34         }]}
35       end
36 
37       uids = nodes.map { |node| node['uid'].to_s }
38       # TODO Everything breakes if agent not found. We have to handle that
39       net_probe = MClient.new(ctx, "net_probe", uids)
40 
41       start_frame_listeners(ctx, net_probe, nodes)
42       ctx.reporter.report({'progress' => 30})
43 
44       send_probing_frames(ctx, net_probe, nodes)
45       ctx.reporter.report({'progress' => 60})
46 
47       net_probe.discover(:nodes => uids)
48       stats = net_probe.get_probing_info
49       result = format_result(stats)
50       Astute.logger.debug "#{ctx.task_id}: Network checking is done. Results: #{result.inspect}"
51 
52       {'nodes' => result}
53     end
54 
55     private
56     def self.start_frame_listeners(ctx, net_probe, nodes)
57       nodes.each do |node|
58         data_to_send = make_interfaces_to_send(node['networks'])
59 
60         Astute.logger.debug(
61           "#{ctx.task_id}: Network checker listen: node: #{node['uid']} data: #{data_to_send.inspect}")
62 
63         net_probe.discover(:nodes => [node['uid'].to_s])
64         net_probe.start_frame_listeners(:interfaces => data_to_send.to_json)
65       end
66     end
67 
68     def self.send_probing_frames(ctx, net_probe, nodes)
69       nodes.each do |node|
70         data_to_send = make_interfaces_to_send(node['networks'])
71 
72         Astute.logger.debug(
73           "#{ctx.task_id}: Network checker send: node: #{node['uid']} data: #{data_to_send.inspect}")
74 
75         net_probe.discover(:nodes => [node['uid'].to_s])
76         net_probe.send_probing_frames(:interfaces => data_to_send.to_json)
77       end
78     end
79 
80     def self.make_interfaces_to_send(networks)
81       data_to_send = {}
82       networks.each do |network|
83         data_to_send[network['iface']] = network['vlans'].join(",")
84       end
85 
86       data_to_send
87     end
88 
89     def self.format_result(stats)
90       uids = stats.map{|node| node.results[:sender]}.sort
91       stats.map do |node|
92         {
93           'uid' => node.results[:sender],
94           'networks' => check_vlans_by_traffic(
95             uids,
96             node.results[:data][:neighbours])
97         }
98       end
99     end
100 
101     def self.check_vlans_by_traffic(uids, data)
102       data.map do |iface, vlans|
103         {
104           'iface' => iface,
105           'vlans' => vlans.reject{ |k, v|
106             v.keys.sort != uids
107           }.keys.map(&:to_i)
108         }
109       end
110     end
111 
112   end
113 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-node_rb.html b/coverage/rcov/lib-astute-node_rb.html new file mode 100644 index 00000000..8e0a2df8 --- /dev/null +++ b/coverage/rcov/lib-astute-node_rb.html @@ -0,0 +1,338 @@ + + + + lib/astute/node.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/node.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/node.rb9344
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'active_support/core_ext/hash/indifferent_access'
18 require 'ostruct'
19 
20 module Astute
21   class Node < OpenStruct
22     def initialize(hash=nil)
23       if hash && (uid = hash['uid'])
24         hash = hash.dup
25         hash['uid'] = uid.to_s
26       else
27         raise TypeError.new("Invalid data: #{hash.inspect}")
28       end
29       super hash
30     end
31 
32     def [](key)
33       send key
34     end
35 
36     def []=(key, value)
37       send "#{key}=", value
38     end
39 
40     def uid
41       @table[:uid]
42     end
43 
44     def uid=(_)
45       raise TypeError.new('Read-only attribute')
46     end
47 
48     def to_hash
49       @table.with_indifferent_access
50     end
51   end
52 
53   class NodesHash < Hash
54     alias uids  keys
55     alias nodes values
56 
57     def self.build(nodes)
58       return nodes if nodes.kind_of? self
59       nodes.inject(self.new) do |hash, node|
60         hash << node
61         hash
62       end
63     end
64 
65     def <<(node)
66       node = normalize_value(node)
67       self[node.uid] = node
68       self
69     end
70 
71     def push(*nodes)
72       nodes.each{|node| self.<< node }
73       self
74     end
75 
76     def [](key)
77       super key.to_s
78     end
79 
80   private
81 
82     def []=(*args)
83       super
84     end
85 
86     def normalize_value(node)
87       if node.kind_of? Node
88         node
89       else
90         Node.new(node.to_hash)
91       end
92     end
93   end
94 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-nodes_remover_rb.html b/coverage/rcov/lib-astute-nodes_remover_rb.html new file mode 100644 index 00000000..ebc96406 --- /dev/null +++ b/coverage/rcov/lib-astute-nodes_remover_rb.html @@ -0,0 +1,374 @@ + + + + lib/astute/nodes_remover.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/nodes_remover.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/nodes_remover.rb10553
98.10%
+
+
+
+
96.23%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 module Astute
18   class NodesRemover
19 
20     def initialize(ctx, nodes)
21       @ctx = ctx
22       @nodes = NodesHash.build(nodes)
23     end
24 
25     def remove
26       # TODO(mihgen):  1. Nailgun should process node error message
27       #   2. Should we rename nodes -> removed_nodes array?
28       #   3. If exception is raised here, we should not fully fall into error, but only failed node
29       erased_nodes, error_nodes, inaccessible_nodes = remove_nodes(@nodes)
30       retry_remove_nodes(error_nodes, erased_nodes,
31                          Astute.config[:MC_RETRIES], Astute.config[:MC_RETRY_INTERVAL])
32 
33       retry_remove_nodes(inaccessible_nodes, erased_nodes,
34                          Astute.config[:MC_RETRIES], Astute.config[:MC_RETRY_INTERVAL])
35 
36       answer = {'nodes' => serialize_nodes(erased_nodes)}
37 
38       unless inaccessible_nodes.empty?
39         serialized_inaccessible_nodes = serialize_nodes(inaccessible_nodes)
40         answer.merge!({'inaccessible_nodes' => serialized_inaccessible_nodes})
41 
42         Astute.logger.warn "#{@ctx.task_id}: Removing of nodes #{@nodes.uids.inspect} finished " \
43                            "with errors. Nodes #{serialized_inaccessible_nodes.inspect} are inaccessible"
44       end
45 
46       unless error_nodes.empty?
47         serialized_error_nodes = serialize_nodes(error_nodes)
48         answer.merge!({'status' => 'error', 'error_nodes' => serialized_error_nodes})
49 
50         Astute.logger.error "#{@ctx.task_id}: Removing of nodes #{@nodes.uids.inspect} finished " \
51                             "with errors: #{serialized_error_nodes.inspect}"
52       end
53       Astute.logger.info "#{@ctx.task_id}: Finished removing of nodes: #{@nodes.uids.inspect}"
54 
55       answer
56     end
57 
58     private
59     def serialize_nodes(nodes)
60       nodes.nodes.map(&:to_hash)
61     end
62 
63     def remove_nodes(nodes)
64       if nodes.empty?
65         Astute.logger.info "#{@ctx.task_id}: Nodes to remove are not provided. Do nothing."
66         return Array.new(3){ NodesHash.new }
67       end
68       Astute.logger.info "#{@ctx.task_id}: Starting removing of nodes: #{nodes.uids.inspect}"
69       remover = MClient.new(@ctx, "erase_node", nodes.uids.sort, check_result=false)
70       responses = remover.erase_node(:reboot => true)
71       Astute.logger.debug "#{@ctx.task_id}: Data received from nodes: #{responses.inspect}"
72       inaccessible_uids = nodes.uids - responses.map{|response| response.results[:sender] }
73       inaccessible_nodes = NodesHash.build(inaccessible_uids.map do |uid|
74         {'uid' => uid, 'error' => 'Node not answered by RPC.'}
75       end)
76       error_nodes = NodesHash.new
77       erased_nodes = NodesHash.new
78       responses.each do |response|
79         node = Node.new('uid' => response.results[:sender])
80         if response.results[:statuscode] != 0
81           node['error'] = "RPC agent 'erase_node' failed. Result: #{response.results.inspect}"
82           error_nodes << node
83         elsif not response.results[:data][:rebooted]
84           node['error'] = "RPC method 'erase_node' failed with message: #{response.results[:data][:error_msg]}"
85           error_nodes << node
86         else
87           erased_nodes << node
88         end
89       end
90       [erased_nodes, error_nodes, inaccessible_nodes]
91     end
92 
93     def retry_remove_nodes(error_nodes, erased_nodes, retries=3, interval=1)
94       retries.times do
95         retried_erased_nodes = remove_nodes(error_nodes)[0]
96         retried_erased_nodes.each do |uid, node|
97           error_nodes.delete uid
98           erased_nodes << node
99         end
100         return if error_nodes.empty?
101         sleep(interval) if interval > 0
102       end
103     end
104 
105   end
106 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-orchestrator_rb.html b/coverage/rcov/lib-astute-orchestrator_rb.html new file mode 100644 index 00000000..7d802f24 --- /dev/null +++ b/coverage/rcov/lib-astute-orchestrator_rb.html @@ -0,0 +1,827 @@ + + + + lib/astute/orchestrator.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/orchestrator.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/orchestrator.rb256141
90.23%
+
+
+
+
82.27%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 module Astute
17   class Orchestrator
18     def initialize(deploy_engine=nil, log_parsing=false)
19       @deploy_engine = deploy_engine || Astute::DeploymentEngine::NailyFact
20       @log_parser = log_parsing ? LogParser::ParseDeployLogs.new : LogParser::NoParsing.new
21     end
22 
23     def node_type(reporter, task_id, nodes, timeout=nil)
24       context = Context.new(task_id, reporter)
25       uids = nodes.map {|n| n['uid']}
26       systemtype = MClient.new(context, "systemtype", uids, check_result=false, timeout)
27       systems = systemtype.get_type
28       systems.map do |n|
29         {
30           'uid'       => n.results[:sender],
31           'node_type' => n.results[:data][:node_type].chomp
32         }
33       end
34     end
35 
36     def deploy(up_reporter, task_id, nodes, attrs)
37       raise "Nodes to deploy are not provided!" if nodes.empty?
38       # Following line fixes issues with uids: it should always be string
39       nodes.map { |x| x['uid'] = x['uid'].to_s }  # NOTE: perform that on environment['nodes'] initialization
40       proxy_reporter = ProxyReporter.new(up_reporter)
41       context = Context.new(task_id, proxy_reporter, @log_parser)
42       deploy_engine_instance = @deploy_engine.new(context)
43       Astute.logger.info "Using #{deploy_engine_instance.class} for deployment."
44       begin
45         @log_parser.prepare(nodes)
46       rescue Exception => e
47         Astute.logger.warn "Some error occurred when prepare LogParser: #{e.message}, trace: #{e.backtrace.inspect}"
48       end
49       deploy_engine_instance.deploy(nodes, attrs)
50       return SUCCESS
51     end
52     
53     def fast_provision(reporter, engine_attrs, nodes)
54       raise "Nodes to provision are not provided!" if nodes.empty?
55 
56       engine = create_engine(engine_attrs, reporter)
57       
58       begin
59         reboot_events = reboot_nodes(engine, nodes)
60         failed_nodes  = check_reboot_nodes(engine, reboot_events)
61         
62       rescue RuntimeError => e
63         Astute.logger.error("Error occured while provisioning: #{e.inspect}")
64         reporter.report({
65                           'status' => 'error',
66                           'error' => 'Cobbler error',
67                           'progress' => 100
68                         })
69         raise StopIteration
70       ensure
71         engine.sync
72       end
73       
74       if failed_nodes.empty?
75         report_result({}, reporter)
76         return SUCCESS
77       else
78         Astute.logger.error("Nodes failed to reboot: #{failed_nodes.inspect}")
79         reporter.report({
80                           'status' => 'error',
81                           'error' => "Nodes failed to reboot: #{failed_nodes.inspect}",
82                           'progress' => 100
83                         })
84         raise StopIteration
85       end
86     end
87     
88     def provision(reporter, task_id, nodes)
89       raise "Nodes to provision are not provided!" if nodes.empty?
90       
91       # Following line fixes issues with uids: it should always be string
92       nodes.map { |x| x['uid'] = x['uid'].to_s } # NOTE: perform that on environment['nodes'] initialization
93 
94       nodes_uids = nodes.map { |n| n['uid'] }
95       
96       provisionLogParser = LogParser::ParseProvisionLogs.new
97       proxy_reporter = ProxyReporter.new(reporter)
98       sleep_not_greater_than(10) do # Wait while nodes going to reboot
99         Astute.logger.info "Starting OS provisioning for nodes: #{nodes_uids.join(',')}"
100         begin
101           provisionLogParser.prepare(nodes)
102         rescue => e
103           Astute.logger.warn "Some error occurred when prepare LogParser: #{e.message}, trace: #{e.backtrace.inspect}"
104         end
105       end
106       nodes_not_booted = nodes_uids.clone
107       begin
108         Timeout.timeout(Astute.config.PROVISIONING_TIMEOUT) do  # Timeout for booting target OS
109           catch :done do
110             while true
111               sleep_not_greater_than(5) do 
112                 types = node_type(proxy_reporter, task_id, nodes, 2)
113                 types.each { |t| Astute.logger.debug("Got node types: uid=#{t['uid']} type=#{t['node_type']}") }
114           
115                 Astute.logger.debug("Not target nodes will be rejected")
116                 target_uids = types.reject{|n| n['node_type'] != 'target'}.map{|n| n['uid']}
117                 nodes_not_booted -= types.map { |n| n['uid'] }
118                 Astute.logger.debug "Not provisioned: #{nodes_not_booted.join(',')}, got target OSes: #{target_uids.join(',')}"
119           
120                 if nodes.length == target_uids.length
121                   Astute.logger.info "All nodes #{target_uids.join(',')} are provisioned."
122                   throw :done
123                 else
124                   Astute.logger.debug("Nodes list length is not equal to target nodes list length: #{nodes.length} != #{target_uids.length}")
125                 end
126 
127                 report_about_progress(proxy_reporter, provisionLogParser, nodes_uids, target_uids, nodes)     
128               end
129             end
130           end
131           # We are here if jumped by throw from while cycle 
132         end
133       rescue Timeout::Error
134         msg = "Timeout of provisioning is exceeded."
135         Astute.logger.error msg
136         error_nodes = nodes_not_booted.map { |n| {'uid' => n,
137                                                   'status' => 'error',
138                                                   'error_msg' => msg,
139                                                   'progress' => 100,
140                                                   'error_type' => 'provision'} }
141         proxy_reporter.report({'status' => 'error', 'error' => msg, 'nodes' => error_nodes})
142         return FAIL
143       end
144 
145       nodes_progress = nodes.map do |n|
146         {'uid' => n['uid'], 'progress' => 100, 'status' => 'provisioned'}
147       end
148       proxy_reporter.report({'nodes' => nodes_progress})
149       return SUCCESS
150     end
151     
152 
153     def remove_nodes(reporter, task_id, nodes)
154       NodesRemover.new(Context.new(task_id, reporter), nodes).remove
155     end
156 
157     def verify_networks(reporter, task_id, nodes)
158       Network.check_network(Context.new(task_id, reporter), nodes)
159     end
160     
161     private
162     
163     def report_result(result, reporter)
164       default_result = {'status' => 'ready', 'progress' => 100}
165       
166       result = {} unless result.instance_of?(Hash)
167       status = default_result.merge(result)
168       reporter.report(status)
169     end
170     
171     def sleep_not_greater_than(sleep_time, &block)
172       time = Time.now.to_f
173       block.call
174       time = time + sleep_time - Time.now.to_f
175       sleep (time) if time > 0
176     end
177     
178     def create_engine(engine_attrs, reporter)
179       begin
180         Astute.logger.info("Trying to instantiate cobbler engine: #{engine_attrs.inspect}")
181         Astute::Provision::Cobbler.new(engine_attrs)
182       rescue
183         Astute.logger.error("Error occured during cobbler initializing")
184         
185         reporter.report({
186                           'status' => 'error',
187                           'error' => 'Cobbler can not be initialized',
188                           'progress' => 100
189                         })
190         raise StopIteration
191       end
192     end
193     
194     def reboot_nodes(engine, nodes)
195       reboot_events = {}
196       nodes.each do |node|
197         begin
198           Astute.logger.info("Adding #{node['name']} into cobbler")
199           engine.item_from_hash('system', node['name'], node,
200                            :item_preremove => true)
201         rescue RuntimeError => e
202           Astute.logger.error("Error occured while adding system #{node['name']} to cobbler")
203           raise e
204         end
205         Astute.logger.debug("Trying to reboot node: #{node['name']}")
206         reboot_events[node['name']] = engine.power_reboot(node['name'])
207       end
208       reboot_events
209     end
210     
211     def check_reboot_nodes(engine, reboot_events)
212       begin
213         Astute.logger.debug("Waiting for reboot to be complete: nodes: #{reboot_events.keys}")
214         failed_nodes = []
215         Timeout::timeout(Astute.config.REBOOT_TIMEOUT) do
216           while not reboot_events.empty?
217             reboot_events.each do |node_name, event_id|
218               event_status = engine.event_status(event_id)
219               Astute.logger.debug("Reboot task status: node: #{node_name} status: #{event_status}")
220               if event_status[2] =~ /^failed$/
221                 Astute.logger.error("Error occured while trying to reboot: #{node_name}")
222                 reboot_events.delete(node_name)
223                 failed_nodes << node_name
224               elsif event_status[2] =~ /^complete$/
225                 Astute.logger.debug("Successfully rebooted: #{node_name}")
226                 reboot_events.delete(node_name)
227               end
228             end
229             sleep(5)
230           end
231         end
232       rescue Timeout::Error => e
233         Astute.logger.debug("Reboot timeout: reboot tasks not completed for nodes #{reboot_events.keys}")
234         raise e
235       end
236       failed_nodes
237     end
238     
239     def report_about_progress(reporter, provisionLogParser, nodes_uids, target_uids, nodes)
240       begin
241         nodes_progress = provisionLogParser.progress_calculate(nodes_uids, nodes)
242         nodes_progress.each do |n|
243           if target_uids.include?(n['uid'])
244             n['progress'] = 100
245             n['status']   = 'provisioned'
246           else
247             n['status']   = 'provisioning'
248           end
249         end
250         reporter.report({'nodes' => nodes_progress})
251       rescue => e
252         Astute.logger.warn "Some error occurred when parse logs for nodes progress: #{e.message}, trace: #{e.backtrace.inspect}"
253       end
254     end
255     
256   end
257 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-puppetd_rb.html b/coverage/rcov/lib-astute-puppetd_rb.html new file mode 100644 index 00000000..724284e4 --- /dev/null +++ b/coverage/rcov/lib-astute-puppetd_rb.html @@ -0,0 +1,569 @@ + + + + lib/astute/puppetd.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/puppetd.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/puppetd.rb17086
95.88%
+
+
+
+
91.86%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'json'
18 require 'timeout'
19 
20 module Astute
21   module PuppetdDeployer
22     # As I (Andrey Danin) understand, Puppet agent goes through these steps:
23     #   * Puppetd has 'stopped' state.
24     #   * We run it as a run_once, and puppetd goes to 'idling' state - it trying to
25     #       retrieve catalog.
26     #   * If it can't retrieve catalog, it goes back to 'stopped' state without
27     #       any update of last_run_summary file.
28     #   * If puppetd retrieve catalog, it goes to 'running' state, which means
29     #       it appying catalog to system.
30     #   * When puppetd finished catalog run, it updates last_run_summary file
31     #       but stays in 'running' state for a while.
32     #   * After puppetd finished all internal jobs connected with finished catalog,
33     #       it goes to 'idling' state.
34     #   * After a short time it goes to 'stopped' state because we ran it as a run_once.
35 
36     private
37     # Runs puppetd.runonce only if puppet is stopped on the host at the time
38     # If it isn't stopped, we wait a bit and try again.
39     # Returns list of nodes uids which appear to be with hung puppet.
40     def self.puppetd_runonce(puppetd, uids)
41       started = Time.now.to_i
42       while Time.now.to_i - started < Astute.config.PUPPET_FADE_TIMEOUT
43         puppetd.discover(:nodes => uids)
44         last_run = puppetd.last_run_summary
45         running_uids = last_run.select {|x| x.results[:data][:status] != 'stopped'}.map {|n| n.results[:sender]}
46         stopped_uids = uids - running_uids
47         if stopped_uids.any?
48           puppetd.discover(:nodes => stopped_uids)
49           puppetd.runonce
50         end
51         uids = running_uids
52         break if uids.empty?
53         sleep Astute.config.PUPPET_FADE_INTERVAL
54       end
55       Astute.logger.debug "puppetd_runonce completed within #{Time.now.to_i - started} seconds."
56       Astute.logger.debug "Following nodes have puppet hung: '#{running_uids.join(',')}'" if running_uids.any?
57       running_uids
58     end
59 
60     def self.calc_nodes_status(last_run, prev_run)
61       # Finished are those which are not in running state,
62       #   and changed their last_run time, which is changed after application of catalog,
63       #   at the time of updating last_run_summary file. At that particular time puppet is
64       #   still running, and will finish in a couple of seconds.
65       # If Puppet had crashed before it got a catalog (e.g. certificate problems),
66       #   it didn't update last_run_summary file and switched to 'stopped' state.
67 
68       stopped = last_run.select {|x| x.results[:data][:status] == 'stopped'}
69 
70       # Select all finished nodes which not failed and changed last_run time.
71       succeed_nodes = stopped.select { |n|
72         prev_n = prev_run.find{|ps| ps.results[:sender] == n.results[:sender] }
73         n.results[:data][:resources]['failed'].to_i == 0 &&
74           n.results[:data][:resources]['failed_to_restart'].to_i == 0 &&
75           n.results[:data][:time]['last_run'] != (prev_n && prev_n.results[:data][:time]['last_run'])
76       }.map{|x| x.results[:sender] }
77 
78       stopped_nodes = stopped.map {|x| x.results[:sender]}
79       error_nodes = stopped_nodes - succeed_nodes
80 
81       # Running are all which didn't appear in finished
82       running_nodes = last_run.map {|n| n.results[:sender]} - stopped_nodes
83 
84       nodes_to_check = running_nodes + succeed_nodes + error_nodes
85       unless nodes_to_check.size == last_run.size
86         raise "Shoud never happen. Internal error in nodes statuses calculation. Statuses calculated for: #{nodes_to_check.inspect},"
87                     "nodes passed to check statuses of: #{last_run.map {|n| n.results[:sender]}}"
88       end
89       {'succeed' => succeed_nodes, 'error' => error_nodes, 'running' => running_nodes}
90     end
91 
92     public
93     def self.deploy(ctx, nodes, retries=2, change_node_status=true)
94       # TODO: can we hide retries, ignore_failure into @ctx ?
95       uids = nodes.map {|n| n['uid']}
96       # Keep info about retries for each node
97       node_retries = {}
98       uids.each {|x| node_retries.merge!({x => retries}) }
99       Astute.logger.debug "Waiting for puppet to finish deployment on all nodes (timeout = #{Astute.config.PUPPET_TIMEOUT} sec)..."
100       time_before = Time.now
101       Timeout::timeout(Astute.config.PUPPET_TIMEOUT) do
102         puppetd = MClient.new(ctx, "puppetd", uids)
103         puppetd.on_respond_timeout do |uids|
104           ctx.reporter.report('nodes' => uids.map{|uid| {'uid' => uid, 'status' => 'error', 'error_type' => 'deploy'}})
105         end if change_node_status
106         prev_summary = puppetd.last_run_summary
107         puppetd_runonce(puppetd, uids)
108         nodes_to_check = uids
109         last_run = puppetd.last_run_summary
110         while nodes_to_check.any?
111           calc_nodes = calc_nodes_status(last_run, prev_summary)
112           Astute.logger.debug "Nodes statuses: #{calc_nodes.inspect}"
113 
114           # At least we will report about successfully deployed nodes
115           nodes_to_report = []
116           nodes_to_report.concat(calc_nodes['succeed'].map { |n| {'uid' => n, 'status' => 'ready'} }) if change_node_status
117 
118           # Process retries
119           nodes_to_retry = []
120           calc_nodes['error'].each do |uid|
121             if node_retries[uid] > 0
122               node_retries[uid] -= 1
123               Astute.logger.debug "Puppet on node #{uid.inspect} will be restarted. "\
124                                   "#{node_retries[uid]} retries remained."
125               nodes_to_retry << uid
126             else
127               Astute.logger.debug "Node #{uid.inspect} has failed to deploy. There is no more retries for puppet run."
128               nodes_to_report << {'uid' => uid, 'status' => 'error', 'error_type' => 'deploy'} if change_node_status
129             end
130           end
131           if nodes_to_retry.any?
132             Astute.logger.info "Retrying to run puppet for following error nodes: #{nodes_to_retry.join(',')}"
133             puppetd_runonce(puppetd, nodes_to_retry)
134             # We need this magic with prev_summary to reflect new puppetd run statuses..
135             prev_summary.delete_if { |x| nodes_to_retry.include?(x.results[:sender]) }
136             prev_summary += last_run.select { |x| nodes_to_retry.include?(x.results[:sender]) }
137           end
138           # /end of processing retries
139 
140           if calc_nodes['running'].any?
141             begin
142               # Pass nodes because logs calculation needs IP address of node, not just uid
143               nodes_progress = ctx.deploy_log_parser.progress_calculate(calc_nodes['running'], nodes)
144               if nodes_progress.any?
145                 Astute.logger.debug "Got progress for nodes: #{nodes_progress.inspect}"
146                 # Nodes with progress are running, so they are not included in nodes_to_report yet
147                 nodes_progress.map! {|x| x.merge!({'status' => 'deploying'})}
148                 nodes_to_report += nodes_progress
149               end
150             rescue Exception => e
151               Astute.logger.warn "Some error occurred when parse logs for nodes progress: #{e.message}, "\
152                                  "trace: #{e.backtrace.inspect}"
153             end
154           end
155           ctx.reporter.report('nodes' => nodes_to_report) if nodes_to_report.any?
156 
157           # we will iterate only over running nodes and those that we restart deployment for
158           nodes_to_check = calc_nodes['running'] + nodes_to_retry
159           break if nodes_to_check.empty?
160 
161           sleep Astute.config.PUPPET_DEPLOY_INTERVAL
162           puppetd.discover(:nodes => nodes_to_check)
163           last_run = puppetd.last_run_summary
164         end
165       end
166       time_spent = Time.now - time_before
167       Astute.logger.info "#{ctx.task_id}: Spent #{time_spent} seconds on puppet run "\
168                          "for following nodes(uids): #{nodes.map {|n| n['uid']}.join(',')}"
169     end
170   end
171 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-reporter_rb.html b/coverage/rcov/lib-astute-reporter_rb.html new file mode 100644 index 00000000..df4ab9f3 --- /dev/null +++ b/coverage/rcov/lib-astute-reporter_rb.html @@ -0,0 +1,431 @@ + + + + lib/astute/reporter.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/reporter.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/reporter.rb12457
99.19%
+
+
+
+
98.25%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'set'
18 
19 STATES = {
20   'offline'      => 0,
21   'discover'     => 10,
22   'provisioning' => 30,
23   'provisioned'  => 40,
24   'deploying'    => 50,
25   'ready'        => 60,
26   'error'        => 70
27 }
28 
29 module Astute
30   class ProxyReporter
31     def initialize(up_reporter)
32       @up_reporter = up_reporter
33       @nodes = []
34     end
35 
36     def report(data)
37       nodes_to_report = []
38       nodes = (data['nodes'] or [])
39       nodes.each do |node|
40         node = node_validate(node)
41         nodes_to_report << node if node
42       end
43       # Let's report only if nodes updated
44       if nodes_to_report.any?
45         data['nodes'] = nodes_to_report
46         @up_reporter.report(data)
47         # Update nodes attributes in @nodes.
48         nodes_to_report.each do |node|
49           saved_node = @nodes.select {|x| x['uid'] == node['uid']}.first  # NOTE: use nodes hash
50           if saved_node
51             node.each {|k, v| saved_node[k] = v}
52           else
53             @nodes << node
54           end
55         end
56       end
57     end
58 
59   private
60 
61     def node_validate(node)
62       # Validate basic correctness of attributes.
63       err = []
64       if node['status'].nil?
65         err << "progress value provided, but no status" unless node['progress'].nil?
66       else
67         err << "Status provided #{node['status']} is not supported" if STATES[node['status']].nil?
68       end
69       unless node['uid']
70         err << "Node uid is not provided"
71       end
72       if err.any?
73         msg = "Validation of node: #{node.inspect} for report failed: #{err.join('; ')}."
74         Astute.logger.error(msg)
75         raise msg
76       end
77 
78       # Validate progress field.
79       unless node['progress'].nil?
80         if node['progress'] > 100
81           Astute.logger.warn("Passed report for node with progress > 100: "\
82                               "#{node.inspect}. Adjusting progress to 100.")
83           node['progress'] = 100
84         end
85         if node['progress'] < 0
86           Astute.logger.warn("Passed report for node with progress < 0: "\
87                               "#{node.inspect}. Adjusting progress to 0.")
88           node['progress'] = 0
89         end
90       end
91       if not node['status'].nil? and ['provisioned', 'ready'].include?(node['status']) and node['progress'] != 100
92         Astute.logger.warn("In #{node['status']} state node should have progress 100, "\
93                             "but node passed: #{node.inspect}. Setting it to 100")
94         node['progress'] = 100
95       end
96 
97       # Comparison with previous state.
98       saved_node = @nodes.select {|x| x['uid'] == node['uid']}.first
99       unless saved_node.nil?
100         saved_status = (STATES[saved_node['status']] or 0)
101         node_status = (STATES[node['status']] or saved_status)
102         saved_progress = (saved_node['progress'] or 0)
103         node_progress = (node['progress'] or saved_progress)
104 
105         if node_status < saved_status
106           Astute.logger.warn("Attempt to assign lower status detected: "\
107                              "Status was: #{saved_status}, attempted to "\
108                              "assign: #{node_status}. Skipping this node (id=#{node['uid']})")
109           return
110         end
111         if node_progress < saved_progress and node_status == saved_status
112           Astute.logger.warn("Attempt to assign lesser progress detected: "\
113                              "Progress was: #{saved_progress}, attempted to "\
114                              "assign: #{node_progress}. Skipping this node (id=#{node['uid']})")
115           return
116         end
117 
118         # We need to update node here only if progress is greater, or status changed
119         return if node.select{|k, v| not saved_node[k].eql?(v)}.empty?
120       end
121 
122       node
123     end
124   end
125 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-rpuppet_rb.html b/coverage/rcov/lib-astute-rpuppet_rb.html new file mode 100644 index 00000000..1f1ee435 --- /dev/null +++ b/coverage/rcov/lib-astute-rpuppet_rb.html @@ -0,0 +1,185 @@ + + + + lib/astute/rpuppet.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/rpuppet.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/rpuppet.rb4217
71.43%
+
+
+
+
29.41%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require 'json'
18 require 'timeout'
19 
20 module Astute
21   module RpuppetDeployer
22     def self.rpuppet_deploy(ctx, nodes, parameters, classes, env="production")
23       if nodes.empty?
24         Astute.logger.info "#{ctx.task_id}: Nodes to deploy are not provided. Do nothing."
25         return false
26       end
27       uids = nodes.map {|n| n['uid']}
28       data = {
29         "parameters" => parameters,
30         "classes" => classes,
31         "environment" => env
32       }
33       Astute.logger.debug "Waiting for puppet to finish deployment on all nodes (timeout = #{Astute.config.PUPPET_TIMEOUT} sec)..."
34       time_before = Time.now
35       Timeout::timeout(Astute.config.PUPPET_TIMEOUT) do
36         rpuppet = MClient.new(ctx, "rpuppet", uids)
37         rpuppet.run(:data => data.to_json)
38       end
39       time_spent = Time.now - time_before
40       Astute.logger.info "#{ctx.task_id}: Spent #{time_spent} seconds on puppet run for following nodes(uids): #{nodes.map {|n| n['uid']}.join(',')}"
41     end
42   end
43 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute-ruby_removed_functions_rb.html b/coverage/rcov/lib-astute-ruby_removed_functions_rb.html new file mode 100644 index 00000000..7a7167e4 --- /dev/null +++ b/coverage/rcov/lib-astute-ruby_removed_functions_rb.html @@ -0,0 +1,128 @@ + + + + lib/astute/ruby_removed_functions.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute/ruby_removed_functions.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute/ruby_removed_functions.rb236
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 class Date
18   def self.day_fraction_to_time(fr)
19     ss,  fr = fr.divmod(Rational(1, 86400))
20     h,   ss = ss.divmod(3600)
21     min, s  = ss.divmod(60)
22     return h, min, s, fr
23   end
24 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/lib-astute_rb.html b/coverage/rcov/lib-astute_rb.html new file mode 100644 index 00000000..a5bfa711 --- /dev/null +++ b/coverage/rcov/lib-astute_rb.html @@ -0,0 +1,248 @@ + + + + lib/astute.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

lib/astute.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
lib/astute.rb6337
93.65%
+
+
+
+
89.19%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 require 'astute/ruby_removed_functions'
17 
18 require 'json'
19 require 'logger'
20 
21 require 'astute/config'
22 require 'astute/logparser'
23 require 'astute/orchestrator'
24 require 'astute/metadata'
25 require 'astute/deployment_engine'
26 require 'astute/network'
27 require 'astute/puppetd'
28 require 'astute/rpuppet'
29 require 'astute/deployment_engine/simple_puppet'
30 require 'astute/deployment_engine/nailyfact'
31 require 'astute/cobbler'
32 
33 module Astute
34   autoload 'Context', 'astute/context'
35   autoload 'MClient', 'astute/mclient'
36   autoload 'ProxyReporter', 'astute/reporter'
37   autoload 'NodesRemover', 'astute/nodes_remover'
38   autoload 'Node', 'astute/node'
39   autoload 'NodesHash', 'astute/node'
40   LogParser.autoload :ParseDeployLogs, 'astute/logparser/deployment'
41   LogParser.autoload :ParseProvisionLogs, 'astute/logparser/provision'
42   LogParser.autoload :Patterns, 'astute/logparser/parser_patterns'
43   
44   SUCCESS = 0
45   FAIL = 1
46 
47   def self.logger
48     unless @logger
49       @logger = Logger.new('/var/log/astute.log')
50       @logger.formatter = proc do |severity, datetime, progname, msg|
51         severity_map = {'DEBUG' => 'debug', 'INFO' => 'info', 'WARN' => 'warning', 'ERROR' => 'err', 'FATAL' => 'crit'}
52         "#{datetime.strftime("%Y-%m-%dT%H:%M:%S")} #{severity_map[severity]}: [#{Process.pid}] #{msg}\n"
53       end
54     end
55     @logger
56   end
57 
58   def self.logger=(logger)
59     @logger = logger
60   end
61 
62   config_file = '/opt/astute/astute.conf'
63   Astute.config.update(YAML.load(File.read(config_file))) if File.exists?(config_file)
64 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-logparser_spec_rb.html b/coverage/rcov/spec-unit-logparser_spec_rb.html new file mode 100644 index 00000000..3502433d --- /dev/null +++ b/coverage/rcov/spec-unit-logparser_spec_rb.html @@ -0,0 +1,893 @@ + + + + spec/unit/logparser_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/logparser_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/logparser_spec.rb278153
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 include Astute
20 
21 describe LogParser do
22   def get_statistics_variables(progress_table)
23     # Calculate some statistics variables: expectancy, standart deviation and
24     # correlation coefficient between real and ideal progress calculation.
25     total_time = 0
26     real_expectancy = 0
27     real_sqr_expectancy = 0
28     prev_event_date = nil
29     progress_table.each do |el|
30       date = el[:date]
31       prev_event_date = date unless prev_event_date
32       progress = el[:progress].to_f
33       period = date - prev_event_date
34       hours, mins, secs, frac = Date::day_fraction_to_time(period)
35       period_in_sec = hours * 60 * 60 + mins * 60 + secs
36       total_time += period_in_sec
37       real_expectancy += period_in_sec * progress
38       real_sqr_expectancy += period_in_sec * progress ** 2
39       el[:time_delta] = period_in_sec
40       prev_event_date = date
41     end
42 
43     # Calculate standart deviation for real progress distibution.
44     real_expectancy = real_expectancy.to_f / total_time
45     real_sqr_expectancy = real_sqr_expectancy.to_f / total_time
46     real_standart_deviation = Math.sqrt(real_sqr_expectancy - real_expectancy ** 2)
47 
48     # Calculate PCC (correlation coefficient).
49     ideal_sqr_expectancy = 0
50     ideal_expectancy = 0
51     t = 0
52     ideal_delta = 100.0 / total_time
53     mixed_expectancy = 0
54     progress_table.each do |el|
55       t += el[:time_delta]
56       ideal_progress = t * ideal_delta
57       ideal_expectancy += ideal_progress * el[:time_delta]
58       ideal_sqr_expectancy += ideal_progress ** 2 * el[:time_delta]
59       el[:ideal_progress] = ideal_progress
60       mixed_expectancy += el[:progress] * ideal_progress * el[:time_delta]
61     end
62 
63     ideal_expectancy = ideal_expectancy / total_time
64     ideal_sqr_expectancy = ideal_sqr_expectancy / total_time
65     mixed_expectancy = mixed_expectancy / total_time
66     ideal_standart_deviation = Math.sqrt(ideal_sqr_expectancy - ideal_expectancy ** 2)
67     covariance = mixed_expectancy - ideal_expectancy * real_expectancy
68     pcc = covariance / (ideal_standart_deviation * real_standart_deviation)
69 
70     statistics = {
71       'real_expectancy' => real_expectancy,
72       'real_sqr_expectancy' => real_sqr_expectancy,
73       'real_standart_deviation' => real_standart_deviation,
74       'ideal_expectancy' => ideal_expectancy,
75       'ideal_sqr_expectancy' => ideal_sqr_expectancy,
76       'ideal_standart_deviation' => ideal_standart_deviation,
77       'mixed_expectancy' => mixed_expectancy,
78       'covariance' => covariance,
79       'pcc' => pcc,
80       'total_time' => total_time,
81     }
82 
83     return statistics
84   end
85 
86   def get_next_line(fo, date_regexp, date_format)
87     until fo.eof?
88       line = fo.readline
89       date_string = line.match(date_regexp)
90       if date_string
91         date = DateTime.strptime(date_string[0], date_format)
92         return line, date
93       end
94     end
95   end
96 
97   def get_next_lines_by_date(fo, now, date_regexp, date_format)
98     lines = ''
99     until fo.eof?
100       pos = fo.pos
101       line, date = get_next_line(fo, date_regexp, date_format)
102       if date <= now
103         lines += line
104       else
105         fo.pos = pos
106         return lines
107       end
108     end
109     return lines
110   end
111 
112   context "Correlation coeff. (PCC) of Provisioning progress bar calculation" do
113     def provision_parser_wrapper(node)
114       uids = [node['uid']]
115       nodes = [node]
116       time_delta = 5.0/24/60/60
117       log_delay = 6*time_delta
118 
119       deploy_parser = Astute::LogParser::ParseProvisionLogs.new
120       pattern_spec = deploy_parser.pattern_spec
121       date_regexp = '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}'
122       date_format = '%Y-%m-%dT%H:%M:%S'
123 
124       Dir.mktmpdir do |dir|
125         # Create temp log files and structures.
126         pattern_spec['path_prefix'] = "#{dir}/"
127         path = "#{pattern_spec['path_prefix']}#{node['fqdn']}/#{pattern_spec['filename']}"
128         Dir.mkdir(File.dirname(File.dirname(path)))
129         Dir.mkdir(File.dirname(path))
130         node['file'] = File.open(path, 'w')
131         src_filename = File.join(File.dirname(__FILE__), "..", "example-logs", node['src_filename'])
132         node['src'] = File.open(src_filename)
133         line, date = get_next_line(node['src'], date_regexp, date_format)
134         node['src'].pos = 0
135         node['now'] = date - log_delay
136         node['progress_table'] ||= []
137 
138         # End 'while' cycle if reach EOF at all src files.
139         until node['src'].eof?
140           # Copy logs line by line from example logfile to tempfile and collect progress for each step.
141           lines, date = get_next_lines_by_date(node['src'], node['now'], date_regexp, date_format)
142           node['file'].write(lines)
143           node['file'].flush
144           node['last_lines'] = lines
145 
146           DateTime.stubs(:now).returns(node['now'])
147           node_progress = deploy_parser.progress_calculate(uids, nodes)[0]
148           node['progress_table'] << {:date => node['now'], :progress => node_progress['progress']}
149           node['now'] += time_delta
150         end
151 
152         nodes.each do |node|
153           node['statistics'] = get_statistics_variables(node['progress_table'])
154         end
155         # Clear temp files.
156         node['file'].close
157         File.unlink(node['file'].path)
158         Dir.unlink(File.dirname(node['file'].path))
159       end
160 
161       return node
162     end
163 
164     it "should be greather than 0.96" do
165       node = {'uid' => '1', 'ip' => '1.0.0.1', 'fqdn' => 'slave-1.domain.tld', 'role' => 'controller', 'src_filename' => 'anaconda.log_',
166         'meta' => { 'disks' =>
167           [
168           {'name' => 'flash drive', 'removable' => true, 'size' => 1000},
169           {'name' => 'sda', 'removable'=> false, 'size' => 32*1000*1000*1000},
170           ]
171         }
172       }
173       calculated_node = provision_parser_wrapper(node)
174       calculated_node['statistics']['pcc'].should > 0.96
175     end
176 
177     it "it must be updated at least 5 times" do
178       # Otherwise progress bar has no meaning I guess...
179       pending('Not yet implemented')
180     end
181 
182   end
183   context "Correlation coeff. (PCC) of Deploying progress bar calculation" do
184     def deployment_parser_wrapper(cluster_type, nodes)
185       uids = nodes.map{|n| n['uid']}
186 
187       deploy_parser = Astute::LogParser::ParseDeployLogs.new(cluster_type)
188       pattern_spec = deploy_parser.pattern_spec
189       date_regexp = '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}'
190       date_format = '%Y-%m-%dT%H:%M:%S'
191 
192       Dir.mktmpdir do |dir|
193         # Create temp log files and structures.
194         pattern_spec['path_prefix'] = "#{dir}/"
195         nodes.each do |node|
196           path = "#{pattern_spec['path_prefix']}#{node['fqdn']}/#{pattern_spec['filename']}"
197           Dir.mkdir(File.dirname(path))
198           node['file'] = File.open(path, 'w')
199           src_filename = File.join(File.dirname(__FILE__), "..", "example-logs", node['src_filename'])
200           node['src'] = File.open(src_filename)
201           node['progress_table'] ||= []
202         end
203 
204         # End 'while' cycle if reach EOF at all src files.
205         while nodes.index{|n| not n['src'].eof?}
206           # Copy logs line by line from example logfile to tempfile and collect progress for each step.
207           nodes.each do |node|
208             unless node['src'].eof?
209               line = node['src'].readline
210               node['file'].write(line)
211               node['file'].flush
212               node['last_line'] = line
213             else
214               node['last_line'] = ''
215             end
216           end
217 
218           nodes_progress = deploy_parser.progress_calculate(uids, nodes)
219           nodes_progress.each do |progress|
220             node = nodes.at(nodes.index{|n| n['uid'] == progress['uid']})
221             date_string = node['last_line'].match(date_regexp)
222             if date_string
223               date = DateTime.strptime(date_string[0], date_format)
224               node['progress_table'] << {:date => date, :progress => progress['progress']}
225             end
226           end
227         end
228 
229         nodes.each do |node|
230           node['statistics'] = get_statistics_variables(node['progress_table'])
231         end
232         # Clear temp files.
233         nodes.each do |n|
234           n['file'].close
235           File.unlink(n['file'].path)
236           Dir.unlink(File.dirname(n['file'].path))
237         end
238       end
239 
240       return nodes
241     end
242 
243     it "should be greather than 0.85 for HA deployment" do
244       nodes = [
245         {'uid' => '1', 'ip' => '1.0.0.1', 'fqdn' => 'slave-1.domain.tld', 'role' => 'controller', 'src_filename' => 'puppet-agent.log.ha.contr.2'},
246         {'uid' => '2', 'ip' => '1.0.0.2', 'fqdn' => 'slave-2.domain.tld', 'role' => 'compute', 'src_filename' => 'puppet-agent.log.ha.compute'},
247       ]
248 
249       calculated_nodes = deployment_parser_wrapper('ha', nodes)
250       calculated_nodes.each {|node| node['statistics']['pcc'].should > 0.85}
251 
252       # For debug purposes.
253       # print "\n"
254       # calculated_nodes.each do |node|
255       #   print node['statistics'].inspect, "\n", node['statistics']['pcc'], "\n", node['progress_table'][-1][:progress], "\n"
256       # end
257     end
258 
259     it "should be greather than 0.97 for singlenode deployment" do
260       nodes = [
261         {'uid' => '1', 'ip' => '1.0.0.1', 'fqdn' => 'slave-1.domain.tld', 'role' => 'controller', 'src_filename' => 'puppet-agent.log.singlenode'},
262       ]
263 
264       calculated_nodes = deployment_parser_wrapper('singlenode', nodes)
265       calculated_nodes.each {|node| node['statistics']['pcc'].should > 0.97}
266     end
267 
268     it "should be greather than 0.94 for multinode deployment" do
269       nodes = [
270         {'uid' => '1', 'ip' => '1.0.0.1', 'fqdn' => 'slave-1.domain.tld', 'role' => 'controller', 'src_filename' => 'puppet-agent.log.multi.contr'},
271         {'uid' => '2', 'ip' => '1.0.0.2', 'fqdn' => 'slave-2.domain.tld', 'role' => 'compute', 'src_filename' => 'puppet-agent.log.multi.compute'},
272       ]
273 
274       calculated_nodes = deployment_parser_wrapper('multinode', nodes)
275       calculated_nodes.each {|node| node['statistics']['pcc'].should > 0.94}
276     end
277 
278   end
279 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-mclient_spec_rb.html b/coverage/rcov/spec-unit-mclient_spec_rb.html new file mode 100644 index 00000000..575c1812 --- /dev/null +++ b/coverage/rcov/spec-unit-mclient_spec_rb.html @@ -0,0 +1,344 @@ + + + + spec/unit/mclient_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/mclient_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/mclient_spec.rb9551
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 include Astute
20 
21 describe MClient do
22   include SpecHelpers
23 
24   before(:each) do
25     @ctx = mock('context')
26     @ctx.stubs(:task_id)
27     @ctx.stubs(:reporter)
28   end
29 
30   it "should receive method call and process valid result correctly" do
31     nodes = [{'uid' => 1}, {'uid' => 2}, {'uid' => 3}]
32     rpcclient = mock_rpcclient(nodes)
33     mc_valid_result = mock_mc_result
34 
35     rpcclient.expects(:echo).with(:msg => 'hello world').once.returns([mc_valid_result]*3)
36 
37     mclient = MClient.new(@ctx, "faketest", nodes.map {|x| x['uid']})
38     stats = mclient.echo(:msg => 'hello world')
39     stats.should eql([mc_valid_result]*3)
40   end
41 
42   it "should return even bad result if check_result=false" do
43     nodes = [{'uid' => 1}, {'uid' => 2}, {'uid' => 3}]
44     rpcclient = mock_rpcclient(nodes)
45     mc_valid_result = mock_mc_result
46     mc_error_result = mock_mc_result({:statuscode => 1, :sender => '2'})
47 
48     rpcclient.expects(:echo).with(:msg => 'hello world').once.\
49         returns([mc_valid_result, mc_error_result])
50 
51     mclient = MClient.new(@ctx, "faketest", nodes.map {|x| x['uid']}, check_result=false)
52     stats = mclient.echo(:msg => 'hello world')
53     stats.should eql([mc_valid_result, mc_error_result])
54   end
55 
56   it "should try to retry for non-responded nodes" do
57     nodes = [{'uid' => 1}, {'uid' => 2}, {'uid' => 3}]
58     rpcclient = mock('rpcclient') do
59       stubs(:progress=)
60       expects(:discover).with(:nodes => ['1','2','3'])
61       expects(:discover).with(:nodes => ['2','3'])
62     end
63     Astute::MClient.any_instance.stubs(:rpcclient).returns(rpcclient)
64 
65     mc_valid_result = mock_mc_result
66     mc_valid_result2 = mock_mc_result({:sender => '2'})
67 
68     rpcclient.stubs(:echo).returns([mc_valid_result]).then.
69                            returns([mc_valid_result2]).then
70 
71     mclient = MClient.new(@ctx, "faketest", nodes.map {|x| x['uid']})
72     mclient.retries = 1
73     expect { mclient.echo(:msg => 'hello world') }.to raise_error(/MCollective agents '3' didn't respond./)
74   end
75 
76   it "should raise error if agent returns statuscode != 0" do
77     nodes = [{'uid' => 1}, {'uid' => 2}, {'uid' => 3}]
78     rpcclient = mock('rpcclient') do
79       stubs(:progress=)
80       expects(:discover).with(:nodes => ['1','2','3'])
81       expects(:discover).with(:nodes => ['2','3'])
82     end
83     Astute::MClient.any_instance.stubs(:rpcclient).returns(rpcclient)
84 
85     mc_valid_result = mock_mc_result
86     mc_failed_result = mock_mc_result({:sender => '2', :statuscode => 1})
87 
88     rpcclient.stubs(:echo).returns([mc_valid_result]).then.
89                            returns([mc_failed_result]).then
90 
91     mclient = MClient.new(@ctx, "faketest", nodes.map {|x| x['uid']})
92     mclient.retries = 1
93     expect { mclient.echo(:msg => 'hello world') }.to \
94         raise_error(/MCollective agents '3' didn't respond. \n.* failed nodes: 2/)
95   end
96 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-nailyfact_deploy_spec_rb.html b/coverage/rcov/spec-unit-nailyfact_deploy_spec_rb.html new file mode 100644 index 00000000..441285dd --- /dev/null +++ b/coverage/rcov/spec-unit-nailyfact_deploy_spec_rb.html @@ -0,0 +1,917 @@ + + + + spec/unit/nailyfact_deploy_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/nailyfact_deploy_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/nailyfact_deploy_spec.rb28670
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 describe "NailyFact DeploymentEngine" do
20   context "When deploy is called, " do
21     before(:each) do
22       @ctx = mock
23       @ctx.stubs(:task_id)
24       @ctx.stubs(:deploy_log_parser).returns(Astute::LogParser::NoParsing.new)
25       reporter = mock
26       @ctx.stubs(:reporter).returns(reporter)
27       reporter.stubs(:report)
28       @deploy_engine = Astute::DeploymentEngine::NailyFact.new(@ctx)
29       meta = {
30         'interfaces' => [
31           {
32             'name' => 'eth1',
33           }, {
34             'name' => 'eth0',
35           }
36         ]
37       }
38       @data = {"args" =>
39                 {"attributes" =>
40                   {"storage_network_range" => "172.16.0.0/24", "auto_assign_floating_ip" => false,
41                    "mysql" => {"root_password" => "Z2EqsZo5"},
42                    "keystone" => {"admin_token" => "5qKy0i63", "db_password" => "HHQ86Rym", "admin_tenant" => "admin"},
43                    "nova" => {"user_password" => "h8RY8SE7", "db_password" => "Xl9I51Cb"},
44                    "glance" => {"user_password" => "nDlUxuJq", "db_password" => "V050pQAn"},
45                    "rabbit" => {"user" => "nova", "password" => "FLF3txKC"},
46                    "management_network_range" => "192.168.0.0/24",
47                    "public_network_range" => "240.0.1.0/24",
48                    "fixed_network_range" => "10.0.0.0/24",
49                    "floating_network_range" => "240.0.0.0/24"},
50                "task_uuid" => "19d99029-350a-4c9c-819c-1f294cf9e741",
51                "nodes" => [{"mac" => "52:54:00:0E:B8:F5", "status" => "provisioning",
52                             "uid" => "devnailgun.mirantis.com", "error_type" => nil,
53                             "fqdn" => "devnailgun.mirantis.com",
54                             "network_data" => [{"gateway" => "192.168.0.1",
55                                                 "name" => "management", "dev" => "eth0",
56                                                 "brd" => "192.168.0.255", "netmask" => "255.255.255.0",
57                                                 "vlan" => 102, "ip" => "192.168.0.2/24"},
58                                                {"gateway" => "240.0.1.1",
59                                                 "name" => "public", "dev" => "eth0",
60                                                 "brd" => "240.0.1.255", "netmask" => "255.255.255.0",
61                                                 "vlan" => 101, "ip" => "240.0.1.2/24"},
62                                                {"name" => "floating", "dev" => "eth0", "vlan" => 120},
63                                                {"name" => "fixed", "dev" => "eth0", "vlan" => 103},
64                                                {"name" => "storage", "dev" => "eth0", "vlan" => 104,
65                                                 "ip" => "172.16.1.2/24", "netmask" => "255.255.255.0",
66                                                 "brd" => "172.16.1.255"}],
67                             "id" => 1,
68                             "ip" => "10.20.0.200",
69                             "role" => "controller",
70                             'meta' => meta},
71                            {"mac" => "52:54:00:50:91:DD", "status" => "provisioning",
72                             "uid" => 2, "error_type" => nil,
73                             "fqdn" => "slave-2.mirantis.com",
74                             "network_data" => [{"gateway" => "192.168.0.1",
75                                                 "name" => "management", "dev" => "eth0",
76                                                 "brd" => "192.168.0.255", "netmask" => "255.255.255.0",
77                                                 "vlan" => 102, "ip" => "192.168.0.3/24"},
78                                                {"gateway" => "240.0.1.1",
79                                                 "name" => "public", "dev" => "eth0",
80                                                 "brd" => "240.0.1.255", "netmask" => "255.255.255.0",
81                                                 "vlan" => 101, "ip" => "240.0.1.3/24"},
82                                                {"name" => "floating", "dev" => "eth0", "vlan" => 120},
83                                                {"name" => "fixed", "dev" => "eth0", "vlan" => 103},
84                                                {"name" => "storage", "dev" => "eth0", "vlan" => 104,
85                                                 "ip" => "172.16.1.3/24", "netmask" => "255.255.255.0",
86                                                 "brd" => "172.16.1.255"}],
87                             "id" => 2,
88                             "ip" => "10.20.0.221",
89                             "role" => "compute",
90                             'meta' => meta},
91                            {"mac" => "52:54:00:C3:2C:28", "status" => "provisioning",
92                             "uid" => 3, "error_type" => nil,
93                             "fqdn" => "slave-3.mirantis.com",
94                             "network_data" => [{"gateway" => "192.168.0.1",
95                                                 "name" => "management", "dev" => "eth0",
96                                                 "brd" => "192.168.0.255", "netmask" => "255.255.255.0",
97                                                 "vlan" => 102, "ip" => "192.168.0.4/24"},
98                                                {"gateway" => "240.0.1.1",
99                                                 "name" => "public", "dev" => "eth0",
100                                                 "brd" => "240.0.1.255", "netmask" => "255.255.255.0",
101                                                 "vlan" => 101, "ip" => "240.0.1.4/24"},
102                                                {"name" => "floating", "dev" => "eth0", "vlan" => 120},
103                                                {"name" => "fixed", "dev" => "eth0", "vlan" => 103},
104                                                {"name" => "storage", "dev" => "eth0", "vlan" => 104,
105                                                 "ip" => "172.16.1.4/24", "netmask" => "255.255.255.0",
106                                                 "brd" => "172.16.1.255"}],
107                             "id" => 3,
108                             "ip" => "10.20.0.68",
109                             "role" => "compute",
110                             'meta' => meta}]},
111               "method" => "deploy",
112               "respond_to" => "deploy_resp"}
113 
114       @data['args']['attributes']['controller_nodes'] = @data['args']['nodes'].
115         select { |node| node['role'] == 'controller'}
116 
117       ha_nodes = @data['args']['nodes'] +
118                           [{"mac" => "52:54:00:0E:88:88", "status" => "provisioned",
119                             "uid" => "4", "error_type" => nil,
120                             "fqdn" => "controller-4.mirantis.com",
121                             "network_data" => [{"gateway" => "192.168.0.1",
122                                                 "name" => "management", "dev" => "eth0",
123                                                 "brd" => "192.168.0.255", "netmask" => "255.255.255.0",
124                                                 "vlan" => 102, "ip" => "192.168.0.5/24"},
125                                                {"gateway" => "240.0.1.1",
126                                                 "name" => "public", "dev" => "eth0",
127                                                 "brd" => "240.0.1.255", "netmask" => "255.255.255.0",
128                                                 "vlan" => 101, "ip" => "240.0.1.5/24"},
129                                                {"name" => "floating", "dev" => "eth0", "vlan" => 120},
130                                                {"name" => "fixed", "dev" => "eth0", "vlan" => 103},
131                                                {"name" => "storage", "dev" => "eth0", "vlan" => 104,
132                                                 "ip" => "172.16.1.5/24", "netmask" => "255.255.255.0",
133                                                 "brd" => "172.16.1.255"}],
134                             "id" => 4,
135                             "ip" => "10.20.0.205",
136                             "role" => "controller",
137                             'meta' => meta},
138                            {"mac" => "52:54:00:0E:99:99", "status" => "provisioned",
139                             "uid" => "5", "error_type" => nil,
140                             "fqdn" => "controller-5.mirantis.com",
141                             "network_data" => [{"gateway" => "192.168.0.1",
142                                                 "name" => "management", "dev" => "eth0",
143                                                 "brd" => "192.168.0.255", "netmask" => "255.255.255.0",
144                                                 "vlan" => 102, "ip" => "192.168.0.6/24"},
145                                                {"gateway" => "240.0.1.1",
146                                                 "name" => "public", "dev" => "eth0",
147                                                 "brd" => "240.0.1.255", "netmask" => "255.255.255.0",
148                                                 "vlan" => 101, "ip" => "240.0.1.6/24"},
149                                                {"name" => "floating", "dev" => "eth0", "vlan" => 120},
150                                                {"name" => "fixed", "dev" => "eth0", "vlan" => 103},
151                                                {"name" => "storage", "dev" => "eth0", "vlan" => 104,
152                                                 "ip" => "172.16.1.6/24", "netmask" => "255.255.255.0",
153                                                 "brd" => "172.16.1.255"}],
154                             "id" => 5,
155                             "ip" => "10.20.0.206",
156                             "role" => "controller",
157                             'meta' => meta}]
158       @data_ha = Marshal.load(Marshal.dump(@data))
159       @data_ha['args']['nodes'] = ha_nodes
160       @data_ha['args']['attributes']['deployment_mode'] = "ha"
161       # VIPs are required for HA mode and should be passed from Nailgun (only in HA)
162       @data_ha['args']['attributes']['management_vip'] = "192.168.0.111"
163       @data_ha['args']['attributes']['public_vip'] = "240.0.1.111"
164     end
165 
166     it "it should call valid method depends on attrs" do
167       nodes = [{'uid' => 1}]
168       attrs = {'deployment_mode' => 'ha'}
169       attrs_modified = attrs.merge({'some' => 'somea'})
170 
171       @deploy_engine.expects(:attrs_ha).with(nodes, attrs).returns(attrs_modified)
172       @deploy_engine.expects(:deploy_ha).with(nodes, attrs_modified)
173       # All implementations of deploy_piece go to subclasses
174       @deploy_engine.respond_to?(:deploy_piece).should be_true
175       @deploy_engine.deploy(nodes, attrs)
176     end
177 
178     it "it should raise an exception if deployment mode is unsupported" do
179       nodes = [{'uid' => 1}]
180       attrs = {'deployment_mode' => 'unknown'}
181       expect {@deploy_engine.deploy(nodes, attrs)}.to raise_exception(/Method attrs_unknown is not implemented/)
182     end
183 
184     it "multinode deploy should not raise any exception" do
185       @data['args']['attributes']['deployment_mode'] = "multinode"
186       Astute::Metadata.expects(:publish_facts).times(@data['args']['nodes'].size)
187       # we got two calls, one for controller, and another for all computes
188       controller_nodes = @data['args']['nodes'].select{|n| n['role'] == 'controller'}
189       compute_nodes = @data['args']['nodes'].select{|n| n['role'] == 'compute'}
190       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, controller_nodes, instance_of(Fixnum), true).once
191       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, compute_nodes, instance_of(Fixnum), true).once
192       @deploy_engine.deploy(@data['args']['nodes'], @data['args']['attributes'])
193     end
194 
195     it "ha deploy should not raise any exception" do
196       Astute::Metadata.expects(:publish_facts).at_least_once
197       controller_nodes = @data_ha['args']['nodes'].select{|n| n['role'] == 'controller'}
198       primary_nodes = [controller_nodes.shift]
199       compute_nodes = @data_ha['args']['nodes'].select{|n| n['role'] == 'compute'}
200       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, primary_nodes, 0, false).once
201       controller_nodes.each do |n|
202         Astute::PuppetdDeployer.expects(:deploy).with(@ctx, [n], 2, true).once
203       end
204       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, primary_nodes, 2, true).once
205       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, compute_nodes, instance_of(Fixnum), true).once
206       @deploy_engine.deploy(@data_ha['args']['nodes'], @data_ha['args']['attributes'])
207     end
208 
209     it "ha deploy should not raise any exception if there are only one controller" do
210       Astute::Metadata.expects(:publish_facts).at_least_once
211       Astute::PuppetdDeployer.expects(:deploy).once
212       ctrl = @data_ha['args']['nodes'].select {|n| n['role'] == 'controller'}[0]
213       @deploy_engine.deploy([ctrl], @data_ha['args']['attributes'])
214     end
215 
216     it "singlenode deploy should not raise any exception" do
217       @data['args']['attributes']['deployment_mode'] = "singlenode"
218       @data['args']['nodes'] = [@data['args']['nodes'][0]]  # We have only one node in singlenode
219       Astute::Metadata.expects(:publish_facts).times(@data['args']['nodes'].size)
220       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, @data['args']['nodes'], instance_of(Fixnum), true).once
221       @deploy_engine.deploy(@data['args']['nodes'], @data['args']['attributes'])
222     end
223 
224     describe 'Vlan manager' do
225       it 'Should set fixed_interface value' do
226         node = {
227           'role' => 'controller',
228           'uid' => 1,
229           'vlan_interface' => 'eth2',
230           'network_data' => [
231             {
232               "gateway" => "192.168.0.1",
233               "name" => "management", "dev" => "eth0",
234               "brd" => "192.168.0.255", "netmask" => "255.255.255.0",
235               "vlan" => 102, "ip" => "192.168.0.2/24"
236             }
237           ],
238           'meta' => {
239             'interfaces' => [
240               {
241                 'name' => 'eth1',
242               }, {
243                 'name' => 'eth0',
244               }
245             ]
246           }
247         }
248         attrs = {
249           'network_manager' => 'VlanManager'
250         }
251 
252         expect = {
253           "role" => "controller",
254           "uid"=>1,
255 
256           "network_data" => {"eth0.102" =>
257             {
258               "interface" => "eth0.102",
259               "ipaddr" => ["192.168.0.2/24"]
260             },
261             "lo" => {
262               "interface" => "lo",
263               "ipaddr" => ["127.0.0.1/8"]
264             },
265             'eth1' => {
266               'interface' => 'eth1',
267               'ipaddr' => 'none'
268             },
269             'eth0' => {
270               'interface' =>'eth0',
271               'ipaddr' => 'none'
272             },
273           }.to_json,
274 
275           "fixed_interface" => "eth2",
276           "network_manager" => "VlanManager",
277           "management_interface" => "eth0.102",
278           "internal_address" => "192.168.0.2",
279           'management_address' => '192.168.0.2'
280         }
281 
282         Astute::Metadata.expects(:publish_facts).with(@ctx, node['uid'], expect)
283         @deploy_engine.create_facts(node, attrs)
284       end
285     end
286   end
287 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-node_spec_rb.html b/coverage/rcov/spec-unit-node_spec_rb.html new file mode 100644 index 00000000..a8104a27 --- /dev/null +++ b/coverage/rcov/spec-unit-node_spec_rb.html @@ -0,0 +1,344 @@ + + + + spec/unit/node_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/node_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/node_spec.rb9552
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 describe Astute::Node do
20   it "accepts hash for initialization" do
21     node = Astute::Node.new('uid' => 'abc', 'info' => 'blabla')
22     node.uid.should == 'abc'
23     node.info.should == 'blabla'
24   end
25 
26   it "requires uid" do
27     expect{ Astute::Node.new({}) }.to raise_error(TypeError)
28   end
29 
30   it "stringifies uid" do
31     node = Astute::Node.new('uid' => :abc)
32     node.uid.should == 'abc'
33     node = Astute::Node.new('uid' => 123)
34     node.uid.should == '123'
35   end
36 
37   it "denies uid changes" do
38     node = Astute::Node.new('uid' => 1)
39     expect{ node.uid    = 2 }.to raise_error(TypeError)
40     expect{ node['uid'] = 2 }.to raise_error(TypeError)
41     expect{ node[:uid]  = 2 }.to raise_error(TypeError)
42   end
43 
44   it "allows [] accessors" do
45     node = Astute::Node.new('uid' => 123, 'info' => 'abc')
46     node['info'].should  == 'abc'
47     node[:info].should   == 'abc'
48     node['info']          = 'cba'
49     node['info'].should  == 'cba'
50     node[:info]           = 'dcb'
51     node[:info].should   == 'dcb'
52   end
53 
54   it "unwraps to hash" do
55     hash = {'uid' => '123', 'info' => 'abc'}
56     node = Astute::Node.new(hash)
57     node.to_hash.should == hash
58     node.to_hash.should_not === node.instance_variable_get(:@table)
59   end
60 end
61 
62 describe Astute::NodesHash do
63   it "accepts array of hashes or nodes for initialization and allows accessing by uid" do
64     nodes = Astute::NodesHash.build(
65       [{'uid' => 123, 'info' => 'blabla1'},
66       Astute::Node.new({'uid' => 'abc', 'info' => 'blabla2'})])
67 
68     nodes['123'].info.should == 'blabla1'
69     nodes['abc'].info.should == 'blabla2'
70     nodes[123].info.should == 'blabla1'
71     nodes[:abc].info.should == 'blabla2'
72     nodes['123'].uid.should == '123'
73     nodes.values.map(&:class).uniq.should == [Astute::Node]
74   end
75 
76   it "allows easy elements addition and normalizes data" do
77     nodes = Astute::NodesHash.new
78     nodes << {'uid' => 1} << {'uid' => 2}
79     nodes.push({'uid' => 3}, {'uid' => 4}, {'uid' => 5})
80     nodes.keys.sort.should == %w(1 2 3 4 5)
81     nodes.values.map(&:class).uniq.should == [Astute::Node]
82   end
83 
84   it "introduces meaningful aliases" do
85     nodes = Astute::NodesHash.build(
86       [{'uid' => 123, 'info' => 'blabla1'},
87       Astute::Node.new({'uid' => 'abc', 'info' => 'blabla2'})])
88 
89     nodes.uids.should  == nodes.keys
90     nodes.nodes.should == nodes.values
91   end
92 
93   it "denies direct accessors" do
94     expect{ Astute::NodesHash.new['fake-uid'] = {'bla' => 'bla'} }.to raise_error(NoMethodError)
95   end
96 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-orchestrator_spec_rb.html b/coverage/rcov/spec-unit-orchestrator_spec_rb.html new file mode 100644 index 00000000..d10439fe --- /dev/null +++ b/coverage/rcov/spec-unit-orchestrator_spec_rb.html @@ -0,0 +1,1328 @@ + + + + spec/unit/orchestrator_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/orchestrator_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/orchestrator_spec.rb423183
98.58%
+
+
+
+
96.72%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 describe Astute::Orchestrator do
20   include SpecHelpers
21 
22   before(:each) do
23     @orchestrator = Astute::Orchestrator.new
24     @reporter = mock('reporter')
25     @reporter.stub_everything
26   end
27 
28   describe '#verify_networks' do
29     def make_nodes(*uids)
30       uids.map do |uid|
31         {
32           'uid' => uid.to_s,
33           'networks' => [
34             {
35               'iface' => 'eth0',
36               'vlans' => [100, 101]
37             }
38           ]
39         }
40       end
41     end
42 
43     it "must be able to complete" do
44       nodes = make_nodes(1, 2)
45       res1 = {
46         :data => {
47           :uid => "1",
48           :neighbours => {
49             "eth0" => {
50               "100" => {"1" => ["eth0"], "2" => ["eth0"]},
51               "101" => {"1" => ["eth0"]}}}},
52         :sender => "1"}
53       res2 = {
54         :data => {
55           :uid => "2",
56           :neighbours => {
57             "eth0" => {
58               "100" => {"1" => ["eth0"], "2" => ["eth0"]},
59               "101" => {"1" => ["eth0"], "2" => ["eth0"]}
60             }}},
61         :sender => "2"}
62       valid_res = {:statuscode => 0, :sender => '1'}
63       mc_res1 = mock_mc_result(res1)
64       mc_res2 = mock_mc_result(res2)
65       mc_valid_res = mock_mc_result
66 
67       rpcclient = mock_rpcclient(nodes)
68 
69       rpcclient.expects(:get_probing_info).once.returns([mc_res1, mc_res2])
70       nodes.each do |node|
71         rpcclient.expects(:discover).with(:nodes => [node['uid']]).at_least_once
72 
73         data_to_send = {}
74         node['networks'].each{ |net| data_to_send[net['iface']] = net['vlans'].join(",") }
75 
76         rpcclient.expects(:start_frame_listeners).
77           with(:interfaces => data_to_send.to_json).
78           returns([mc_valid_res]*2)
79 
80         rpcclient.expects(:send_probing_frames).
81           with(:interfaces => data_to_send.to_json).
82           returns([mc_valid_res]*2)
83       end
84       Astute::MClient.any_instance.stubs(:rpcclient).returns(rpcclient)
85 
86       res = @orchestrator.verify_networks(@reporter, 'task_uuid', nodes)
87       expected = {"nodes" => [{"networks" => [{"iface"=>"eth0", "vlans"=>[100]}], "uid"=>"1"},
88           {"networks"=>[{"iface"=>"eth0", "vlans"=>[100, 101]}], "uid"=>"2"}]}
89       res.should eql(expected)
90     end
91 
92     it "returns error if nodes list is empty" do
93       res = @orchestrator.verify_networks(@reporter, 'task_uuid', [])
94       res.should eql({'status' => 'error', 'error' => "Network verification requires a minimum of two nodes."})
95     end
96 
97     it "returns all vlans passed if only one node provided" do
98       nodes = make_nodes(1)
99       res = @orchestrator.verify_networks(@reporter, 'task_uuid', nodes)
100       expected = {"nodes" => [{"uid"=>"1", "networks" => [{"iface"=>"eth0", "vlans"=>[100, 101]}]}]}
101       res.should eql(expected)
102     end
103   end
104 
105   it "must be able to return node type" do
106     nodes = [{'uid' => '1'}]
107     res = {:data => {:node_type => 'target'},
108            :sender=>"1"}
109 
110     mc_res = mock_mc_result(res)
111     mc_timeout = 5
112 
113     rpcclient = mock_rpcclient(nodes, mc_timeout)
114     rpcclient.expects(:get_type).once.returns([mc_res])
115 
116     types = @orchestrator.node_type(@reporter, 'task_uuid', nodes, mc_timeout)
117     types.should eql([{"node_type"=>"target", "uid"=>"1"}])
118   end
119 
120   it "in remove_nodes, it returns empty list if nodes are not provided" do
121     res = @orchestrator.remove_nodes(@reporter, 'task_uuid', [])
122     res.should eql({'nodes' => []})
123   end
124 
125   it "remove_nodes cleans nodes and reboots them" do
126     removed_hash = {:sender => '1',
127                     :data => {:rebooted => true}}
128     error_hash = {:sender => '2',
129                   :data => {:rebooted => false, :error_msg => 'Could not reboot'}}
130     nodes = [{'uid' => 1}, {'uid' => 2}]
131 
132     rpcclient = mock_rpcclient
133     mc_removed_res = mock_mc_result(removed_hash)
134     mc_error_res = mock_mc_result(error_hash)
135 
136     rpcclient.expects(:erase_node).at_least_once.with(:reboot => true).returns([mc_removed_res, mc_error_res])
137 
138     res = @orchestrator.remove_nodes(@reporter, 'task_uuid', nodes)
139     res.should eql({'nodes' => [{'uid' => '1'}], 'status' => 'error',
140                     'error_nodes' => [{"uid"=>"2", "error"=>"RPC method 'erase_node' failed "\
141                                                             "with message: Could not reboot"}]})
142   end
143 
144   it "it calls deploy method with valid arguments" do
145     nodes = [{'uid' => 1}]
146     attrs = {'a' => 'b'}
147     Astute::DeploymentEngine::NailyFact.any_instance.expects(:deploy).
148                                                      with([{'uid' => '1'}], attrs)
149     @orchestrator.deploy(@reporter, 'task_uuid', nodes, attrs)
150   end
151 
152   it "deploy method raises error if nodes list is empty" do
153     expect {@orchestrator.deploy(@reporter, 'task_uuid', [], {})}.
154                           to raise_error(/Nodes to deploy are not provided!/)
155   end
156 
157   it "remove_nodes try to call MCAgent multiple times on error" do
158     removed_hash = {:sender => '1',
159                     :data => {:rebooted => true}}
160     error_hash = {:sender => '2',
161                   :data => {:rebooted => false, :error_msg => 'Could not reboot'}}
162     nodes = [{'uid' => 1}, {'uid' => 2}]
163 
164     rpcclient = mock_rpcclient(nodes)
165     mc_removed_res = mock_mc_result(removed_hash)
166     mc_error_res = mock_mc_result(error_hash)
167 
168     retries = Astute.config[:MC_RETRIES]
169     retries.should == 5
170     rpcclient.expects(:discover).with(:nodes => ['2']).times(retries)
171     rpcclient.expects(:erase_node).times(retries + 1).with(:reboot => true).returns([mc_removed_res, mc_error_res]).then.returns([mc_error_res])
172 
173     res = @orchestrator.remove_nodes(@reporter, 'task_uuid', nodes)
174     res.should eql({'nodes' => [{'uid' => '1'}], 'status' => 'error',
175                     'error_nodes' => [{"uid"=>"2", "error"=>"RPC method 'erase_node' failed "\
176                                                             "with message: Could not reboot"}]})
177   end
178 
179   it "remove_nodes try to call MCAgent multiple times on no response" do
180     removed_hash = {:sender => '2', :data => {:rebooted => true}}
181     then_removed_hash = {:sender => '3', :data => {:rebooted => true}}
182     nodes = [{'uid' => 1}, {'uid' => 2}, {'uid' => 3}]
183 
184     rpcclient = mock_rpcclient(nodes)
185     mc_removed_res = mock_mc_result(removed_hash)
186     mc_then_removed_res = mock_mc_result(then_removed_hash)
187 
188     retries = Astute.config[:MC_RETRIES]
189     rpcclient.expects(:discover).with(:nodes => %w(1 3)).times(1)
190     rpcclient.expects(:discover).with(:nodes => %w(1)).times(retries - 1)
191     rpcclient.expects(:erase_node).times(retries + 1).with(:reboot => true).
192         returns([mc_removed_res]).then.returns([mc_then_removed_res]).then.returns([])
193 
194     res = @orchestrator.remove_nodes(@reporter, 'task_uuid', nodes)
195     res['nodes'] = res['nodes'].sort_by{|n| n['uid'] }
196     res.should eql({'nodes' => [{'uid' => '2'}, {'uid' => '3'}],
197                     'inaccessible_nodes' => [{'uid'=>'1', 'error'=>'Node not answered by RPC.'}]})
198   end
199 
200   it "remove_nodes and returns early if retries were successful" do
201     removed_hash = {:sender => '1', :data => {:rebooted => true}}
202     then_removed_hash = {:sender => '2', :data => {:rebooted => true}}
203     nodes = [{'uid' => 1}, {'uid' => 2}]
204 
205     rpcclient = mock_rpcclient(nodes)
206     mc_removed_res = mock_mc_result(removed_hash)
207     mc_then_removed_res = mock_mc_result(then_removed_hash)
208 
209     retries = Astute.config[:MC_RETRIES]
210     retries.should_not == 2
211     rpcclient.expects(:discover).with(:nodes => %w(2)).times(1)
212     rpcclient.expects(:erase_node).times(2).with(:reboot => true).
213         returns([mc_removed_res]).then.returns([mc_then_removed_res])
214 
215     res = @orchestrator.remove_nodes(@reporter, 'task_uuid', nodes)
216     res['nodes'] = res['nodes'].sort_by{|n| n['uid'] }
217     res.should eql({'nodes' => [{'uid' => '1'}, {'uid' => '2'}]})
218   end
219 
220   it "remove_nodes do not fail if any of nodes failed"
221   
222   
223   before(:all) do
224     @data = {
225       "engine"=>{
226         "url"=>"http://localhost/cobbler_api", 
227         "username"=>"cobbler", 
228         "password"=>"cobbler"
229       }, 
230       "task_uuid"=>"a5c44b9a-285a-4a0c-ae65-2ed6b3d250f4",
231       "nodes" => [
232         {
233           'uid' => '1',
234           'profile' => 'centos-x86_64',
235           "name"=>"controller-1",
236           'power_type' => 'ssh',
237           'power_user' => 'root',
238           'power_pass' => '/root/.ssh/bootstrap.rsa',
239           'power-address' => '1.2.3.5',
240           'hostname' => 'name.domain.tld',
241           'name_servers' => '1.2.3.4 1.2.3.100',
242           'name_servers_search' => 'some.domain.tld domain.tld',
243           'netboot_enabled' => '1',
244           'ks_meta' => 'some_param=1 another_param=2',
245           'interfaces' => {
246             'eth0' => {
247               'mac_address' => '00:00:00:00:00:00',
248               'static' => '1',
249               'netmask' => '255.255.255.0',
250               'ip_address' => '1.2.3.5',
251               'dns_name' => 'node.mirantis.net',
252             },
253             'eth1' => {
254               'mac_address' => '00:00:00:00:00:01',
255               'static' => '0',
256               'netmask' => '255.255.255.0',
257               'ip_address' => '1.2.3.6',
258             }
259           },
260           'interfaces_extra' => {
261             'eth0' => {
262               'peerdns' => 'no',
263               'onboot' => 'yes',
264             },
265             'eth1' => {
266               'peerdns' => 'no',
267               'onboot' => 'yes',
268             }
269           }
270         }
271       ]
272     }.freeze
273   end
274   
275   describe '#fast_provision' do
276     
277     context 'cobler cases' do
278       it "raise error if cobler settings empty" do
279         expect {@orchestrator.fast_provision(@reporter, {}, @data['nodes'])}.
280                               to raise_error(StopIteration)
281       end
282     end
283     
284     context 'node state cases' do
285       before(:each) do
286       
287         remote = mock() do
288           stubs(:call)
289           stubs(:call).with('login', 'cobbler', 'cobbler').returns('remotetoken')
290         end
291         @tmp = XMLRPC::Client
292         XMLRPC::Client = mock() do
293           stubs(:new).returns(remote)
294         end
295       end
296     
297       it "raises error if nodes list is empty" do
298         expect {@orchestrator.fast_provision(@reporter, @data['engine'], {})}.
299                               to raise_error(/Nodes to provision are not provided!/)
300       end
301     
302       it "try to reboot nodes from list" do
303         Astute::Provision::Cobbler.any_instance do
304           expects(:power_reboot).with('controller-1')
305         end
306         @orchestrator.stubs(:check_reboot_nodes).returns([])
307         @orchestrator.fast_provision(@reporter, @data['engine'], @data['nodes'])
308       end
309       
310       before(:each) { Astute::Provision::Cobbler.any_instance.stubs(:power_reboot).returns(333) }
311         
312       context 'node reboot success' do
313         
314         before(:each) { Astute::Provision::Cobbler.any_instance.stubs(:event_status).
315                                                    returns([Time.now.to_f, 'controller-1', 'complete'])}
316         
317         it "does not find failed nodes" do
318           Astute::Provision::Cobbler.any_instance.stubs(:event_status).
319                                                   returns([Time.now.to_f, 'controller-1', 'complete'])
320           
321           @orchestrator.fast_provision(@reporter, @data['engine'], @data['nodes'])
322         end
323         
324         it "report about success" do
325           @reporter.expects(:report).with({'status' => 'ready', 'progress' => 100}).returns(true)
326           @orchestrator.fast_provision(@reporter, @data['engine'], @data['nodes'])
327         end
328         
329         it "sync engine state" do
330           Astute::Provision::Cobbler.any_instance do
331             expects(:sync).once
332           end
333           @orchestrator.fast_provision(@reporter, @data['engine'], @data['nodes'])
334         end
335                 
336       end
337       
338       context 'node reboot fail' do
339         before(:each) { Astute::Provision::Cobbler.any_instance.stubs(:event_status).
340                                                                 returns([Time.now.to_f, 'controller-1', 'failed'])}
341         
342         it "should sync engine state" do
343           Astute::Provision::Cobbler.any_instance do
344             expects(:sync).once
345           end
346           begin
347             @orchestrator.fast_provision(@reporter, @data['engine'], @data['nodes'])
348           rescue
349           end
350         end
351         
352         it "raise error if failed node find" do
353           expect {@orchestrator.fast_provision(@reporter, @data['engine'], @data['nodes'])}.to raise_error(StopIteration)
354         end
355               
356       end
357 
358     end
359   end
360   
361   describe '#provision' do
362     
363     before(:each) do
364       # Disable sleeping in test env (doubles the test speed)
365       def @orchestrator.sleep_not_greater_than(time, &block)
366         block.call
367       end
368     end
369     
370     it "raises error if nodes list is empty" do
371       expect {@orchestrator.provision(@reporter, @data['task_uuid'], {})}.
372                             to raise_error(/Nodes to provision are not provided!/)
373     end
374     
375     it "prepare provision log for parsing" do
376       Astute::LogParser::ParseProvisionLogs.any_instance do
377         expects(:prepare).with(@data['nodes']).once
378       end
379       @orchestrator.stubs(:report_about_progress).returns()
380       @orchestrator.stubs(:node_type).returns([{'uid' => '1', 'node_type' => 'target' }])
381       
382       @orchestrator.provision(@reporter, @data['task_uuid'], @data['nodes'])
383     end
384     
385     it "ignore problem with parsing provision log" do
386       Astute::LogParser::ParseProvisionLogs.any_instance do
387         stubs(:prepare).with(@data['nodes']).raises
388       end
389       
390       @orchestrator.stubs(:report_about_progress).returns()
391       @orchestrator.stubs(:node_type).returns([{'uid' => '1', 'node_type' => 'target' }])
392       
393       @orchestrator.provision(@reporter, @data['task_uuid'], @data['nodes'])
394     end
395     
396     it 'provision nodes using mclient' do
397       @orchestrator.stubs(:report_about_progress).returns()
398       @orchestrator.expects(:node_type).returns([{'uid' => '1', 'node_type' => 'target' }])
399       
400       @orchestrator.provision(@reporter, @data['task_uuid'], @data['nodes'])
401     end
402     
403     it "fail if timeout of provisioning is exceeded" do
404       Astute::LogParser::ParseProvisionLogs.any_instance do
405         stubs(:prepare).returns()
406       end
407             
408       Timeout.stubs(:timeout).raises(Timeout::Error)
409       
410       msg = 'Timeout of provisioning is exceeded.'      
411       error_mgs = {'status' => 'error', 'error' => msg, 'nodes' => [{ 'uid' => '1',
412                                                             'status' => 'error',
413                                                             'error_msg' => msg,
414                                                             'progress' => 100,
415                                                             'error_type' => 'provision'}]}
416         
417       @reporter.expects(:report).with(error_mgs).once
418       @orchestrator.provision(@reporter, @data['task_uuid'], @data['nodes'])
419     end
420     
421   end
422   
423 end
424 
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-puppetd_spec_rb.html b/coverage/rcov/spec-unit-puppetd_spec_rb.html new file mode 100644 index 00000000..ee89ab9a --- /dev/null +++ b/coverage/rcov/spec-unit-puppetd_spec_rb.html @@ -0,0 +1,758 @@ + + + + spec/unit/puppetd_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/puppetd_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/puppetd_spec.rb233105
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 include Astute
20 
21 describe "Puppetd" do
22   include SpecHelpers
23 
24   context "PuppetdDeployer" do
25     before :each do
26       @ctx = mock
27       @ctx.stubs(:task_id)
28       @reporter = mock('reporter')
29       @ctx.stubs(:reporter).returns(ProxyReporter.new(@reporter))
30       @ctx.stubs(:deploy_log_parser).returns(Astute::LogParser::NoParsing.new)
31     end
32 
33     it "reports ready status for node if puppet deploy finished successfully" do
34       @reporter.expects(:report).with('nodes' => [{'uid' => '1', 'status' => 'ready', 'progress' => 100}])
35       last_run_result = {:data=>
36           {:time=>{"last_run"=>1358425701},
37            :status => "running", :resources => {'failed' => 0},
38            :running => 1, :idling => 0},
39            :sender=>"1"}
40       last_run_result_new = Marshal.load(Marshal.dump(last_run_result))
41       last_run_result_new[:data][:time]['last_run'] = 1358426000
42 
43       last_run_result_finished = Marshal.load(Marshal.dump(last_run_result))
44       last_run_result_finished[:data][:status] = 'stopped'
45       last_run_result_finished[:data][:time]['last_run'] = 1358427000
46 
47       nodes = [{'uid' => '1'}]
48 
49       rpcclient = mock_rpcclient(nodes)
50 
51       rpcclient_valid_result = mock_mc_result(last_run_result)
52       rpcclient_new_res = mock_mc_result(last_run_result_new)
53       rpcclient_finished_res = mock_mc_result(last_run_result_finished)
54 
55       rpcclient.stubs(:last_run_summary).returns([rpcclient_valid_result]).then.
56           returns([rpcclient_valid_result]).then.
57           returns([rpcclient_new_res]).then.
58           returns([rpcclient_finished_res])
59 
60       rpcclient.expects(:runonce).at_least_once.returns([rpcclient_valid_result])
61 
62       Astute::PuppetdDeployer.deploy(@ctx, nodes, retries=0)
63     end
64 
65     it "doesn't report ready status for node if change_node_status disabled" do
66       @reporter.expects(:report).never
67       last_run_result = {:data=>
68           {:time=>{"last_run"=>1358425701},
69            :status => "running", :resources => {'failed' => 0},
70            :running => 1, :idling => 0},
71            :sender=>"1"}
72       last_run_result_new = Marshal.load(Marshal.dump(last_run_result))
73       last_run_result_new[:data][:time]['last_run'] = 1358426000
74 
75       last_run_result_finished = Marshal.load(Marshal.dump(last_run_result))
76       last_run_result_finished[:data][:status] = 'stopped'
77       last_run_result_finished[:data][:time]['last_run'] = 1358427000
78 
79       nodes = [{'uid' => '1'}]
80 
81       rpcclient = mock_rpcclient(nodes)
82 
83       rpcclient_valid_result = mock_mc_result(last_run_result)
84       rpcclient_new_res = mock_mc_result(last_run_result_new)
85       rpcclient_finished_res = mock_mc_result(last_run_result_finished)
86 
87       rpcclient.stubs(:last_run_summary).returns([rpcclient_valid_result]).then.
88           returns([rpcclient_valid_result]).then.
89           returns([rpcclient_new_res]).then.
90           returns([rpcclient_finished_res])
91 
92       rpcclient.expects(:runonce).at_least_once.returns([rpcclient_valid_result])
93 
94       Astute::PuppetdDeployer.deploy(@ctx, nodes, retries=0, change_node_status=false)
95     end
96 
97     it "publishes error status for node if puppet failed" do
98       @reporter.expects(:report).with('nodes' => [{'status' => 'error',
99         'error_type' => 'deploy', 'uid' => '1'}])
100 
101       last_run_result = {:statuscode=>0, :data=>
102           {:changes=>{"total"=>1}, :time=>{"last_run"=>1358425701},
103            :resources=>{"failed"=>0}, :status => "stopped", :enabled => 1,
104            :stopped => 1, :idling => 0, :running => 0, :runtime => 1358425701},
105          :sender=>"1"}
106 
107       last_run_result_idle_pre = Marshal.load(Marshal.dump(last_run_result))
108       last_run_result_idle_pre[:data].update(
109         {:status => 'idling', :idling => 1, :stopped => 0}
110       )
111 
112       last_run_result_running = Marshal.load(Marshal.dump(last_run_result))
113       last_run_result_running[:data].update(
114         {:status => 'running', :running => 1, :stopped => 0}
115       )
116 
117       last_run_result_finishing = Marshal.load(Marshal.dump(last_run_result_running))
118       last_run_result_finishing[:data].update(
119         {
120           :runtime => 1358426000, :time => {"last_run" => 1358426000},
121           :resources => {"failed" => 1}
122         }
123       )
124 
125       last_run_result_idle_post = Marshal.load(Marshal.dump(last_run_result_finishing))
126       last_run_result_idle_post[:data].update(
127         {:status => 'idling', :idling => 1, :running => 0}
128       )
129 
130       last_run_result_finished = Marshal.load(Marshal.dump(last_run_result_finishing))
131       last_run_result_finished[:data].update(
132         {:status => 'stopped', :stopped => 1, :running => 0}
133       )
134 
135       nodes = [{'uid' => '1'}]
136 
137       rpcclient = mock_rpcclient(nodes)
138 
139       rpcclient.stubs(:last_run_summary).times(9).
140         returns([ mock_mc_result(last_run_result) ]).then.
141         returns([ mock_mc_result(last_run_result) ]).then.
142         returns([ mock_mc_result(last_run_result_idle_pre) ]).then.
143         returns([ mock_mc_result(last_run_result_idle_pre) ]).then.
144         returns([ mock_mc_result(last_run_result_running) ]).then.
145         returns([ mock_mc_result(last_run_result_running) ]).then.
146         returns([ mock_mc_result(last_run_result_finishing) ]).then.
147         returns([ mock_mc_result(last_run_result_idle_post) ]).then.
148         returns([ mock_mc_result(last_run_result_finished) ])
149       rpcclient.expects(:runonce).once.
150         returns([ mock_mc_result(last_run_result) ])
151 
152       MClient.any_instance.stubs(:rpcclient).returns(rpcclient)
153       Astute::PuppetdDeployer.deploy(@ctx, nodes, 0)
154     end
155 
156     it "doesn't publish error status for node if change_node_status disabled" do
157       @reporter.expects(:report).never
158 
159       last_run_result = {:statuscode=>0, :data=>
160           {:changes=>{"total"=>1}, :time=>{"last_run"=>1358425701},
161            :resources=>{"failed"=>0}, :status => "running",
162            :running => 1, :idling => 0, :runtime => 100},
163          :sender=>"1"}
164       last_run_result_new = Marshal.load(Marshal.dump(last_run_result))
165       last_run_result_new[:data][:time]['last_run'] = 1358426000
166       last_run_result_new[:data][:resources]['failed'] = 1
167 
168       nodes = [{'uid' => '1'}]
169 
170       last_run_result_finished = Marshal.load(Marshal.dump(last_run_result))
171       last_run_result_finished[:data][:status] = 'stopped'
172       last_run_result_finished[:data][:time]['last_run'] = 1358427000
173       last_run_result_finished[:data][:resources]['failed'] = 1
174 
175       rpcclient = mock_rpcclient(nodes)
176 
177       rpcclient_valid_result = mock_mc_result(last_run_result)
178       rpcclient_new_res = mock_mc_result(last_run_result_new)
179       rpcclient_finished_res = mock_mc_result(last_run_result_finished)
180 
181       rpcclient.stubs(:last_run_summary).returns([rpcclient_valid_result]).then.
182           returns([rpcclient_valid_result]).then.
183           returns([rpcclient_new_res]).then.
184           returns([rpcclient_finished_res])
185       rpcclient.expects(:runonce).at_least_once.returns([rpcclient_valid_result])
186 
187       MClient.any_instance.stubs(:rpcclient).returns(rpcclient)
188       Astute::PuppetdDeployer.deploy(@ctx, nodes, retries=0, change_node_status=false)
189     end
190 
191     it "retries to run puppet if it fails" do
192       @reporter.expects(:report).with('nodes' => [{'uid' => '1', 'status' => 'ready', 'progress' => 100}])
193 
194       last_run_result = {:statuscode=>0, :data=>
195           {:changes=>{"total"=>1}, :time=>{"last_run"=>1358425701},
196            :resources=>{"failed"=>0}, :status => "running",
197            :running => 1, :idling => 0, :runtime => 100},
198          :sender=>"1"}
199       last_run_failed = Marshal.load(Marshal.dump(last_run_result))
200       last_run_failed[:data][:time]['last_run'] = 1358426000
201       last_run_failed[:data][:resources]['failed'] = 1
202       last_run_failed[:data][:status] = 'stopped'
203 
204       last_run_fixing = Marshal.load(Marshal.dump(last_run_result))
205       last_run_fixing[:data][:time]['last_run'] = 1358426000
206       last_run_fixing[:data][:resources]['failed'] = 1
207       last_run_fixing[:data][:status] = 'running'
208 
209       last_run_success = Marshal.load(Marshal.dump(last_run_result))
210       last_run_success[:data][:time]['last_run'] = 1358428000
211       last_run_success[:data][:status] = 'stopped'
212 
213       nodes = [{'uid' => '1'}]
214 
215       rpcclient = mock_rpcclient(nodes)
216 
217       rpcclient_valid_result = mock_mc_result(last_run_result)
218       rpcclient_failed = mock_mc_result(last_run_failed)
219       rpcclient_fixing = mock_mc_result(last_run_fixing)
220       rpcclient_succeed = mock_mc_result(last_run_success)
221 
222       rpcclient.stubs(:last_run_summary).returns([rpcclient_valid_result]).then.
223           returns([rpcclient_valid_result]).then.
224           returns([rpcclient_failed]).then.
225           returns([rpcclient_failed]).then.
226           returns([rpcclient_fixing]).then.
227           returns([rpcclient_succeed])
228       rpcclient.expects(:runonce).at_least_once.returns([rpcclient_valid_result])
229 
230       MClient.any_instance.stubs(:rpcclient).returns(rpcclient)
231       Astute::PuppetdDeployer.deploy(@ctx, nodes, retries=1)
232     end
233   end
234 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-reporter_spec_rb.html b/coverage/rcov/spec-unit-reporter_spec_rb.html new file mode 100644 index 00000000..fced1f3a --- /dev/null +++ b/coverage/rcov/spec-unit-reporter_spec_rb.html @@ -0,0 +1,674 @@ + + + + spec/unit/reporter_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/reporter_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/reporter_spec.rb205126
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 include Astute
20 
21 describe "ProxyReporter" do
22   context "Instance of ProxyReporter class" do
23     before :each do
24       @msg = {'nodes' => [{'status' => 'ready', 'uid' => '1'}]}
25       @msg_pr = {'nodes' => [@msg['nodes'][0],
26                              {'status' => 'deploying', 'uid' => '2',
27                               'progress' => 54}]}
28       @up_reporter = mock('up_reporter')
29       @reporter = ProxyReporter.new(@up_reporter)
30     end
31 
32     it "reports first-come data" do
33       @up_reporter.expects(:report).with(@msg)
34       @reporter.report(@msg)
35     end
36 
37     it "does not report the same message" do
38       @up_reporter.expects(:report).with(@msg).once
39       5.times { @reporter.report(@msg) }
40     end
41 
42     it "reports only updated node" do
43       updated_node = @msg_pr['nodes'][1]
44       expected_msg = {'nodes' => [updated_node]}
45       @up_reporter.expects(:report).with(@msg)
46       @up_reporter.expects(:report).with(expected_msg)
47       @reporter.report(@msg)
48       @reporter.report(@msg_pr)
49     end
50 
51     it "reports only if progress value is greater" do
52       msg1 = {'nodes' => [{'status' => 'deploying', 'uid' => '1', 'progress' => 54},
53                           {'status' => 'deploying', 'uid' => '2', 'progress' => 54}]}
54       msg2 = Marshal.load(Marshal.dump(msg1))
55       msg2['nodes'][1]['progress'] = 100
56       msg2['nodes'][1]['status'] = 'ready'
57       updated_node = msg2['nodes'][1]
58       expected_msg = {'nodes' => [updated_node]}
59 
60       @up_reporter.expects(:report).with(msg1)
61       @up_reporter.expects(:report).with(expected_msg)
62       @reporter.report(msg1)
63       @reporter.report(msg2)
64     end
65 
66     it "raises exception if wrong key passed" do
67       @msg['nodes'][0]['ups'] = 'some_value'
68       lambda {@reporter.report(@msg)}.should raise_error
69     end
70 
71     it "adjusts progress to 100 if passed greater" do
72       input_msg = {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 120}]}
73       expected_msg = {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 100}]}
74       @up_reporter.expects(:report).with(expected_msg)
75       @reporter.report(input_msg)
76     end
77 
78     it "adjusts progress to 0 if passed less" do
79       input_msg = {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => -20}]}
80       expected_msg = {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 0}]}
81       @up_reporter.expects(:report).with(expected_msg)
82       @reporter.report(input_msg)
83     end
84 
85     it "adjusts progress to 100 if status provisioned and no progress given" do
86       input_msg = {'nodes' => [{'uid' => 1, 'status' => 'provisioned'}]}
87       expected_msg = {'nodes' => [{'uid' => 1, 'status' => 'provisioned', 'progress' => 100}]}
88       @up_reporter.expects(:report).with(expected_msg)
89       @reporter.report(input_msg)
90     end
91 
92     it "adjusts progress to 100 if status ready and no progress given" do
93       input_msg = {'nodes' => [{'uid' => 1, 'status' => 'ready'}]}
94       expected_msg = {'nodes' => [{'uid' => 1, 'status' => 'ready', 'progress' => 100}]}
95       @up_reporter.expects(:report).with(expected_msg)
96       @reporter.report(input_msg)
97     end
98 
99     it "adjusts progress to 100 if status provisioned with progress" do
100       input_msg = {'nodes' => [{'uid' => 1, 'status' => 'provisioned', 'progress' => 50}]}
101       expected_msg = {'nodes' => [{'uid' => 1, 'status' => 'provisioned', 'progress' => 100}]}
102       @up_reporter.expects(:report).with(expected_msg)
103       @reporter.report(input_msg)
104     end
105 
106     it "adjusts progress to 100 if status ready with progress" do
107       input_msg = {'nodes' => [{'uid' => 1, 'status' => 'ready', 'progress' => 50}]}
108       expected_msg = {'nodes' => [{'uid' => 1, 'status' => 'ready', 'progress' => 100}]}
109       @up_reporter.expects(:report).with(expected_msg)
110       @reporter.report(input_msg)
111     end
112 
113     it "does not report if node was in ready, and trying to set is deploying" do
114       msg1 = {'nodes' => [{'uid' => 1, 'status' => 'ready'}]}
115       msg2 = {'nodes' => [{'uid' => 2, 'status' => 'ready'}]}
116       msg3 = {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 100}]}
117       @up_reporter.expects(:report).with(msg1)
118       @up_reporter.expects(:report).with(msg2)
119       @up_reporter.expects(:report).never
120       @reporter.report(msg1)
121       @reporter.report(msg2)
122       5.times { @reporter.report(msg3) }
123     end
124 
125     it "reports even not all keys provided" do
126       msg1 = {'nodes' => [{'uid' => 1, 'status' => 'deploying'}]}
127       msg2 = {'nodes' => [{'uid' => 2, 'status' => 'ready'}]}
128       @up_reporter.expects(:report).with(msg1)
129       @up_reporter.expects(:report).with(msg2)
130       @reporter.report(msg1)
131       @reporter.report(msg2)
132     end
133 
134     it "raises exception if progress provided and no status" do
135       msg1 = {'nodes' => [{'uid' => 1, 'status' => 'ready'}]}
136       msg2 = {'nodes' => [{'uid' => 1, 'progress' => 100}]}
137       @up_reporter.expects(:report).with(msg1)
138       @up_reporter.expects(:report).never
139       @reporter.report(msg1)
140       lambda {@reporter.report(msg2)}.should raise_error
141     end
142 
143     it "raises exception if status of node is not supported" do
144       msg1 = {'nodes' => [{'uid' => 1, 'status' => 'hah'}]}
145       @up_reporter.expects(:report).never
146       lambda {@reporter.report(msg1)}.should raise_error
147     end
148 
149     it "some other attrs are valid and passed" do
150       msg1 = {'nodes' => [{'uid' => 1, 'status' => 'deploying'}]}
151       msg2 = {'status' => 'error', 'error_type' => 'deploy',
152               'nodes' => [{'uid' => 2, 'status' => 'error', 'message' => 'deploy'}]}
153       @up_reporter.expects(:report).with(msg1)
154       @up_reporter.expects(:report).with(msg2)
155       @reporter.report(msg1)
156       @reporter.report(msg2)
157     end
158 
159     it "reports if status is greater" do
160       msgs = [{'nodes' => [{'uid' => 1, 'status' => 'provisioned'}]},
161               {'nodes' => [{'uid' => 1, 'status' => 'provisioning'}]},
162               {'nodes' => [{'uid' => 1, 'status' => 'ready'}]},
163               {'nodes' => [{'uid' => 1, 'status' => 'error'}]}]
164       @up_reporter.expects(:report).with(msgs[0])
165       @up_reporter.expects(:report).with(msgs[2])
166       @up_reporter.expects(:report).with(msgs[3])
167       msgs.each {|msg| @reporter.report(msg)}
168     end
169 
170     it "doesn't update progress if it less than previous progress with same status" do
171       msgs = [{'nodes' => [{'uid' => 1, 'status' => 'provisioning', 'progress' => 50}]},
172               {'nodes' => [{'uid' => 1, 'status' => 'provisioning', 'progress' => 10}]},
173               {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 50}]},
174               {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 10}]}]
175       @up_reporter.expects(:report).with(msgs[0])
176       @up_reporter.expects(:report).with(msgs[2])
177       @up_reporter.expects(:report).never
178       msgs.each {|msg| @reporter.report(msg)}
179     end
180 
181     it "updates progress if it less than previous progress when changing status" do
182       msgs = [{'nodes' => [{'uid' => 1, 'status' => 'provisioning', 'progress' => 50}]},
183               {'nodes' => [{'uid' => 1, 'status' => 'provisioned'}]},
184               {'nodes' => [{'uid' => 1, 'status' => 'provisioned', 'progress' => 100}]},
185               {'nodes' => [{'uid' => 1, 'status' => 'deploying', 'progress' => 0}]}]
186       @up_reporter.expects(:report).with(msgs[0])
187       @up_reporter.expects(:report).with(msgs[2])
188       @up_reporter.expects(:report).with(msgs[3])
189       @up_reporter.expects(:report).never
190       msgs.each {|msg| @reporter.report(msg)}
191     end
192 
193     it "doesn't forget previously reported attributes" do
194       msgs = [{'nodes' => [{'uid' => 1, 'status' => 'provisioning', 'progress' => 50}]},
195               {'nodes' => [{'uid' => 1, 'status' => 'provisioning'}]},
196               {'nodes' => [{'uid' => 1, 'status' => 'provisioning', 'key' => 'value'}]},
197               {'nodes' => [{'uid' => 1, 'status' => 'provisioning', 'progress' => 0}]},
198             ]
199       @up_reporter.expects(:report).with(msgs[0])
200       @up_reporter.expects(:report).with(msgs[2])
201       @up_reporter.expects(:report).never
202       msgs.each {|msg| @reporter.report(msg)}
203     end
204 
205   end
206 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/coverage/rcov/spec-unit-simplepuppet_deploy_spec_rb.html b/coverage/rcov/spec-unit-simplepuppet_deploy_spec_rb.html new file mode 100644 index 00000000..ba356765 --- /dev/null +++ b/coverage/rcov/spec-unit-simplepuppet_deploy_spec_rb.html @@ -0,0 +1,425 @@ + + + + spec/unit/simplepuppet_deploy_spec.rb + + + + + + +

Astute C0 Coverage Information - Simploco - RCov

+

spec/unit/simplepuppet_deploy_spec.rb

+ +
+ + + + + + + + + + + + + + + + + + + +
NameTotal LinesLines of CodeTotal CoverageCode Coverage
spec/unit/simplepuppet_deploy_spec.rb12273
100.00%
+
+
+
+
100.00%
+
+
+
+
+
+ +

Key

+ +
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
+ +

Coverage Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
2 #    Copyright 2013 Mirantis, Inc.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 
16 
17 require File.join(File.dirname(__FILE__), '../spec_helper')
18 
19 describe "SimplePuppet DeploymentEngine" do
20   context "When deploy is called, " do
21     before(:each) do
22       @ctx = mock
23       @ctx.stubs(:task_id)
24       @ctx.stubs(:deploy_log_parser).returns(Astute::LogParser::NoParsing.new)
25       @reporter = mock('reporter')
26       @reporter.stub_everything
27       @ctx.stubs(:reporter).returns(Astute::ProxyReporter.new(@reporter))
28       @deploy_engine = Astute::DeploymentEngine::SimplePuppet.new(@ctx)
29       @env = YAML.load_file(File.join(File.dirname(__FILE__), "..", "..", "examples", "no_attrs.yaml"))
30     end
31 
32     it "it should call valid method depends on attrs" do
33       nodes = [{'uid' => 1}]
34       attrs = {'deployment_mode' => 'ha'}
35       @deploy_engine.expects(:attrs_ha).never  # It is not supported in SimplePuppet
36       @deploy_engine.expects(:deploy_ha).with(nodes, attrs)
37       # All implementations of deploy_piece go to subclasses
38       @deploy_engine.respond_to?(:deploy_piece).should be_true
39       @deploy_engine.deploy(nodes, attrs)
40     end
41 
42     it "it should raise an exception if deployment mode is unsupported" do
43       nodes = [{'uid' => 1}]
44       attrs = {'deployment_mode' => 'unknown'}
45       expect {@deploy_engine.deploy(nodes, attrs)}.to raise_exception(
46               /Method deploy_unknown is not implemented/)
47     end
48 
49     it "multinode deploy should not raise any exception" do
50       @env['attributes']['deployment_mode'] = "multinode"
51       Astute::Metadata.expects(:publish_facts).never  # It is not supported in SimplePuppet
52       # we got two calls, one for controller, and another for all computes
53       Astute::PuppetdDeployer.expects(:deploy).twice
54       @deploy_engine.deploy(@env['nodes'], @env['attributes'])
55     end
56 
57     it "ha deploy should not raise any exception" do
58       @env['attributes']['deployment_mode'] = "ha"
59       Astute::Metadata.expects(:publish_facts).never
60       Astute::PuppetdDeployer.expects(:deploy).times(2)
61       @deploy_engine.deploy(@env['nodes'], @env['attributes'])
62     end
63 
64     it "singlenode deploy should not raise any exception" do
65       @env['attributes']['deployment_mode'] = "singlenode"
66       @env['nodes'] = [@env['nodes'][0]]  # We have only one node in singlenode
67       Astute::Metadata.expects(:publish_facts).never
68       Astute::PuppetdDeployer.expects(:deploy).once  # one call for one node
69       @deploy_engine.deploy(@env['nodes'], @env['attributes'])
70     end
71 
72     it "ha_compact deploy should not raise any exception" do
73       @env['attributes']['deployment_mode'] = "ha_compact"
74       @env['nodes'].concat([{'uid'=>'c1', 'role'=>'controller'},
75                             {'uid'=>'c2', 'role'=>'controller'},
76                             {'uid'=>'o1', 'role'=>'other'}])
77       controller_nodes = @env['nodes'].select{|n| n['role'] == 'controller'}
78       compute_nodes = @env['nodes'].select{|n| n['role'] == 'compute'}
79       other_nodes = @env['nodes'] - controller_nodes
80       primary_ctrl_nodes = [controller_nodes.shift]
81 
82       Astute::Metadata.expects(:publish_facts).never
83       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, primary_ctrl_nodes, 0, false).once
84       controller_nodes.each do |n|
85         Astute::PuppetdDeployer.expects(:deploy).with(@ctx, [n], 2, true).once
86       end
87       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, primary_ctrl_nodes, 2, true).once
88 
89       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, other_nodes, instance_of(Fixnum), true).once
90       @deploy_engine.deploy(@env['nodes'], @env['attributes'])
91     end
92 
93     it "ha_full deploy should not raise any exception" do
94       @env['attributes']['deployment_mode'] = "ha_full"
95       @env['nodes'].concat([{'uid'=>'c1', 'role'=>'controller'}, {'uid'=>'c2', 'role'=>'controller'},
96                             {'uid'=>'q1', 'role'=>'quantum'}, {'uid'=>'q2', 'role'=>'quantum'},
97                             {'uid'=>'st1', 'role'=>'storage'}, {'uid'=>'st2', 'role'=>'storage'},
98                             {'uid'=>'sw1', 'role'=>'primary-swift-proxy'}, {'uid'=>'sw2', 'role'=>'swift-proxy'},
99                             {'uid'=>'o1', 'role'=>'other'}])
100       controller_nodes = @env['nodes'].select{|n| n['role'] == 'controller'}
101       primary_ctrl_nodes = [controller_nodes.shift]
102       compute_nodes = @env['nodes'].select{|n| n['role'] == 'compute'}
103       quantum_nodes = @env['nodes'].select {|n| n['role'] == 'quantum'}
104       storage_nodes = @env['nodes'].select {|n| n['role'] == 'storage'}
105       proxy_nodes = @env['nodes'].select {|n| n['role'] == 'swift-proxy'}
106       primary_proxy_nodes = @env['nodes'].select {|n| n['role'] == 'primary-swift-proxy'}
107       primary_nodes = primary_ctrl_nodes + primary_proxy_nodes
108       other_nodes = @env['nodes'] - controller_nodes - primary_nodes - quantum_nodes
109 
110       Astute::Metadata.expects(:publish_facts).never
111       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, primary_ctrl_nodes, 0, false).once
112       controller_nodes.each do |n|
113         Astute::PuppetdDeployer.expects(:deploy).with(@ctx, [n], 2, true).once
114       end
115       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, primary_nodes, 2, true).once
116 
117       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, quantum_nodes, 2, true).once
118 
119       Astute::PuppetdDeployer.expects(:deploy).with(@ctx, other_nodes, instance_of(Fixnum), true).once
120       @deploy_engine.deploy(@env['nodes'], @env['attributes'])
121     end
122   end
123 end
+ +

Generated on 2013-07-19 12:05:31 +0400 with SimpleCov-RCov 0.2.3

+ + + diff --git a/examples/example_new_provisioning.yaml b/examples/example_new_provisioning.yaml index 969c63cc..1ea648fe 100644 --- a/examples/example_new_provisioning.yaml +++ b/examples/example_new_provisioning.yaml @@ -1,44 +1,71 @@ -node_01: - mac: 64:43:7B:CA:56:DD - name: controller-01 - ip: 10.20.0.94 - profile: centos-x86_64 - fqdn: controller-01.domain.tld +# Base config +task_uuid: deployment_task +engine: + url: http://localhost/cobbler_api + username: cobbler + password: cobbler + +power_info: &power_info power_type: ssh power_user: root - power_pass: /root/.ssh/bootstrap.rsa - power_address: 10.20.0.94 - netboot_enabled: '1' 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: &fqdn controller-5.domain.tld +# id: &id 5 +# uid: *id +# mac: &mac 08:00:27:E3:BC:28 +# ip: &ip 10.20.0.41 +# power_address: *ip + <<: *power_info #Write size in megabytes - ks_meta: - ks_spaces: '"[{\"type\": \"disk\", \"id\": \"disk/by-path/pci-0000:00:06.0-virtio-pci-virtio3\", - \"volumes\": [{\"mount\": \"/boot\", \"type\": \"partition\", \"size\": 200}, - {\"type\": \"mbr\"}, {\"size\": 20000, \"type\": \"pv\", \"vg\": \"os\"}], - \"size\": 20480}, {\"type\": \"vg\", \"id\": \"os\", \"volumes\": [{\"mount\": - \"/\", \"type\": \"lv\", \"name\": \"root\", \"size\": 10240 }, {\"mount\": - \"swap\", \"type\": \"lv\", \"name\": \"swap\", \"size\": 2048}]}]"' - 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 + 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.94 + ip_address: 10.20.0.41 netmask: 255.255.255.0 - dns_name: controller-01.domain.tld + dns_name: controller-5.domain.tld static: '1' - mac_address: 64:43:7B:CA:56:DD + 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' @@ -49,8 +76,6 @@ node_01: eth0: onboot: 'yes' peerdns: 'no' - -engine: - url: http://localhost/cobbler_api - username: cobbler - password: cobbler + +nodes: +- <<: *node1 diff --git a/lib/astute/enviroment.rb b/lib/astute/enviroment.rb new file mode 100644 index 00000000..fae21428 --- /dev/null +++ b/lib/astute/enviroment.rb @@ -0,0 +1,74 @@ +# 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 'rest-client' +require 'json' + +module Astute + class Enviroment + + def self.load_file(file) + env = YAML.load_file(file) + expand_data(env) + end + + def self.expand_data(env) + + + + env['nodes'].each do |node| + net_data = node['interfaces'].values.select { |value| value if value['use_for_provision'] } rescue [] + id = api_data.find{ |v| v['id'] if v['mac'] == net_data[0]['mac_address'] }['id'] + if net_data.size == 1 + node.merge!({ + 'ip' => net_data[0]['ip_address'], + 'power_address' => net_data[0]['ip_address'], + 'fqdn' => net_data[0]['dns_name'], + 'mac' => net_data[0]['mac_address'], + 'id' => id, + 'uid' => id + }) + p "== 123 ===" + p node['ip'] + p node['mac'] + p "=== end ===" + else + Astute.logger.error "Not find use_for_provision in #{node[:name]}" + end + + end + env + end + + def get_data_from_nailgun + # Get additional data from FuelWeb + begin + response = RestClient.get 'http://localhost:8000/api/nodes' + rescue => e + e.response + end + + if response. + api_data = JSON.parse(response) + end + + + def set_param(env, param, new) + env[param] + end + + + end +end \ No newline at end of file diff --git a/lib/astute/logparser/parser_patterns.rb b/lib/astute/logparser/parser_patterns.rb index 943d946a..85c5c444 100644 --- a/lib/astute/logparser/parser_patterns.rb +++ b/lib/astute/logparser/parser_patterns.rb @@ -46,7 +46,7 @@ module Astute {'pattern' => 'wait while node rebooting', 'supposed_time' => 20}, ].reverse, 'filename' => 'install/anaconda.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", }, 'anaconda-log-supposed-time-kvm' => # key for default kvm provision pattern @@ -70,7 +70,7 @@ module Astute {'pattern' => 'wait while node rebooting', 'supposed_time' => 20}, ].reverse, 'filename' => 'install/anaconda.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", }, 'puppet-log-components-list-ha-controller' => # key for default HA deploy pattern @@ -78,7 +78,7 @@ module Astute 'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}], 'chunk_size' => 40000, 'filename' => 'puppet-agent.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", 'components_list' => [ {'name' => 'Galera', 'weight' => 5, 'patterns' => [ {'pattern' => '/Stage[main]/Galera/File[/etc/mysql]/ensure) created', 'progress' => 0.1}, @@ -243,7 +243,7 @@ module Astute 'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}], 'chunk_size' => 40000, 'filename' => 'puppet-agent.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", 'components_list' => [ {'name' => 'Keystone', 'weight' => 10, 'patterns' => [ {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 1}, @@ -296,7 +296,7 @@ module Astute 'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}], 'chunk_size' => 40000, 'filename' => 'puppet-agent.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", 'components_list' => [ {'name' => 'Glance', 'weight' => 10, 'patterns' => [ {'pattern' => '/Stage[main]/Glance/Package[glance]/ensure) created', 'progress' => 0.1}, @@ -383,7 +383,7 @@ module Astute 'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}], 'chunk_size' => 40000, 'filename' => 'puppet-agent.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", 'components_list' => [ {'name' => 'Glance', 'weight' => 10, 'patterns' => [ {'pattern' => '/Stage[main]/Glance/Package[glance]/ensure) created', 'progress' => 0.1}, @@ -470,7 +470,7 @@ module Astute 'endlog_patterns' => [{'pattern' => /Finished catalog run in [0-9]+\.[0-9]* seconds\n/, 'progress' => 1.0}], 'chunk_size' => 40000, 'filename' => 'puppet-agent.log', - 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['fqdn'] %>/<%= @pattern_spec['filename'] %>", + 'path_format' => "<%= @pattern_spec['path_prefix'] %><%= node['ip'] %>/<%= @pattern_spec['filename'] %>", 'components_list' => [ {'name' => 'Keystone', 'weight' => 10, 'patterns' => [ {'pattern' => '/Stage[main]/Keystone::Python/Package[python-keystone]/ensure) created', 'progress' => 1}, diff --git a/lib/astute/logparser/provision.rb b/lib/astute/logparser/provision.rb index e6cce6e3..6163a003 100644 --- a/lib/astute/logparser/provision.rb +++ b/lib/astute/logparser/provision.rb @@ -54,7 +54,7 @@ module Astute pattern_spec['path_prefix'] ||= PATH_PREFIX.to_s pattern_spec['separator'] ||= SEPARATOR.to_s - hdd = node['meta']['disks'].select{|disk| not disk['removable']}[0] + hdd = node['meta']['disks'].select{|disk| not disk['removable']}[0] if node['meta'] && node['meta']['disks'] if hdd # Convert size from bytes to GB hdd_size = hdd['size'] / 10 ** 9